spring源码分析(二)Aop

时间:2021-11-06 00:47:18

创建日期:2016.08.19

修改日期:2016.08.20-2016.08.21

交流QQ:992591601

参考资料:《spring源码深度解析》、《spring技术内幕》、传值播客spring教学视频

http://www.cnblogs.com/xing901022/p/4264334.html

http://www.cnblogs.com/digdeep/p/4528353.html

一,动态代理、java InvocationHandler实现、Cglib实现

要了解动态代理,可阅读设计模式相关书籍,不难理解。这篇博文我简单解释,动态代理就是与静态代理相对应的,在程序运行过程中动态创建的代理类。代理类可以简单理解为一种对目标类的增强,之后我们要使用目标类的话,只需用代理类就可以了。

实现动态代理有两种方式,其一是java自带的动态代理功能,另外就是使用Cglib库实现。前者的使用有一个必须的条件,那就是目标类必须实现接口。而后者的使用则是对前者的一种补充。

假设我们有这样两个bean,其一为AlienFishServiceBean,不实现任何接口。

其一为FishServiceImpl,实现FishService接口。

对于前者,我们需要使用Cglib来实现动态代理功能(示例代码,实现功能:当bean的fishName字段不为空时,才能调用该bean的方法):

  1. package cn;
  2. import java.lang.reflect.Method;
  3. import cn.aop.service.AlienFishServiceBean;
  4. import net.sf.cglib.proxy.Enhancer;
  5. import net.sf.cglib.proxy.MethodInterceptor;
  6. import net.sf.cglib.proxy.MethodProxy;
  7. /**
  8. * @ClassName: CGlibProxyFactory
  9. * @Description: CGlibProxyFactory
  10. * @author 无名
  11. * @date 2016-8-14 17:31:48
  12. * @version 1.0
  13. */
  14. public class CGlibProxyFactory implements MethodInterceptor {
  15. private Object targetObject;
  16. public Object createProxyIntance(Object targetObject) {
  17. this.targetObject = targetObject;
  18. Enhancer enhancer = new Enhancer();
  19. enhancer.setSuperclass(this.targetObject.getClass());
  20. enhancer.setCallback(this);
  21. return enhancer.create();
  22. }
  23. public Object intercept(Object proxy, Method method, Object[] args,
  24. MethodProxy  methodProxy) throws Throwable {
  25. AlienFishServiceBean bean = (AlienFishServiceBean) this.targetObject;
  26. Object result = null;
  27. if(bean.getFishName()!=null) {
  28. result = methodProxy.invoke(targetObject, args);
  29. }
  30. return result;
  31. }
  32. }

对于后者,我们可以使用java自带的动态代理功能(示例代码,实现功能:当bean的fishName字段不为空时,才能调用该bean的方法):

  1. package cn;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. import java.lang.reflect.Proxy;
  5. import cn.aop.service.FishService;
  6. import cn.aop.service.FishServiceImpl;
  7. /**
  8. * @ClassName: JDKProxyFactory
  9. * @Description: JDKProxyFactory
  10. * @author 无名
  11. * @date 2016-8-13 下午11:55:31
  12. * @version 1.0
  13. */
  14. public class JDKProxyFactory implements InvocationHandler {
  15. private Object targetObject;
  16. public Object createInstance(Object targetObject) {
  17. this.targetObject = targetObject;
  18. return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),
  19. this.targetObject.getClass().getInterfaces(), this);
  20. }
  21. @Override
  22. public Object invoke(Object proxy, Method method, Object[] args)
  23. throws Throwable {
  24. FishService fs = (FishServiceImpl)targetObject;
  25. Object result = null;
  26. if(fs.getFishName() != null) {
  27. result = method.invoke(targetObject, args);
  28. }
  29. return result;
  30. }
  31. }

上述两个代理方式使用时,先用代理类工厂创建对应代理类,然后使用代理类即可(此代理类是对目标类的代理,‘增强了’目标类的一些方面)。

二,Spring Aop

spring aop需要的jar包:

  org.springframework.aop-xxx.jar(spring),aopalliance-1.0.jar,aspectjrt.jar, aspectjweaver.jar,cglib.jar

spring aop是在动态代理这种设计模式的基础之上的。

这里我说下aop的几个基本概念,都是基于我自己的理解,简单粗暴:

aspect:面,可以理解为一个事务,对该事务做对应处理。

pointcut:切入点,对应于面具体的切入的地方。

advice:spring定义了四个advice, BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice。在触及面和点的时候,根据配置,执行对应通知。

spring aop的实现由两种,其一是配置文件方式,另外是注解方式。

我们首先,用配置文件方式:

目标类,接口和实现类:

  1. package cn.service;
  2. public interface FishService {
  3. public void say00();
  4. public void say01();
  5. }
  1. package cn.service.impl;
  2. import cn.service.FishService;
  3. public class FishServiceBean implements FishService {
  4. public void say00() {
  5. System.out.println("I am fish 00");
  6. }
  7. public void say01() {
  8. System.out.println("I am fish 01");
  9. }
  10. }

