AOP
AOP(Aspect Oriented Programming)是一种编程思想, 面向切面编程,而Spring AOP只是对它的一种实现,常用的实现框架还有AspectJ。
面向切面编程就要求我们要对目标类方法进行切入,可以在目标类方法前后执行一些的逻辑,这不可避免的就要用到动态代理去实现,接下来我们从动态代理开始逐步认识Spring AOP是怎么实现的。
动态代理
动态代理的基础知识可以参这篇文章–动态代理基础知识,文章中描写了Cglib的动态代理和JDK的动态代理的应用。
Spring中的动态代理
-
PoxyFactory
上面两种动态代理技术在Spring中进行了封装,通过ProxyFactory,可以不再关心到底是用cglib还是jdk动态代理了,ProxyFactory会去判断,如果目标类实现了接口,那么ProxyFactory底层就会用jdk动态代理,如果没有实现接口,就会用cglib技术。 -
Advice
MethodBeforeAdvice:定义目标方法执行前的逻辑
AfterReturningAdvice: 定义目标方法执行后的逻辑
org.aopalliance.intercept.MethodInterceptor:定义目标方法执行前后的逻辑
** **:定义目标方法抛出异常的逻辑// MethodBeforeAdvice public class BeforeAdvice1 implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("BeforeAdvice1"); } }
// AfterReturningAdvice public class AfterAdvice1 implements AfterReturningAdvice { @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("AfterAdvice1"); } }
// MethodInterceptor public class AroundAdvice1 implements MethodInterceptor { @Nullable @Override public Object invoke(@NotNull MethodInvocation invocation) throws Throwable { System.out.println("方法执行Around前"); Object proceed = invocation.proceed(); System.out.println("方法执行Around后"); return proceed; } }
// ThrowsAdvice public class ThrowsAdvice1 implements ThrowsAdvice { // method参数:调用到的方法 // args参数:调用到方法传入的参数 // target参数:目标对象 // e参数:将来调用方法所抛出的异常 // 这个方法要么如上介绍的四个参数,要么只有一个Exception参数 public void afterThrowing(Method method, Object[] args, Object target, Exception e) { System.out.println("ThrowAdvice:" + e.getMessage()); } }
// 测试用例 public class ProxyFactoryTest { public static void main(String[] args) throws Exception { TargetService target = new TargetService(); ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setTarget(target); proxyFactory.addAdvice(new AfterAdvice1()); proxyFactory.addAdvice(new BeforeAdvice1()); proxyFactory.addAdvice(new AroundAdvice1()); //proxyFactory.addAdvice(new ThrowsAdvice1()); TargetService proxy = (TargetService) proxyFactory.getProxy(); proxy.test(); } }
-
Advisor
advisor是有一个Pointcut和一个Advice组成的,通过Pointcut可以指定要需要被代理的逻辑,可以通过Advisor,来控制到具体代理哪一个方法// PointcutAdvisor public class NingPointcutAdvisor implements PointcutAdvisor { @Override // 得到一个Advice public Advice getAdvice() { return new MethodInterceptor() { @Nullable @Override public Object invoke(@NotNull MethodInvocation invocation) throws Throwable { System.out.println("before method"); Object proceed = invocation.proceed(); System.out.println("after method"); return proceed; } }; } @Override // 只有匹配成功的Target Class才可以执行上述Advice方法逻辑,这个方法就是定义匹配逻辑的 public Pointcut getPointcut() { return new StaticMethodMatcherPointcut() { @Override public boolean matches(Method method, Class<?> targetClass) { return targetClass.equals(TargetService.class); } }; } // 暂时先不关注此方法 @Override public boolean isPerInstance() { return false; } }
-
ProxyFactoryBean
我们希望ProxyFactory所产生的代理对象能直接就是Bean,能直接从Spring容器中得到目标类的代理对象,我们可以通过ProxyFactoryBean把某一个目标类直接注册为Spring Bean, 也就只能针对一个Bean//ProxyFactoryBean, 可以在AppConfig中做如下配置 @Bean public ProxyFactoryBean userServiceProxy(){ TargetService targetService = new TargetService(); ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean(); proxyFactoryBean.setTarget(targetService); proxyFactoryBean.addAdvice(new MethodInterceptor() { @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("before..."); Object result = invocation.proceed(); System.out.println("after..."); return result; } }); return proxyFactoryBean; }
-
BeanNameAutoProxyCreator
ProxyFactoryBean得自己指定被代理的对象,那么我们可以通过BeanNameAutoProxyCreator来通过指定某个bean的名字,来对该bean进行代理//BeanNameAutoProxyCreator, 可以在AppConfig中做如下配置 @Bean public BeanNameAutoProxyCreator beanNameAutoProxyCreator() { BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator(); // 可以用*做模糊匹配,如这所示会匹配到所有以‘target’开头的Bean beanNameAutoProxyCreator.setBeanNames("target*"); // 根据beanName去Spring容器中找到这个Advice beanNameAutoProxyCreator.setInterceptorNames("aroundAdvice1"); beanNameAutoProxyCreator.setProxyTargetClass(true); return beanNameAutoProxyCreator; }
-
DefaultAdvisorAutoProxyCreator
通过DefaultAdvisorAutoProxyCreator会直接去找所有Advisor类型的Bean,根据Advisor中的PointCut和Advice信息,确定要代理的Bean以及代理逻辑。// DefaultAdvisorAutoProxyCreator,可以在AppConfig中做如下配置 @Bean public DefaultPointcutAdvisor defaultPointcutAdvisor(){ NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut(); pointcut.addMethodName("test"); DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor(); defaultPointcutAdvisor.setPointcut(pointcut); defaultPointcutAdvisor.setAdvice(new AfterAdvice1()); return defaultPointcutAdvisor; } @Bean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { return new DefaultAdvisorAutoProxyCreator(); }
总结,通过上述Spring中代理的一些认识,可以了解到可以利用代理来对Bean进行在类外部进行类的曾强,其实Spring AOP也运用了这些来实现的
Spring AOP 基础概念
- Aspect:表示切面,比如被@Aspect注解的类就是切面,可以在切面中去定义Pointcut、Advice等
- Join point:表示连接点,表示一个程序在执行过程中的一个点,比如一个方法的执行,比如一个异常的处理,在Spring AOP中,一个连接点通常表示一个方法的执行。
- Advice:表示通知,表示在一个特定连接点上所采取的动作,在很多AOP框架中,包括Spring,会用Interceptor拦截器来实现Advice,
- Pointcut:表示切点,用来匹配一个或多个连接点。
- Introduction:可以使用@DeclareParents来给所匹配的类添加一个接口,并指定一个默认实现
- Target object:目标对象,被代理对象
Spring AOP 关键点解析
@EnableAspectJAutoProxy ,Spring AOP应用的起点。
会在启动类上放入这个注解,它的作用是去注册一个AnnotationAwareAspectJAutoProxyCreator类型的Bean,这个Bean本质上是一个BeanPostProcess, 这样在Bean生命周期的过程中在初始化后去执行wrapIfNecessary(),生成成代理Bean。
AnnotationAwareAspectJAutoProxyCreator类图:
ProxyFactory 生成代理对象
在上述AnnotationAwareAspectJAutoProxyCreator ->postProcessAfterInitialization() -> wrapIfNecessary() 的方法中会给符合的Bean去生成代理对象,用的就是ProxyFactory。
wrapIfNecessary方法的简单流程图:
在文章最开始的时候就介绍了PoxyFactory可以自动选择是用Cglib还是JDK动态代理去生成代理对象,那么我们从源码来看一下它是怎么做选择的。
如下所示为PoxyFactory核心判断代码
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// NativeDetector.inNativeImage()是不是在GraalVM虚拟机上运行
// isOptimize 是个标记字段,如果ProxyFactory的isOptimize为true,Spring认为cglib比jdk动态代理要快,早期的JDK,现在不一定了
// hasNoUserSuppliedProxyInterfaces 判断被代理对象是否实现接口,
if (!NativeDetector.inNativeImage() &&
(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
//未完待续。。。。。。
在wrapIfNecessary()方法中会从Spring容器查找到所有的advisor,那么这些advisor是什么时候放入到容器中的呢????
Spring AOP中代理对象的执行???
- @Before:AspectJMethodBeforeAdvice,实际上就是一个MethodBeforeAdvice
- @AfterReturning:AspectJAfterReturningAdvice,实际上就是一个AfterReturningAdvice
- @AfterThrowing:AspectJAfterThrowingAdvice,实际上就是一个MethodInterceptor
- @After:AspectJAfterAdvice,实际上就是一个MethodInterceptor
- @Around:AspectJAroundAdvice,实际上就是一个MethodInterceptor
代理对象的执行过程
- 在使用ProxyFactory创建代理对象之前,需要往ProxyFactory先添加Advisor
- 代理对象在执行某个方法时,会把ProxyFactory中的Advisor拿出来和当前正在执行的方法进行匹配筛选
- 把和方法所匹配的Advisor适配成MethodInterceptor
- 把和当前方法匹配的MethodInterceptor链,以及被代理对象、代理对象、代理类、当前Method对象、方法参数封装为MethodInvocation对象
- 调用MethodInvocation的proceed()方法,开始执行各个MethodInterceptor以及被代理对象的对应方法
- 按顺序调用每个MethodInterceptor的invoke()方法,并且会把MethodInvocation对象传入invoke()方法
- 直到执行完最后一个MethodInterceptor了,就会调用invokeJoinpoint()方法,从而执行被代理对象的当前方法