一 、 动态代理

代理模式的解释:为其他对象提供一种代理以控制对这个对象的访问,增强一个类中的某个方法,对程序进行扩展。

不修改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的分类

  1. Before Advice:方法之前执行
  2. After returning advice:方法return后执行
  3. After throwing advice:方法抛异常后执行
  4. After (finally) advice:方法执行完finally之后执行,这是最后的,比return更后
  5. 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中的概念

  1. Aspect:表示切面,比如被@Aspect注解的类就是切面,可以在切面中去定义Pointcut、 Advice等等
  2. Join point:表示连接点,表示一个程序在执行过程中的一个点,比如一个方法的执行,比如一 个异常的处理,在Spring AOP中,一个连接点通常表示一个方法的执行。
  3. Advice:表示通知,表示在一个特定连接点上所采取的动作。Advice分为不同的类型,后面详 细讨论,在很多AOP框架中,包括Spring,会用Interceptor拦截器来实现Advice,并且在连接点周围维护一个Interceptor链
  4. Pointcut:表示切点,用来匹配一个或多个连接点,Advice与切点表达式是关联在一起的, Advice将会执行在和切点表达式所匹配的连接点上
  5. Introduction引入。就是对原始对象无中生有的添加成员变量或成员方法。可以使用@DeclareParents来给所匹配的类添加一个接口,并指定一个默认实现
  6. Target object目标对象,被代理对象
  7. AOP proxy:表示代理工厂,用来创建代理对象的,在Spring中,要么是JDK动态 代理,要么是CGLIB代理
  8. Weaving:表示织入,表示创建代理对象的动作,这个动作可以发生在编译时期(比如 Aspejctj),或者运行时,比如Spring AOP

八、Advice在Spring AOP中对应API

对应advice的分类,Aspject中的注解:

  1. @Before
  2. @AfterReturning
  3. @AfterThrowing
  4. @After
  5. @Around

Srping提供的类似的执行实现类:

  1. 接口MethodBeforeAdvice,继承了接口BeforeAdvice
  2. 接口AfterReturningAdvice
  3. 接口ThrowsAdvice
  4. 接口AfterAdvice
  5. 接口MethodInterceptor

Spring会把五个注解解析为对应的Advice类:

  1. @Before:AspectJMethodBeforeAdvice,实际上就是一个MethodBeforeAdvice
  2. @AfterReturning:AspectJAfterReturningAdvice,实际上就是一个AfterReturningAdvice
  3. @AfterThrowing:AspectJAfterThrowingAdvice,实际上就是一个MethodInterceptor
  4. @After:AspectJAfterAdvice,实际上就是一个MethodInterceptor
  5. @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

首先看下需求:

  1. 对某些接口调用做记录
  2. 对test环境拦截,生产环境放行

在Java 中,从织入切面的方式上来看,存在三种织入方式:编译期织入、类加载期织入和运行期织入

编译期织入是指在Java编译期,采用特殊的编译器,将切面织入到Java类中;

类加载期织入则指通过特殊的类加载器,在类字节码加载到JVM时,织入切面;

运行期织入则是采用CGLib或JDK动态代理进行切面的织入。

AspectJ采用编译期织入和类加载期织入的方式织入切面,是语言级的AOP实现,提供了完备的AOP支持。

AspectJ两种切面织入方式:

第一种通过特殊编译器,在编译期,将AspectJ语言编写的切面类织入到Java类中,可以通过一个Ant或Maven任务来完成这个操作;

第二种方式是类加载期织入,也简称为LTW(Load Time Weaving)。

使用Load Time Weaving

  1. 通过JVM的-javaagent参数设置LTW的织入器类包,以代理JVM默认的类加载器;
-Javaagent:D:\org\springframework\spring-agent\2.5.6.SEC02\spring-agent-2.5.6.SEC02.jar
  1. 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

  1. 在构造JdkDynamicAopProxy对象时,会先拿到被代理对象自己所实现的接口,并且额外的增 加SpringProxyAdvisedDecoratingProxy三个接口,组合成一个Class[],并赋值给 proxiedInterfaces属性
  2. 检查这些接口中是否定义了equals()、hashcode()方法
  3. 执行 Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this) ,得到代理对象, JdkDynamicAopProxy作为InvocationHandler,代理对象在执行某个方法时,会进入到 JdkDynamicAopProxyinvoke()方法中

ObjenesisCglibAopProxy

  1. 创建Enhancer对象
  2. 设置Enhancer的superClass为通过ProxyFactory.setTarget()所设置的对象的类
  3. 设置Enhancer的interfaces为通过ProxyFactory.addInterface()所添加的接口,以及 SpringProxyAdvisedDecoratingProxy接口
  4. 设置Enhancer的Callbacks为DynamicAdvisedInterceptor
  5. 最后创建一个代理对象,代理对象在执行某个方法时,会进入到DynamicAdvisedInterceptor#intercept()方法中

十四、代理对象执行过程

  1. 在使用ProxyFactory创建代理对象之前,需要往ProxyFactory先添加Advisor
  2. 代理对象在执行某个方法时,会把ProxyFactory中的Advisor拿出来和当前正在执行的方法进行 匹配筛选
  3. 把和方法所匹配的Advisor适配成MethodInterceptor
  4. 把和当前方法匹配的MethodInterceptor链,以及被代理对象、代理对象、代理类、当前 Method对象、方法参数封装为MethodInvocation对象
  5. 调用MethodInvocation的proceed()方法,开始执行各个MethodInterceptor以及被代理对象 的对应方法
  6. 按顺序调用每个MethodInterceptor的invoke()方法,并且会把MethodInvocation对象传入 invoke()方法
  7. 直到执行完最后一个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的父类是AbstractAdvisorAutoProxyCreatorAbstractAdvisorAutoProxyCreator非常强大以及重要,只要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原理流程图

待续