下面的类提供对应的advice

  1. package cn.service;
  2. import org.aspectj.lang.ProceedingJoinPoint;
  3. /**
  4. * 切面
  5. *
  6. */
  7. public class MyInterceptor {
  8. public void doBefore() {
  9. System.out.println("before");
  10. }
  11. public void doAfter() {
  12. System.out.println("after");
  13. }
  14. public void doFinal() {
  15. System.out.println("final");
  16. }
  17. public void doThrowing() {
  18. System.out.println("throwing");
  19. }
  20. public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
  21. System.out.println("进入方法");
  22. Object result = pjp.proceed();
  23. System.out.println("退出方法");
  24. return result;
  25. }
  26. }

配置文件(aop:config内设置aop,切面里设置切入点,及几种advice):

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:aop="http://www.springframework.org/schema/aop"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans
  7. http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
  8. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
  9. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
  10. <aop:aspectj-autoproxy/>
  11. <bean id="fishService" class="cn.service.impl.FishServiceBean"></bean>
  12. <bean id="aspetbean" class="cn.service.MyInterceptor"/>
  13. <aop:config>
  14. <aop:aspect id="asp" ref="aspetbean">
  15. <aop:pointcut id="mycut" expression="execution(* cn.service..*.*(..))"/>
  16. <aop:before pointcut-ref="mycut" method="doBefore"/>
  17. <aop:after-returning pointcut-ref="mycut" method="doFinal"/>
  18. <aop:after-throwing pointcut-ref="mycut" method="doThrowing"/>
  19. <aop:after pointcut-ref="mycut" method="doAfter"/>
  20. <aop:around pointcut-ref="mycut" method="doBasicProfiling"/>
  21. </aop:aspect>
  22. </aop:config>
  23. </beans>

测试类:

  1. package junit.test;
  2. import org.junit.BeforeClass;
  3. import org.junit.Test;
  4. import org.springframework.context.ApplicationContext;
  5. import org.springframework.context.support.ClassPathXmlApplicationContext;
  6. import cn.service.FishService;
  7. public class SpringAOPTest {
  8. @BeforeClass
  9. public static void setUpBeforeClass() throws Exception {
  10. }
  11. @Test public void interceptorTest(){
  12. ApplicationContext cxt = new ClassPathXmlApplicationContext("beans.xml");
  13. FishService fishService = (FishService)cxt.getBean("fishService");
  14. fishService.say00();
  15. fishService.say01();
  16. }
  17. }

运行结果:

spring源码分析(二)Aop

与配置文件方式相对应的便是注解的方式

注解方式只需在spring的xml文件里保留  <aop:aspectj-autoproxy/> 就可以了。

而上面对应的MyInterceptor类需要多写相对的注解(著名切入点、advice等)。

三,Spring源码分析

首先自己设想一下spring实现aop的思路,大概是:1,获取、寻找所有bean,如果为AspectJ注解的类,则进行对应处理。

2,对标记为AspectJ注解的类进行增强器的提取(前文提到动态代理是对目标类的一种增强)

3,创建动态代理。

首先看一个类AopNamespaceHandler,这里是aop注解对应的解析器:

  1. public class AopNamespaceHandler extends NamespaceHandlerSupport {
  2. public void init() {
  3. // In 2.0 XSD as well as in 2.1 XSD.
  4. registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
  5. registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
  6. registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
  7. // Only in 2.0 XSD: moved to context namespace as of 2.1
  8. registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
  9. }
  10. }

我们看registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());这一句,可知其内涵是向NamespaceHandlerSupport这一类的

private final Map<String, BeanDefinitionParser> parsers =
new HashMap<String, BeanDefinitionParser>();    这一map数据结构中注册对应解析器。

spring源码分析(二)Aop

此后 一旦遇到aspectj-autoproxy注解,便自然会从map中取到对应解析器,并使用解析器AspectJAutoProxyBeanDefinitionParser解析。AspectJAutoProxyBeanDefinitionParser是继承了BeanDefinitionParser接口的。

  1. public BeanDefinition parse(Element element, ParserContext parserContext) {
  2. AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
  3. extendBeanDefinition(element, parserContext);
  4. return null;
  5. }
  1. public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
  2. ParserContext parserContext, Element sourceElement) {
  3. BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
  4. parserContext.getRegistry(), parserContext.extractSource(sourceElement));
  5. useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
  6. registerComponentIfNecessary(beanDefinition, parserContext);
  7. }

首先看下进入该函数时参数的状态:

spring源码分析(二)Aop

sourceElement的name为aop:aspectj-autoproxy。这个函数就是专门用于注册aop:aspectj注解的。

这个方法三句代码,各有其作用,分别是:1注册AnnotationAwareAspectJAutoProxyCreator(AOP的实现都靠这个),返回的BeanDefinition设置对应的AnnotationAwareAspectJAutoProxyCreator

2处理proxy-target-class(CgLib或jdk)以及expose-proxy属性

3注册组件并通知

