写在前面

  Spring AOP中Pointcut,dvice 和 Advisor三个概念

  1)切入点 Pointcut

    在介绍Pointcut之前,有必要先介绍 Join Point(连接点)概念。

    连接点:程序运行中的某个阶段点,比如方法的调用、异常的抛出等。比如方法doSome();Pointcut是JoinPoint的集合,它是程序中需要注入Advice 的位置的集合,指明Advice要在什么样的条件下才能被触发。org.springframework.aop.Pointcut接口用来指定到特定的类和方法。

  2)通知Advice

    它是某个连接点所采用的处理逻辑,也就是向连接点注入的代码。例如:输出的日志信息 就是一个Advice。

  3)Advisor

    Advisor是Pointcut和Advice的配置器,它包括Pointcut和Advice,是将Advice注入程序中Pointcut位置的代码。

  1. <aop:aspectj-autoproxy/>
  2. <aop:config proxy-target-class="true">
  3. <aop:pointcut id="servicePointcut" expression="execution(* com.cpic..*Service.*(..))" />
  4. <aop:advisor pointcut-ref="servicePointcut" advice-ref="txAdvice" order="3" />
  5. </aop:config>
  6. <tx:advice id="txAdvice" transaction-manager="transactionManager">
  7. <tx:attributes>
  8. <tx:method name="add*" />
  9. </tx:attributes>
  10. </tx:advice>

接下来就需要看下配置完成之后是如何生成代理对象的

  还是要从对xml中的配置<aop:config>标签的解析来入手。同样是从标签解析接口开始,即找BeanDefinitionParser的实现类,最终我们会找到AspectJAutoProxyBeanDefinitionParser是用来处理aspectj-autoproxy标签的,而ConfigBeanDefinitionParser则是用来处理aop:config标签的。看下ConfigBeanDefinitionParser的解析过程:

  1. public BeanDefinition parse(Element element, ParserContext parserContext) {
  2. CompositeComponentDefinition compositeDef =
  3. new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
  4. parserContext.pushContainingComponent(compositeDef);
  5. configureAutoProxyCreator(parserContext, element);
  6. List<Element> childElts = DomUtils.getChildElements(element);
  7. for (Element elt: childElts) {
  8. String localName = parserContext.getDelegate().getLocalName(elt);
  9. if (POINTCUT.equals(localName)) {
  10. parsePointcut(elt, parserContext);
  11. }
  12. else if (ADVISOR.equals(localName)) {
  13. parseAdvisor(elt, parserContext);
  14. }
  15. else if (ASPECT.equals(localName)) {
  16. parseAspect(elt, parserContext);
  17. }
  18. }
  19. parserContext.popAndRegisterContainingComponent();
  20. return null;
  21. }

  以上过程比较费劲,有兴趣的可以弄清楚。这里主要注册一些Advisor,同时注册了一个AspectJAwareAdvisorAutoProxyCreator,并且设置xml中所配置的proxy-target-class和expose-proxy到它的属性中。AspectJAwareAdvisorAutoProxyCreator本身存储着配置信息,然后使用这些配置创建出来代理对象,在它的父类AbstractAutoProxyCreator的createProxy方法中:

  1. protected Object createProxy(
  2. Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
  3.  
  4. ProxyFactory proxyFactory = new ProxyFactory();
  5. // Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
  6. //重点1 proxyFactory.copyFrom(this);将ProxyConfig信息复制到ProxyFactory 中。ProxyFactory、AspectJAwareAdvisorAutoProxyCreator都继承了ProxyConfig,ProxyConfig拥有代理的一些配置信息。看下ProxyConfig:
  7. proxyFactory.copyFrom(this);
  8. //重点2
  9. if (!proxyFactory.isProxyTargetClass()) {
  10. if (shouldProxyTargetClass(beanClass, beanName)) {
  11. proxyFactory.setProxyTargetClass(true);
  12. }
  13. else {
  14. evaluateProxyInterfaces(beanClass, proxyFactory);
  15. }
  16. }
  17. Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
  18. for (Advisor advisor : advisors) {
  19. proxyFactory.addAdvisor(advisor);
  20. }
  21. proxyFactory.setTargetSource(targetSource);
  22. customizeProxyFactory(proxyFactory);
  23. proxyFactory.setFrozen(this.freezeProxy);
  24. if (advisorsPreFiltered()) {
  25. proxyFactory.setPreFiltered(true);
  26. }
  27. //重点3
  28. return proxyFactory.getProxy(this.proxyClassLoader);
  29. }

