创建日期: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. 陨石坑之webapi使用filter

    首先为什么说这是一个坑,是因为我们在webapi中使用filter的时候也许会先百度一下,好吧,挖坑的来了,我看了好几篇文章写的是使用System.Web.Mvc.Filters.ActionFilt ...

  2. jQuery .css color 重写 :hover样式没了

    $("#quickSqlDiv a").css({"color":"red"}); $("#course a").css ...

  3. 页面点击任意js事件,触发360、IE浏览器新页面

    在<head></head>标签中 <base target=_self> 不会再增加页面

  4. listener监听器

    前言:之前写了一篇关于Filter的文章:http://tianweili.github.io/blog/2015/01/26/java-filter/,现在再来一篇Listener的,Filter和 ...

  5. 浅谈struts2之chain

    转自:http://blog.csdn.net/randomnet/article/details/8656759 前一段时间,有关chain的机制着实困绕了许久.尽管网上有许多关于chain的解说, ...

  6. 启动tomcat时遇到的问题

    1.当启动Tomcat时,出现了如下信息: The APR based Apache Tomcat Native library which allows optimal performance in ...

  7. 命令安装VS

     Installing Visual Studio Visual Studio 2015   Other Versions Visual Studio 2013 Visual Studio 2010 ...

  8. [转]Android开发:Parallax效果的ScrollerView,改编自ParallaxListView

    https://github.com/nirhart/ParallaxScroll这个gethub上的地址 本文转自http://www.2cto.com/kf/201502/376970.html ...

  9. Linux初记

    ctrl+u可以在shell下删除行,如果此键不起作用,就试试ctrl+x ctrl+z可以将程序挂起,不会终止程序,但可以将程序挂起. 通过fg命令可再把此作业切换到前台 cp命令的目标文件如果是一 ...

  10. 【转】【WebDriver】不可编辑域和日历控件域的输入 javascript

    http://blog.csdn.net/fudax/article/details/8089404 今天用到日历控件,用第一个javascript执行后页面上的日期控件后,在html中可以看到生效日 ...