从spring的 DefaultAopProxyFactory类入手,该类继承自AopProxyFactory接口。

  1. package org.springframework.aop.framework;
  2. /**
  3. * Interface to be implemented by factories that are able to create
  4. * AOP proxies based on {@link AdvisedSupport} configuration objects.
  5. *
  6. * <p>Proxies should observe the following contract:
  7. * <ul>
  8. * <li>They should implement all interfaces that the configuration
  9. * indicates should be proxied.
  10. * <li>They should implement the {@link Advised} interface.
  11. * <li>They should implement the equals method to compare proxied
  12. * interfaces, advice, and target.
  13. * <li>They should be serializable if all advisors and target
  14. * are serializable.
  15. * <li>They should be thread-safe if advisors and target
  16. * are thread-safe.
  17. * </ul>
  18. *
  19. * <p>Proxies may or may not allow advice changes to be made.
  20. * If they do not permit advice changes (for example, because
  21. * the configuration was frozen) a proxy should throw an
  22. * {@link AopConfigException} on an attempted advice change.
  23. *
  24. * @author Rod Johnson
  25. * @author Juergen Hoeller
  26. */
  27. public interface AopProxyFactory {
  28. /**
  29. * Create an {@link AopProxy} for the given AOP configuration.
  30. * @param config the AOP configuration in the form of an
  31. * AdvisedSupport object
  32. * @return the corresponding AOP proxy
  33. * @throws AopConfigException if the configuration is invalid
  34. */
  35. AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException;
  36. }

我们看最核心的createAopProxy方法:

  1. public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
  2. if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
  3. Class targetClass = config.getTargetClass();
  4. if (targetClass == null) {
  5. throw new AopConfigException("TargetSource cannot determine target class: " +
  6. "Either an interface or a target is required for proxy creation.");
  7. }
  8. if (targetClass.isInterface()) {
  9. return new JdkDynamicAopProxy(config);
  10. }
  11. if (!cglibAvailable) {
  12. throw new AopConfigException(
  13. "Cannot proxy target class because CGLIB2 is not available. " +
  14. "Add CGLIB to the class path or specify proxy interfaces.");
  15. }
  16. return CglibProxyFactory.createCglibProxy(config);
  17. }
  18. else {
  19. return new JdkDynamicAopProxy(config);
  20. }
  21. }

这段代码逻辑很清晰,判断如果目标类实现接口则使用jdk创建代理,否则使用cglib。

再仔细看这段代码,发现无论是jdk还是cglib,都要使用AdvisedSupport config这个参数。

设置断点,查看该变量,发现果然信息量很大,对应的AdvisedSupport类代码我就不贴了,看下面截图也可以想象个大概了。

spring源码分析(二)Aop

具体看下advice的内容,我们配置文件里配置的内容都在里面了:

spring源码分析(二)Aop

至于这个config是怎样生成的,其实很好想象,就是前面讲的,对应注解的解析,注册。

new JdkDynamicAopProxy(config)

  1. /**
  2. * Construct a new JdkDynamicAopProxy for the given AOP configuration.
  3. * @param config the AOP configuration as AdvisedSupport object
  4. * @throws AopConfigException if the config is invalid. We try to throw an informative
  5. * exception in this case, rather than let a mysterious failure happen later.
  6. */
  7. public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
  8. Assert.notNull(config, "AdvisedSupport must not be null");
  9. if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
  10. throw new AopConfigException("No advisors and no TargetSource specified");
  11. }
  12. this.advised = config;
  13. }

CglibProxyFactory.createCglibProxy(config)

  1. /**
  2. * Create a new Cglib2AopProxy for the given AOP configuration.
  3. * @param config the AOP configuration as AdvisedSupport object
  4. * @throws AopConfigException if the config is invalid. We try to throw an informative
  5. * exception in this case, rather than let a mysterious failure happen later.
  6. */
  7. public Cglib2AopProxy(AdvisedSupport config) throws AopConfigException {
  8. Assert.notNull(config, "AdvisedSupport must not be null");
  9. if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
  10. throw new AopConfigException("No advisors and no TargetSource specified");
  11. }
  12. this.advised = config;
  13. this.advisedDispatcher = new AdvisedDispatcher(this.advised);
  14. }

都是用config设置对应的advise属性。

小小总结:最后我又单步调试一通,结合上一章节内容《spring源码分析(一)IoC、DI》,发现AOP代理类的创建时CreateBean这一阶段开始的,其实这时候就是对应目标类用代理类取代了。

这是断点运行时,观察createBean返回的结果,可见创建的fishService bean实质上是JdkDynamicAopProxy代理类。在那之后我们使用fishService实际上就是使用对应的JdkDynamicAopProxy代理类了,之前注册的那些advice也就生效了:

(在这一过程中值得一提的还有AbstractAutoProxyCreator的postProcessAfterInitialization方法,正如注释所说,作用是Create a proxy with the configured interceptors if   the bean is  identified as one to proxy by the subclass.

AnnotationAwareAspectJAutoProxyCreator的findCandidateAdvisors方法,作用是获取增强器)

spring源码分析(二)Aop