创建日期: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的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数据结构中注册对应解析器。

此后 一旦遇到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. }

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

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类代码我就不贴了,看下面截图也可以想象个大概了。

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

至于这个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的更多相关文章

  1. Spring源码分析之AOP从解析到调用

    正文: 在上一篇,我们对IOC核心部分流程已经分析完毕,相信小伙伴们有所收获,从这一篇开始,我们将会踏上新的旅程,即Spring的另一核心:AOP! 首先,为了让大家能更有效的理解AOP,先带大家过一 ...

  2. 【Spring源码分析】AOP源码解析(上篇)

    前言 前面写了六篇文章详细地分析了Spring Bean加载流程,这部分完了之后就要进入一个比较困难的部分了,就是AOP的实现原理分析.为了探究AOP实现原理,首先定义几个类,一个Dao接口: pub ...

  3. Spring源码分析之AOP

    1.AOP简介 AOP即面向切面编程(Aspect Oriented Programming),通过预编译方式及运行期动态代理实现程序功能的统一维护的一种技术.使用aop对业务逻辑的各个部分进行隔离, ...

  4. 【Spring源码分析】AOP源码解析(下篇)

    AspectJAwareAdvisorAutoProxyCreator及为Bean生成代理时机分析 上篇文章说了,org.springframework.aop.aspectj.autoproxy.A ...

  5. Spring源码分析笔记--AOP

    核心类&方法 BeanDefinition Bean的定义信息,封装bean的基本信息,从中可以获取类名.是否是单例.是否被注入到其他bean中.是否懒加载.bean依赖的bean的名称等. ...

  6. Spring源码分析之Bean的创建过程详解

    前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostProcessor调用过程详解 本文内容: 在 ...

  7. Spring源码分析之`BeanFactoryPostProcessor`调用过程

    前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 本文内容: AbstractApplicationContext#refresh前部分的一点小内容 ...

  8. Spring源码分析之循环依赖及解决方案

    Spring源码分析之循环依赖及解决方案 往期文章: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostPro ...

  9. Spring源码分析——BeanFactory体系之抽象类、类分析(二)

    上一篇分析了BeanFactory体系的2个类,SimpleAliasRegistry和DefaultSingletonBeanRegistry——Spring源码分析——BeanFactory体系之 ...

随机推荐

  1. 拦截js方法备忘录

    很明显,以下代码拦截了fusion2.dialog.invite,然后在页面执行fusion2.dialog.invite方法的时候修改了参数中的img. <script> var old ...

  2. OD使用教程11

    首先把安装好的软件拖入PEID,看看它是用什么语言写的    然后用OD载入程序,查找关键字,步骤看上一个笔记 双击到达代码处,发现这在一个跳转里面.可能第一反应是修改跳转,经试验后发现这是没用的所以 ...

  3. hd 2112 HDU Today

    Problem Description 经过锦囊相助,海东集团终于度过了危机,从此,HDU的发展就一直顺风顺水,到了2050年,集团已经相当规模了,据说进入了钱江肉丝经济开发区500强.这时候,XHD ...

  4. Unity将来时:IL2CPP是什么?

    作者:小玉链接:https://zhuanlan.zhihu.com/p/19972689来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. Unity3D 想必大家都不陌 ...

  5. 原生ajax基础

    /*ajax对象的成员常用属性:responseText:以字符串形式接收服务器端返回的信息responseXML:以Xml Document对象形式接收服务器返回的信息readyState:返回当前 ...

  6. npm ERR publish 403,nodejs发布包流程

    nodejs学习体验之发布包,发布环境如下:1:win10系统,2:已安装nodejs. 具体操作步骤如下: *编写模块 1)新建文件夹,比如:somepackage 2) 该文件夹下新建js文件,比 ...

  7. 【菜鸟玩Linux开发】Redis安装和自启动配置

    Redis是一个C实现的基于内存.可持久化的键值对数据库,在分布式服务中常作为缓存服务.本篇将介绍在CentOS下如何从零开始安装到配置启动服务. 一. 安装Redis Redis的安装其实相当简单, ...

  8. Activity设置全屏的三种方法

    1.super.onCreate(savedInstanceState)方法之前调用:            setTheme(android.R.style.Theme_Light_NoTitleB ...

  9. step by step 之餐饮管理系统六(数据库访问模块)

    距上次写的博客已经好几个月,一方面公司里面有很多的东西要学,平时的时候又要写代码,所以没有及时更新,不过现在还好,已经成型了,现在把之前的东西贴出来,先看一下现在做的几个界面吧.第一个界面是用颜色用区 ...

  10. Pair Project:电梯控制程序

    12061160刘垚鹏 & 12061166宋天舒 1.1结对编程的优缺点结对编程相对于个人编程有很多优点.首先,督促作用,在讨论过程中能够很快投入工作,为了不耽误对方时间,我们会尽快完成各自 ...