扩展Spring切面

概述

Spring的切面(Spring动态代理)在Spring中应用十分广泛,例如还有事务管理,重试等等。网上介绍SpringAop源码很多,这里假设你对SpringAop有基本的了解。如果你认为Spring代理类会创建多重代理,那说明你真的没了解。

需求背景

假设我现在想提供一个jar包,这个jar包会拦截制定注解方法,并做一些记录。这里要分析一下具体需求

拦截的注解是在方法上

如果注解是放在方法上,那么我们完全可以使用SpringAop的方式,通过自己定义一个注解,并对该注解进行拦截即可。这个不是本篇文章的重点,拦截制定注解的很简单。

拦截的注解是在类上

如果我们需要拦截的注解是类上,这里首先介绍第一个接口

Advisor

public interface Advisor {
    Advice getAdvice();
    boolean isPerInstance();
}

 

这个接口简单来说就是持有一个通知(Advice),但是一般不直接使用这个接口,因为这个对通知没有一个有效的过滤(当然如果你想对所有方法都进行拦截),所以一般是使用PointcutAdvisor,

public interface PointcutAdvisor extends Advisor {
    Pointcut getPointcut();
}

 

这个接口就可以通过Pointcut做一些限制。为什么呢? 直接看AopUtils#findAdvisorsThatCanApply方法,这个方法就是从所有Advisor接口实现Bean选择对某个Class有效。

public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
    if (candidateAdvisors.isEmpty()) {
        return candidateAdvisors;
    }
    List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
    for (Advisor candidate : candidateAdvisors) {
        if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
            eligibleAdvisors.add(candidate);
        }
    }
    boolean hasIntroductions = !eligibleAdvisors.isEmpty();
    for (Advisor candidate : candidateAdvisors) {
        if (candidate instanceof IntroductionAdvisor) {
            // already processed
            continue;
        }
        //!!!有兴趣的可以重点看一下重点看这里实现,就不大段贴代码了
        if (canApply(candidate, clazz, hasIntroductions)) {
            eligibleAdvisors.add(candidate);
        }
    }
    return eligibleAdvisors;
    }

 

好了,我们知道了,要想让我们的自定义的切面生效,首先,

第一步,创建一个Advisor
public class MyAdvisor implements PointcutAdvisor {

    //这个是Spring提供的一个简单的通过注解来获取切点类
    AnnotationMatchingPointcut annotationMatchingPointcut = new AnnotationMatchingPointcut(MyAop.class);
    //这个是我们自己实现的继承于Advice,
    MyAdvice myAdvice =new MyAdvice();

    @Override
    public Pointcut getPointcut() {
        return annotationMatchingPointcut;
    }

    @Override
    public Advice getAdvice() {
        return myAdvice;
    }

    @Override
    public boolean isPerInstance() {
        return false;
    }
    
    //简单起见,就直接使用MethodInterceptor
    class MyAdvice implements MethodInterceptor{

        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("haha");
            return invocation.proceed();
        }
    }

 

第二步,定义一个开关注解,让我们的自定义切面生效

创建一个开关注解,就是一个套路,先创建一个开关注解,@import我们一个Bean注册类

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(MyImporter.class)
public @interface EnableMyAop {
}

 

在Bean注册类中把我们的MyAdvisor注册进去吧

public class MyImporter implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        register(registry,MyAdvisor.class);
    }

    private void register(BeanDefinitionRegistry registry, Class<?> aopBeanFactoryPostClass) {
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
        rootBeanDefinition.setBeanClass(aopBeanFactoryPostClass);
        registry.registerBeanDefinition(aopBeanFactoryPostClass.getSimpleName(),rootBeanDefinition);
    }
}

 至此,只要使用MyAop注解的类,在调用他的任何方法的时候(类内部调用不算),都会进入到我们的切面中。

小结

本篇文章是简单看了一下Spring Aop源码后,尝试自己进行扩展,因此可能不是最优的方法。不过希望能起到抛砖迎玉作用,在其基础上可以做很多有很意思的事情。如果有兴趣可以进一步研究spring自己的Advice,Pointcut的实现类。

posted @ 2017-10-31 01:23  atheva  阅读(1506)  评论(0编辑  收藏  举报