9.0 Spring AOP底层解析
一 、 动态代理
代理模式的解释:为其他对象提供一种代理以控制对这个对象的访问,增强一个类中的某个方法,对程序进行扩展。
不修改UserService类的源码前提下,给test()增加额外逻辑,那么就可以使用动态 代理机制来创建UserService对象
动态代理技术具体实现方式有jdk动态代理(基于接口)和从cglib(基于父子类)两种。
// cglib方式
public class UserService {
public void test() {
System.out.println("test...");
}
}
UserService target = new UserService();
// 通过cglib技术
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
// 定义额外逻辑,也就是代理逻辑
enhancer.setCallbacks(new Callback[]{new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before...");
Object result = methodProxy.invoke(target, objects);
System.out.println("after...");
return result;
}
}});
// 动态代理所创建出来的UserService对象
UserService userService = (UserService) enhancer.create();
// 执行这个userService的test方法时,就会额外会执行一些其他逻辑
userService.test();
// JDK动态代理
public interface UserInterface {
public void test();
}
public class UserService implements UserInterface {
public void test() {
System.out.println("test...");
}
}
UserService target = new UserService();
// UserInterface接口的代理对象
Object proxy = Proxy.newProxyInstance(UserService.class.getClassLoader(), new Class[]{UserInterface.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before...");
Object result = method.invoke(target, args);
System.out.println("after...");
return result;
}
});
UserInterface userService = (UserInterface) proxy;
userService.test();
由于把new Class[]{UserInterface.class}
的限制,代理对象的类型是UserInterface,这是需要注意的
二、 ProxyFactory
Spring中基于动态代理技术进行了封装类ProxyFactory
表示创建代理的工厂
UserService target = new UserService();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.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;
}
});
UserInterface userService = (UserInterface) proxyFactory.getProxy();
userService.test();
通过ProxyFactory可以不再关心用的是jdk动态代理还是Cglib动态代理。
优先判断是否实现了接口,使用JDK动态代理
三、Advice的分类
- Before Advice:方法之前执行
- After returning advice:方法return后执行
- After throwing advice:方法抛异常后执行
- After (finally) advice:方法执行完finally之后执行,这是最后的,比return更后
- Around advice:这是功能最强大的Advice,可以自定义执行顺序
四、Advisor的理解
一个Advisor是有一个Pointcut和一个Advice组成的,通 过Pointcut可以指定要需要被代理的逻辑,比如一个UserService类中有两个方法,按上面的例子, 这两个方法都会被代理,被增强,那么我们现在可以通过Advisor,来控制到具体代理哪一个方法, 比如:
public static void main(String[] args) {
UserService target = new UserService();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAdvisor(new PointcutAdvisor() {
@Override
public Pointcut getPointcut() {
return new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return method.getName().equals("testAbc");
}
};
}
@Override
public Advice getAdvice() {
return new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("before...");
Object result = invocation.proceed();
System.out.println("after...");
return result;
}
};
}
@Override
public boolean isPerInstance() {
return false;
}
});
UserInterface userService = (UserInterface) proxyFactory.getProxy();
userService.test();
}
产生的代理对象,只有在执行testAbc这个方法时才会被增强,会执行额外的逻辑, 而在执行其他方法时是不会增强的
五、 创建代理对象的方式
作为开发者的我们得告诉Spring,那些类需要被代理,代理逻辑是什么。
1. ProxyFactoryBean
// 针对某一个Bean
@Bean
public ProxyFactoryBean userServiceProxy() {
UserService userService = new UserService();
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
proxyFactoryBean.setTarget(userService);
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;
}
// 把某个Advise或Advisor定义成为Bean,然后在ProxyFactoryBean中进行设置
@Bean
public MethodInterceptor customAroundAdvise() {
return new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("before...");
Object result = invocation.proceed();
System.out.println("after...");
return result;
}
};
}
@Bean
public ProxyFactoryBean userService() {
UserService userService = new UserService();
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
proxyFactoryBean.setTarget(userService);
proxyFactoryBean.setInterceptorNames("customAroundAdvise");
return proxyFactoryBean;
}
2. BeanNameAutoProxyCreator
@Bean
public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
beanNameAutoProxyCreator.setBeanNames("userSe*");
beanNameAutoProxyCreator.setInterceptorNames("zhouyuAroundAdvise");
beanNameAutoProxyCreator.setProxyTargetClass(true);
return beanNameAutoProxyCreator;
}
通过BeanNameAutoProxyCreator
可以对批量的Bean进行AOP,并且指定了代理逻辑,指定了一个 InterceptorName
,也就是一个Advise,前提条件是这个Advise也得是一个Bean,这样Spring才能 找到的,但是缺点很明显,它只能根据beanName来指定想要代理 的Bean。
3. DefaultAdvisorAutoProxyCreator
@Bean
public DefaultPointcutAdvisor defaultPointcutAdvisor(){
NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
pointcut.addMethodName("test");
DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor();
defaultPointcutAdvisor.setPointcut(pointcut);
defaultPointcutAdvisor.setAdvice(new ZhouyuAfterReturningAdvise());
return defaultPointcutAdvisor;
}
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new
DefaultAdvisorAutoProxyCreator();
return defaultAdvisorAutoProxyCreator;
}
通过DefaultAdvisorAutoProxyCreator
会直接去找所有Advisor类型的Bean,根据Advisor中的 PointCut和Advice信息,确定要代理的Bean以及代理逻辑。 但是,我们发现,通过这种方式,我们得依靠某一个类来实现定义我们的Advisor,或者Advise,或 者Pointcut,那么这个步骤能不能更加简化一点呢? 通过注解!
定义一个类,然后通过在类中的方法上通过某些注解,来定义PointCut以及 Advice,可以的,比如:
@Aspect
@Component
public class ZhouyuAspect {
@Before("execution(public void com.zhouyu.service.UserService.test())")
public void zhouyuBefore(JoinPoint joinPoint) {
System.out.println("zhouyuBefore");
}
}
定义好了所要代理的方法(通过一个表达式),以及代理逻辑(被 @Before修饰的方法),简单明了,这样对于Spring来说,它要做的就是来解析这些注解了,解析之后得到对应的Pointcut对象、Advice对象,生成Advisor对象,扔进ProxyFactory中,进而产生对应的代理对象,具体怎么解析这些注解就是@EnableAspectJAutoProxy注解所要做的事情了
六、 对Spring AOP的理解
OOP表示面向对象编程
AOP表示面向切面编程
注解的方式来定义Pointcut和Advice,Spring并不是首创,首创是 AspectJ
。Spring是直接依赖了AspectJ
,或者说, Spring是直接把AspectJ
中所定义的那些注解直接拿过来用,自己没有再重复定义,不过也仅仅只是把注解的定义赋值过来了,每个注解具体底层是怎么解析的,还是Spring自己做的,所以我们在用 Spring时,如果你想用@Before、@Around等注解,是需要单独引入aspecj
相关jar包的,比如:
compile group: 'org.aspectj', name: 'aspectjrt', version: '1.9.5'
compile group: 'org.aspectj', name: 'aspectjweaver', version: '1.9.5'
AspectJ
是在编译时对字节码进行了修改,是直接在UserService类对应的字节码中进 行增强的,也就是可以理解为是在编译时就会去解析@Before这些注解,然后得到代理逻辑,加入到 被代理的类中的字节码中去的,所以如果想用AspectJ技术来生成代理对象 ,是需要用单独的 AspectJ编译器的。我们在项目中很少这么用,我们仅仅只是用了@Before这些注解,而我们在启动 Spring的过程中,Spring会去解析这些注解,然后利用动态代理机制生成代理对象的。
IDEA中使用Aspectj
七、AOP中的概念
- Aspect:表示切面,比如被@Aspect注解的类就是切面,可以在切面中去定义Pointcut、 Advice等等
- Join point:表示连接点,表示一个程序在执行过程中的一个点,比如一个方法的执行,比如一 个异常的处理,在Spring AOP中,一个连接点通常表示一个方法的执行。
- Advice:表示通知,表示在一个特定连接点上所采取的动作。Advice分为不同的类型,后面详 细讨论,在很多AOP框架中,包括Spring,会用Interceptor拦截器来实现Advice,并且在连接点周围维护一个Interceptor链
- Pointcut:表示切点,用来匹配一个或多个连接点,Advice与切点表达式是关联在一起的, Advice将会执行在和切点表达式所匹配的连接点上
- Introduction:引入。就是对原始对象无中生有的添加成员变量或成员方法。可以使用@DeclareParents来给所匹配的类添加一个接口,并指定一个默认实现
- Target object:目标对象,被代理对象
- AOP proxy:表示代理工厂,用来创建代理对象的,在Spring中,要么是JDK动态 代理,要么是CGLIB代理
- Weaving:表示织入,表示创建代理对象的动作,这个动作可以发生在编译时期(比如 Aspejctj),或者运行时,比如Spring AOP
八、Advice在Spring AOP中对应API
对应advice的分类,Aspject中的注解:
- @Before
- @AfterReturning
- @AfterThrowing
- @After
- @Around
Srping提供的类似的执行实现类:
- 接口MethodBeforeAdvice,继承了接口BeforeAdvice
- 接口AfterReturningAdvice
- 接口ThrowsAdvice
- 接口AfterAdvice
- 接口MethodInterceptor
Spring会把五个注解解析为对应的Advice类:
- @Before:AspectJMethodBeforeAdvice,实际上就是一个MethodBeforeAdvice
- @AfterReturning:AspectJAfterReturningAdvice,实际上就是一个AfterReturningAdvice
- @AfterThrowing:AspectJAfterThrowingAdvice,实际上就是一个MethodInterceptor
- @After:AspectJAfterAdvice,实际上就是一个MethodInterceptor
- @Around:AspectJAroundAdvice,实际上就是一个MethodInterceptor
九、TargetSource的使用
在我们日常的AOP中,被代理对象就是Bean对象,是由BeanFactory给我们创建出来的,但是 Spring AOP中提供了TargetSource机制,可以让我们用来自定义逻辑来创建被代理对象。
比如之前所提到的@Lazy注解,当加在属性上时,会产生一个代理对象赋值给这个属性,产生代理对象的代码为:
protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) {
BeanFactory beanFactory = getBeanFactory();
Assert.state(beanFactory instanceof DefaultListableBeanFactory,
"BeanFactory needs to be a DefaultListableBeanFactory");
final DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory) beanFactory;
TargetSource ts = new TargetSource() {
@Override
public Class<?> getTargetClass() {
return descriptor.getDependencyType();
}
@Override
public boolean isStatic() {
return false;
}
@Override
public Object getTarget() {
Set<String> autowiredBeanNames = (beanName != null ? new LinkedHashSet<>(1) : null);
Object target = dlbf.doResolveDependency(descriptor, beanName, autowiredBeanNames, null);
if (target == null) {
Class<?> type = getTargetClass();
if (Map.class == type) {
return Collections.emptyMap();
} else if (List.class == type) {
return Collections.emptyList();
} else if (Set.class == type || Collection.class == type) {
return Collections.emptySet();
}
throw new NoSuchBeanDefinitionException(descriptor.getResolvableType(),
"Optional dependency not present for lazy injection point");
}
if (autowiredBeanNames != null) {
for (String autowiredBeanName : autowiredBeanNames) {
if (dlbf.containsBean(autowiredBeanName)) {
dlbf.registerDependentBean(autowiredBeanName, beanName);
}
}
}
return target;
}
@Override
public void releaseTarget(Object target) {
}
};
ProxyFactory pf = new ProxyFactory();
pf.setTargetSource(ts);
Class<?> dependencyType = descriptor.getDependencyType();
if (dependencyType.isInterface()) {
pf.addInterface(dependencyType);
}
return pf.getProxy(dlbf.getBeanClassLoader());
}
这段代码就利用了ProxyFactory来生成代理对象,以及使用了TargetSource,以达到代理对象在执 行某个方法时,调用TargetSource的getTarget()方法实时得到一个被代理对象。
十、Introduction
什么是Introduction以及如何使用@DeclareParents注解呢?通过下面例子
假设已经有一个UserService类提供了保存User对象的服务,但是现在想增加对User进行验证的功能,只对通过验证的User提供保存服务,在不修改UserService类代码的前提下就可以通过Introduction来解决。
// 首先定义一个Verifier接口,里面定义了进行验证的方法validate()
public interface Verifier {
public boolean validate(User user);
}
// 该接口的一个实现类BasicVerifier
public class BasicVerifier implements Verifier {
@Override
public boolean validate(User user) {
if(user.getUsername().equals("jack") && user.getPassword().equals("1234")) {
return true;
}
return false;
}
}
// 为UserService类增加验证User的功能,如下所示定义Aspect:
@Aspect
@Component
public class MyAspect {
@DeclareParents(value="com.tsinghuait.services.UserService",
defaultImpl=com.tsinghuait.aop.BasicVerifier.class)
public Verifier verifer;
}
// 将UserService对象转型为Verifier对象并对用户进行验证
public static void main(String[] args) {
User user1 = new User();
user1.setUsername("abc");
user1.setPassword("def");
ApplicationContext factory = new ClassPathXmlApplicationContext("config.xml");
Service s = (Service) factory.getBean("service");
Verifier v = (Verifier) s;
if(v.validate(user1) {
System.out.println("验证成功");
s.serve(user1);
}
}
十一、LoadTimeWeaver
首先看下需求:
- 对某些接口调用做记录
- 对test环境拦截,生产环境放行
在Java 中,从织入切面的方式上来看,存在三种织入方式:编译期织入、类加载期织入和运行期织入。
编译期织入是指在Java编译期,采用特殊的编译器,将切面织入到Java类中;
类加载期织入则指通过特殊的类加载器,在类字节码加载到JVM时,织入切面;
运行期织入则是采用CGLib或JDK动态代理进行切面的织入。
AspectJ采用编译期织入和类加载期织入的方式织入切面,是语言级的AOP实现,提供了完备的AOP支持。
AspectJ两种切面织入方式:
第一种通过特殊编译器,在编译期,将AspectJ语言编写的切面类织入到Java类中,可以通过一个Ant或Maven任务来完成这个操作;
第二种方式是类加载期织入,也简称为LTW(Load Time Weaving)。
使用Load Time Weaving
-
通过JVM的-javaagent参数设置LTW的织入器类包,以代理JVM默认的类加载器;
-Javaagent:D:\org\springframework\spring-agent\2.5.6.SEC02\spring-agent-2.5.6.SEC02.jar
-
LTW织入器需要一个 aop.xml文件,在该文件中指定切面类和需要进行切面织入的目标类。
在没有硬性的性能测试需求下,很快得到一些性能指标。(以调用方法的执行时间和CPU使用率为例)
// 织入受体:被拦截对象
public class DemoBean {
public void run() {
System.out.println("Run");
}
}
// 分析方法执行效率的切面
@Aspect
public class ProfilingAspect {
@Around("profileMethod()")
public Object profile(ProceedingJoinPoint pjp) throws Throwable {
StopWatch sw = new StopWatch(getClass().getSimpleName());
try {
sw.start(pjp.getSignature().getName());
return pjp.proceed();
} finally {
sw.stop();
System.out.println(sw.prettyPrint());
}
}
@Pointcut("execution(public * com.shansun..*(..))")
public void profileMethod() {}
}
// META-INF/aop.xml
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
<weaver>
<include within="com.shansun..*" />
</weaver>
<aspects>
<!-- weave in just this aspect -->
<aspect name="com.shansun.multidemo.spring.ltw.ProfilingAspect" />
</aspects>
</aspectj>
// LTW
<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<context:load-time-weaver aspectj-weaving="autodetect" />
<context:component-scan base-package="com.shansun.multidemo"></context:component-scan>
</beans>
通过 <context:load-time-weaver aspectj-weaving="on" />
使 spring 开启 loadtimeweaver
aspectj-weaving
有三个选项 : on, off, auto-detect, 如果设置为 auto-detect, spring 将会在 classpath 中查找 aspejct 需要的 META-INF/aop.xml, 如果找到则开启 aspectj weaving, 这个逻辑在LoadTimeWeaverBeanDefinitionParser#isAspectJWeavingEnabled()
方法中:
protected boolean isAspectJWeavingEnabled(String value, ParserContext parserContext) {
if ("on".equals(value)) {
return true;
}
else if ("off".equals(value)) {
return false;
}
else {
// Determine default...
ClassLoader cl = parserContext.getReaderContext().getResourceLoader().getClassLoader();
return (cl.getResource(ASPECTJ_AOP_XML_RESOURCE) != null);
}
}
// 准备就绪切面类、aop.xml、Spring的配置,开始测试
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// DemoBean bean = (DemoBean) ctx.getBean("demoBean");
DemoBean bean = new DemoBean();
bean.run();
}
}
// 输出结果
Run
StopWatch 'ProfilingAspect': running time (millis) = 0
-----------------------------------------
ms % Task name
-----------------------------------------
0001 100% run
解决javaagent参数问题
不加javaagent参数时抛出IllegalStateException
异常。由下面这个类
public void addTransformer(ClassFileTransformer transformer) {
Assert.notNull(transformer, "Transformer must not be null");
FilteringClassFileTransformer actualTransformer =
new FilteringClassFileTransformer(transformer, this.classLoader);
synchronized (this.transformers) {
if (this.instrumentation == null) {
throw new IllegalStateException(
"Must start with Java agent to use InstrumentationLoadTimeWeaver. See Spring documentation.");
}
this.instrumentation.addTransformer(actualTransformer);
this.transformers.add(actualTransformer);
}
}
// 重写这个方法隐匿异常
public class ExtInstrumentationLoadTimeWeaver extends
InstrumentationLoadTimeWeaver {
@Override
public void addTransformer(ClassFileTransformer transformer) {
try {
super.addTransformer(transformer);
} catch (Exception e) {}
}
}
// 再将Spring配置文件中的load-time-weaver入口设置为我们刚自定义ExtInstrumentationLoadTimeWeaver即可。
<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<context:load-time-weaver weaver-class="com.shansun.multidemo.spring.ExtInstrumentationLoadTimeWeaver" aspectj-weaving="autodetect" />
<context:component-scan base-package="com.shansun.multidemo"></context:component-scan>
</beans>
// 再次运行我们的main方法,发现只输出了如下结果,切面没有起作用。
Run
至此,同一份代码、同一份配置,只需要在VM启动参数中稍加变化,即可实现同一个应用包在不同环境下可以自由选择使用使用AOP功能。
十二、ProxyFactory选择动态代理原理
public static void main(String[] args) {
// config就是ProxyFactory对象
// optimize为true,或proxyTargetClass为true,或用户没有给ProxyFactory对象添加interface
if (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.");
}
// targetClass是接口,直接使用Jdk动态代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 使用Cglib
return new ObjenesisCglibAopProxy(config);
} else {
// 使用Jdk动态代理
return new JdkDynamicAopProxy(config);
}
}
十三、代理对象创建过程
JdkDynamicAopProxy
- 在构造
JdkDynamicAopProxy
对象时,会先拿到被代理对象自己所实现的接口,并且额外的增 加SpringProxy
、Advised
、DecoratingProxy
三个接口,组合成一个Class[],并赋值给 proxiedInterfaces属性 - 检查这些接口中是否定义了equals()、hashcode()方法
- 执行
Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this)
,得到代理对象,JdkDynamicAopProxy
作为InvocationHandler
,代理对象在执行某个方法时,会进入到JdkDynamicAopProxy
的invoke()方法中
ObjenesisCglibAopProxy
- 创建Enhancer对象
- 设置Enhancer的superClass为通过
ProxyFactory.setTarget()
所设置的对象的类 - 设置Enhancer的interfaces为通过
ProxyFactory.addInterface()
所添加的接口,以及SpringProxy
、Advised
、DecoratingProxy
接口 - 设置Enhancer的Callbacks为
DynamicAdvisedInterceptor
- 最后创建一个代理对象,代理对象在执行某个方法时,会进入到
DynamicAdvisedInterceptor#intercept()
方法中
十四、代理对象执行过程
- 在使用ProxyFactory创建代理对象之前,需要往ProxyFactory先添加Advisor
- 代理对象在执行某个方法时,会把ProxyFactory中的Advisor拿出来和当前正在执行的方法进行 匹配筛选
- 把和方法所匹配的Advisor适配成MethodInterceptor
- 把和当前方法匹配的MethodInterceptor链,以及被代理对象、代理对象、代理类、当前 Method对象、方法参数封装为MethodInvocation对象
- 调用MethodInvocation的proceed()方法,开始执行各个MethodInterceptor以及被代理对象 的对应方法
- 按顺序调用每个MethodInterceptor的invoke()方法,并且会把MethodInvocation对象传入 invoke()方法
- 直到执行完最后一个MethodInterceptor了,就会调用invokeJoinpoint()方法,从而执行被代理对象的当前方法
各注解对应的MethodInterceptor
-
@Before
对应的是AspectJMethodBeforeAdvice,在进行动态代理时会把AspectJMethodBeforeAdvice
转成MethodBeforeAdviceInterceptor
-
先执行advice对应的方法
-
再执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个 Interceptor了,会执行target对应的方法
-
-
@After
对应的是AspectJAfterAdvice,直接实现了MethodInterceptor
- 先执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个 Interceptor了,会执行target对应的方法
- 再执行advice对应的方法
-
@Around
对应的是AspectJAroundAdvice,直接实现了MethodInterceptor
- 直接执行advice对应的方法,由@Around自己决定要不要继续往后面调用
-
@AfterThrowing
对应的是AspectJAfterThrowingAdvice,直接实现了MethodInterceptor
- 先执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个 Interceptor了,会执行target对应的方法
- 如果上面抛了Throwable,那么则会执行advice对应的方法
-
@AfterReturning
对应的是AspectJAfterReturningAdvice,在进行动态代理时会把 AspectJAfterReturningAdvice转成AfterReturningAdviceInterceptor
- 先执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个 Interceptor了,会执行target对应的方法
- 执行上面的方法后得到最终的方法的返回值 再执行Advice对应的方法
十五、AbstractAdvisorAutoProxyCreator
DefaultAdvisorAutoProxyCreator
的父类是AbstractAdvisorAutoProxyCreator
。 AbstractAdvisorAutoProxyCreator
非常强大以及重要,只要Spring容器中存在这个类型的 Bean,就相当于开启了AOP,AbstractAdvisorAutoProxyCreator
实际上就是一个 BeanPostProcessor,所以在创建某个Bean时,就会进入到它对应的生命周期方法中,比如:在某 个Bean初始化之后,会调用wrapIfNecessary()
方法进行AOP,底层逻辑是, AbstractAdvisorAutoProxyCreator
会找到所有的Advisor,然后判断当前这个Bean是否存在某个 Advisor与之匹配(根据Pointcut),如果匹配就表示当前这个Bean有对应的切面逻辑,需要进行 AOP,需要产生一个代理对象。
十六、@EnableAspectJAutoProxy
这个注解主要就是往Spring容器中添加了一个AnnotationAwareAspectJAutoProxyCreator
类型的 Bean。
AspectJAwareAdvisorAutoProxyCreator
继承了AbstractAdvisorAutoProxyCreator
,重写了 findCandidateAdvisors()方法,AbstractAdvisorAutoProxyCreator
只能找到所有Advisor类型的 Bean对象,但是AspectJAwareAdvisorAutoProxyCreator
除开可以找到所有Advisor类型的 Bean对象,还能把@Aspect注解所标注的Bean中的@Before等注解及方法进行解析,并生成对应的 Advisor对象。 所以,我们可以理解@EnableAspectJAutoProxy
,其实就是像Spring容器中添加了一个 AbstractAdvisorAutoProxyCreator
类型的Bean,从而开启了AOP,并且还会解析@Before等注解 生成Advisor。
十七、Spring中AOP原理流程图
待续