然后我们就详细的说明下整个过程: 
  重点1:proxyFactory.copyFrom(this);将ProxyConfig信息复制到ProxyFactory 中。ProxyFactory、AspectJAwareAdvisorAutoProxyCreator都继承了ProxyConfig,ProxyConfig拥有代理的一些配置信息。

  看下ProxyConfig:

  1. public class ProxyConfig implements Serializable {
  2. //是否强制使用CGLIB 代理对象
  3. private boolean proxyTargetClass = false;
  4. //是否优化
  5. private boolean optimize = false;
  6. boolean opaque = false;
  7. //是否在线程内部暴露出代理对象
  8. boolean exposeProxy = false;
  9. private boolean frozen = false;
  10. }

  

  重点2:复制完配置信息后,看下proxyTargetClass 属性是否为false,则查看目标类是否含有接口,若无则仍然设置proxyTargetClass为true,若有则把接口设置到ProxyFactory中。然后在设置些Advisor、targetSource等其他参数,为创建代理对象做准备。来看下上述Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);的具体内容:

  1. protected Advisor[] buildAdvisors(String beanName, Object[] specificInterceptors) {
  2. // Handle prototypes correctly...
  3. Advisor[] commonInterceptors = resolveInterceptorNames();
  4. List<Object> allInterceptors = new ArrayList<Object>();
  5. if (specificInterceptors != null) {
  6. allInterceptors.addAll(Arrays.asList(specificInterceptors));
  7. if (commonInterceptors != null) {
  8. if (this.applyCommonInterceptorsFirst) {
  9. allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
  10. }
  11. else {
  12. allInterceptors.addAll(Arrays.asList(commonInterceptors));
  13. }
  14. }
  15. }
  16. Advisor[] advisors = new Advisor[allInterceptors.size()];
  17. for (int i = 0; i < allInterceptors.size(); i++) {
  18. ////重点重点重点重点重点重点重点(对配置信息中的specificInterceptors全部封装成Advisor。再看下具体的封装过程,在上述wrap方法中 )
  19. advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));
  20. }
  21. return advisors;
  22. }

  具体的封装过程(如果是Advisor直接返回不处理,接下来必须是Advice,然后通过MethodInterceptor和AdvisorAdapter 对Advice进行包装。)

  1. @Override
  2. public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
  3. if (adviceObject instanceof Advisor) {
  4. return (Advisor) adviceObject;
  5. }
  6. if (!(adviceObject instanceof Advice)) {
  7. throw new UnknownAdviceTypeException(adviceObject);
  8. }
  9. Advice advice = (Advice) adviceObject;
  10. if (advice instanceof MethodInterceptor) {
  11. // So well-known it doesn't even need an adapter.
  12. return new DefaultPointcutAdvisor(advice);
  13. }
  14. for (AdvisorAdapter adapter : this.adapters) {
  15. // Check that it is supported.
  16. if (adapter.supportsAdvice(advice)) {
  17. return new DefaultPointcutAdvisor(advice);
  18. }
  19. }
  20. throw new UnknownAdviceTypeException(advice);
  21. }

  重点3:使用DefaultAopProxyFactory来创建AopProxy,有了AopProxy我们就能创建代理对象了。看下AopProxy的创建过程:

  1. @Override
  2. public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
  3. if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
  4. Class<?> targetClass = config.getTargetClass();
  5. if (targetClass == null) {
  6. throw new AopConfigException("TargetSource cannot determine target class: " +
  7. "Either an interface or a target is required for proxy creation.");
  8. }
  9. if (targetClass.isInterface()) {
  10. return new JdkDynamicAopProxy(config);
  11. }
  12. return new ObjenesisCglibAopProxy(config);
  13. }
  14. else {
  15. return new JdkDynamicAopProxy(config);
  16. }
  17. }

这里决定着到底采用jdk动态代理还是cglib方式来创建代理对象。 
  条件1:config.isOptimize()是否进行优化,默认是false。

  条件2:config.isProxyTargetClass()就是ProxyConfig的proxyTargetClass属性,是否强制使用cglib代理。但它为true也不是肯定就采用cglib,因为下面还有一个判断条件,即目标类是接口,则使用jdk动态代理的方式。

  条件3:hasNoUserSuppliedProxyInterfaces(config)目标类没有实现接口,或者有但是是接口类型是SpringProxy

  只要上述三个条件有一个为true并且目标类不是接口就会采用cglib方式来创建代理对象,其他情况使用jdk动态代理的方式来创建。

  有了JdkDynamicAopProxy和ObjenesisCglibAopProxy则可以顺利创建出代理对象,便可以跳到这篇文章http://www.cnblogs.com/chihirotan/p/7063781.html,至此整个过程就连接通了。

