扩展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的实现类。