总结 

我们先看下ProxyFactory是什么东西。 把下面的图理解透了,就掌握了SpringAOP的整个运行机制

spring---aop(5)---Spring AOP的配置的背后的配置的更多相关文章

  1. spring tx:advice 和 aop:config 配置事务

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...

  2. spring AOP 之二:@AspectJ注解的3种配置

    @AspectJ相关文章 <spring AOP 之二:@AspectJ注解的3种配置> <spring AOP 之三:使用@AspectJ定义切入点> <spring ...

  3. [转]spring tx:advice 和 aop:config 配置事务

      <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www. ...

  4. 《Java Spring框架》Spring切面(AOP)配置详解

    1.  Spring 基本概念 AOP(Aspect Oriented Programming)称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2 ...

  5. Spring之AOP原理、代码、使用详解(XML配置方式)

    Spring 的两大核心,一是IOC,另一个是AOP,本博客从原理.AOP代码以及AOP使用三个方向来讲AOP.先给出一张AOP相关的结构图,可以放大查看. 一.Spring AOP 接口设计 1.P ...

  6. 阶段3 2.Spring_08.面向切面编程 AOP_9 spring基于注解的AOP配置

    复制依赖和改jar包方式 src下的都复制过来. 复制到新项目里了 bean.xml里面复制上面一行代码到下面.把aop改成context. 配置spring容器创建时要扫描的包 Service的配置 ...

  7. Spring MVC 中使用AOP 进行统一日志管理--XML配置实现

    1.介绍 上一篇博客写了使用AOP进行统一日志管理的注解版实现,今天写一下使用XML配置实现版本,与上篇不同的是上次我们记录的Controller层日志,这次我们记录的是Service层的日志.使用的 ...

  8. 死磕Spring之AOP篇 - Spring AOP注解驱动与XML配置

    该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读. Spring 版本:5.1 ...

  9. Spring的IOC和AOP之深剖

    今天,既然讲到了Spring 的IOC和AOP,我们就必须要知道 Spring主要是两件事: 1.开发Bean:2.配置Bean.对于Spring框架来说,它要做的,就是根据配置文件来创建bean实例 ...

随机推荐

  1. jQuery-对标签元素 文本操作-属性操作-文档的操作

    一.对标签元素文本操作 1.1 对标签中内容的操作 // js var div1 = document.getElementById("div1"); div1.innerText ...

  2. 使用UDP和TCP协议的各种应用和应用层协议

    IGMP和ICMP是传输层协议

  3. RedisTemplate使用

    RedisTemplate中定义了对5种数据结构操作 redisTemplate.opsForValue();//操作字符串 redisTemplate.opsForHash();//操作hash r ...

  4. springboot配置fastjson后端往前端传输格式化

    import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.Spri ...

  5. Go 命令行总结

    go build:已当前目录作为package进行编译,将当前目录下的所有文件编译成package文件,文件名与所在的目录同名. go install: 分两步操作:1.先执行go build进行编译 ...

  6. Linux sudo 配置

    1.配置sudo权限 sudo配置信息保存在 /etc/sudoers 文件中,可以使用vi修改.但使用vi修改需要给root用户设置权限,可以使用 visudo 直接修改. visudo 复制这行将 ...

  7. 第五届CCF软件能力认证

    1.数列分段 问题描述 给定一个整数数列,数列中连续相同的最长整数序列算成一段,问数列中共有多少段? 输入格式 输入的第一行包含一个整数n,表示数列中整数的个数. 第二行包含n个整数a1, a2, … ...

  8. 使用linux mysql客户端建立表时遇到格式解析的问题

    发现在notepad++写好的建表脚本,粘贴到linux客户端后,执行时总是报我的脚本有问题. 我看了又看,发现建表脚本本身是没有问题,问题出在"Tab"键上和注释上边了. 解决办 ...

  9. 7-10 守卫棋盘 uva11214

    输入要给n*m的棋盘  均小于10   某些格子有标记  用最少的皇后  辐射到所有的标记 限时 6666ms 用IDA*    时间6000  尴尬. #include<bits/stdc++ ...

  10. 华三IRF的配置

    https://blog.csdn.net/VictoryKingLIU/article/details/79255901 拓扑结构 1 配置成员编号(重启) 2 配置成员优先级(大的主设备) 3 配 ...