Spring AOP源码分析(六)Spring AOP配置的背后

简介:
本篇文章主要对Spring AOP配置背后进行了哪些事情做下说明。还是如上类似的工程,在xml中AOP拦截配置如下:  
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<bean id= "aspectBean" class = "com.lg.aop.TestAspect" />
     
     <aop:config expose-proxy= "false" proxy-target- class = "false"
         <aop:aspect id= "TestAspect" ref= "aspectBean"
             
             <aop:pointcut id= "businessService1" 
                 expression= "execution(* com.lg.aop.service.*.bar*(..))" /> 
             <aop:pointcut id= "businessService2" 
                 expression= "execution(* com.lg.aop.service.*.foo*(..))" /> 
             <aop:before pointcut-ref= "businessService1" method= "doBefore" /> 
             <aop:after pointcut-ref= "businessService2" method= "doAfter" /> 
             <aop:around pointcut-ref= "businessService2" method= "doAround" />
             <aop:after-throwing pointcut-ref= "businessService1" method= "doThrowing" throwing= "ex" /> 
         </aop:aspect> 
     </aop:config>

其中接口AService和类BServiceImpl都在com.lg.aop.service包下,AService的实现类 
AServiceImpl在com.lg.aop.service.impl包下。
 
?
1
2
3
4
5
6
public interface AService {
 
     public void fooA(String _msg); 
       
     public void barA();
}

?
1
2
3
4
5
6
7
8
9
10
11
12
13
@Service
public class BServiceImpl {
 
      public static final void barB(String _msg, int _type) { 
         System.out.println( "BServiceImpl.barB(msg:" +_msg+ " type:" +_type+ ")" ); 
         if (_type == 1
             throw new IllegalArgumentException( "测试异常" ); 
     
   
     public void fooB() { 
         System.out.println( "BServiceImpl.fooB()" ); 
    
}

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Service
public class AServiceImpl implements AService{
 
     @Override
     public void fooA(String _msg) {
          System.out.println( "AServiceImpl.fooA(msg:" +_msg+ ")" );
     }
 
     @Override
     public void barA() {
          System.out.println( "AServiceImpl.barA()" ); 
     }
 
}

测试方法如下:  
?
1
2
3
4
5
@Test
     public void testAOP(){
         aService.barA();
         bServiceImpl.fooB();
     }

运行效果如下:  
?
1
2
3
4
5
log Begining method: com.lg.aop.service.impl.AServiceImpl.barA
AServiceImpl.barA()
BServiceImpl.fooB()
process time: 12 ms
log Ending method: com.lg.aop.service.BServiceImpl.fooB

接下来就需要看下配置完成之后是如何生成代理对象的。 
还是要从对xml中的配置<aop:config>标签的解析来入手。同样是从标签解析接口开始,即找BeanDefinitionParser的实现类,最终我们会找到AspectJAutoProxyBeanDefinitionParser是用来处理aspectj-autoproxy标签的,而ConfigBeanDefinitionParser则是用来处理aop:config标签的。看下ConfigBeanDefinitionParser的解析过程:
 
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public BeanDefinition parse(Element element, ParserContext parserContext) {
         CompositeComponentDefinition compositeDef =
                 new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
         parserContext.pushContainingComponent(compositeDef);
 
         configureAutoProxyCreator(parserContext, element);
 
         List<Element> childElts = DomUtils.getChildElements(element);
         for (Element elt: childElts) {
             String localName = parserContext.getDelegate().getLocalName(elt);
             if (POINTCUT.equals(localName)) {
                 parsePointcut(elt, parserContext);
             }
             else if (ADVISOR.equals(localName)) {
                 parseAdvisor(elt, parserContext);
             }
             else if (ASPECT.equals(localName)) {
                 parseAspect(elt, parserContext);
             }
         }
 
         parserContext.popAndRegisterContainingComponent();
         return null ;
     }

这个过程比较费劲,有兴趣的可以弄清楚。这里主要注册一些Advisor,同时注册了一个AspectJAwareAdvisorAutoProxyCreator,并且设置xml中所配置的proxy-target-class和expose-proxy到它的属性中。AspectJAwareAdvisorAutoProxyCreator本身存储着配置信息,然后使用这些配置创建出来代理对象,在它的父类AbstractAutoProxyCreator的createProxy方法中:  
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
protected Object createProxy(
             Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
 
         ProxyFactory proxyFactory = new ProxyFactory();
         // Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
//重点1
         proxyFactory.copyFrom( this );
 
//重点2
         if (!proxyFactory.isProxyTargetClass()) {
             if (shouldProxyTargetClass(beanClass, beanName)) {
                 proxyFactory.setProxyTargetClass( true );
             }
             else {
                 evaluateProxyInterfaces(beanClass, proxyFactory);
             }
         }
 
         Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
         for (Advisor advisor : advisors) {
             proxyFactory.addAdvisor(advisor);
         }
 
         proxyFactory.setTargetSource(targetSource);
         customizeProxyFactory(proxyFactory);
 
         proxyFactory.setFrozen( this .freezeProxy);
         if (advisorsPreFiltered()) {
             proxyFactory.setPreFiltered( true );
         }
//重点3
         return proxyFactory.getProxy( this .proxyClassLoader);
     }

在该方法中创建出代理对象,待会我们再详细说这个过程。我们先看下ProxyFactory是什么东西。 
把下面的图理解透了,就掌握了SpringAOP的整个运行机制。
 

 
然后我们就详细的说明下整个过程: 
重点1:proxyFactory.copyFrom(this);将ProxyConfig信息复制到ProxyFactory 中。ProxyFactory、AspectJAwareAdvisorAutoProxyCreator都继承了ProxyConfig,ProxyConfig拥有代理的一些配置信息。看下ProxyConfig:
 
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ProxyConfig implements Serializable {
 
     /** use serialVersionUID from Spring 1.2 for interoperability */
     private static final long serialVersionUID = -8409359707199703185L;
 
     private boolean proxyTargetClass = false ;
 
     private boolean optimize = false ;
 
     boolean opaque = false ;
 
     boolean exposeProxy = false ;
 
     private boolean frozen = false ;
}

含有两个我们所关注的proxyTargetClass,exposeProxy : 
proxyTargetClass:是否强制使用cglib来实现代理 
exposeProxy:是否在线程内部暴露出代理对象(使用ThreadLocal模式实现线程内共享,见http://lgbolgger.iteye.com/blog/2116164中对exposeProxy的描述)。 

重点2:复制完配置信息后,看下proxyTargetClass 属性是否为false,则查看目标类是否含有接口,若无则仍然设置proxyTargetClass为true,若有则把接口设置到ProxyFactory中。然后在设置些Advisor、targetSource等其他参数,为创建代理对象做准备。来看下上述Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);的具体内容:
 
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
protected Advisor[] buildAdvisors(String beanName, Object[] specificInterceptors) {
         // Handle prototypes correctly...
         Advisor[] commonInterceptors = resolveInterceptorNames();
 
         List<Object> allInterceptors = new ArrayList<Object>();
         if (specificInterceptors != null ) {
             allInterceptors.addAll(Arrays.asList(specificInterceptors));
             if (commonInterceptors != null ) {
                 if ( this .applyCommonInterceptorsFirst) {
                     allInterceptors.addAll( 0 , Arrays.asList(commonInterceptors));
                 }
                 else {
                     allInterceptors.addAll(Arrays.asList(commonInterceptors));
                 }
             }
         }
         if (logger.isDebugEnabled()) {
             int nrOfCommonInterceptors = (commonInterceptors != null ? commonInterceptors.length : 0 );
             int nrOfSpecificInterceptors = (specificInterceptors != null ? specificInterceptors.length : 0 );
             logger.debug( "Creating implicit proxy for bean '" + beanName + "' with " + nrOfCommonInterceptors +
                     " common interceptors and " + nrOfSpecificInterceptors + " specific interceptors" );
         }
 
         Advisor[] advisors = new Advisor[allInterceptors.size()];
         for ( int i = 0 ; i < allInterceptors.size(); i++) {
 
//重点重点重点重点重点重点重点
             advisors[i] = this .advisorAdapterRegistry.wrap(allInterceptors.get(i));
         }
         return advisors;
     }

对配置信息中的specificInterceptors全部封装成Advisor。再看下具体的封装过程,在上述wrap方法中  
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
         if (adviceObject instanceof Advisor) {
             return (Advisor) adviceObject;
         }
         if (!(adviceObject instanceof Advice)) {
             throw new UnknownAdviceTypeException(adviceObject);
         }
         Advice advice = (Advice) adviceObject;
         if (advice instanceof MethodInterceptor) {
             // So well-known it doesn't even need an adapter.
             return new DefaultPointcutAdvisor(advice);
         }
         for (AdvisorAdapter adapter : this .adapters) {
             // Check that it is supported.
             if (adapter.supportsAdvice(advice)) {
                 return new DefaultPointcutAdvisor(advice);
             }
         }
         throw new UnknownAdviceTypeException(advice);
     }

如果是Advisor直接返回不处理,接下来必须是Advice,然后通过MethodInterceptor和AdvisorAdapter 对Advice进行包装。对此过程还不清楚的,请先去看之前的接口介绍(http://lgbolgger.iteye.com/blog/2117214) 
重点3:使用DefaultAopProxyFactory来创建AopProxy,有了AopProxy我们就能创建代理对象了。看下AopProxy的创建过程:
 
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
     public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
         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." );
             }
             if (targetClass.isInterface()) {
                 return new JdkDynamicAopProxy(config);
             }
             return new ObjenesisCglibAopProxy(config);
         }
         else {
             return new JdkDynamicAopProxy(config);
         }
     }

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

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

条件3:hasNoUserSuppliedProxyInterfaces(config)目标类没有实现接口,或者有但是是接口类型是SpringProxy,如下:
 
?
1
2
3
4
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
         Class<?>[] interfaces = config.getProxiedInterfaces();
         return (interfaces.length == 0 || (interfaces.length == 1 && SpringProxy. class .equals(interfaces[ 0 ])));
     }

只要上述三个条件有一个为true并且目标类不是接口就会采用cglib方式来创建代理对象,其他情况使用jdk动态代理的方式来创建。 
有了JdkDynamicAopProxy和ObjenesisCglibAopProxy则可以顺利创建出代理对象,便可以跳到这篇文章http://lgbolgger.iteye.com/blog/2116164,至此整个过程就连接通了。 

目录
相关文章
|
11天前
|
监控 Java 应用服务中间件
Spring Boot整合Tomcat底层源码分析
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置和起步依赖等特性,大大简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是其与Tomcat的整合。
35 1
|
12天前
|
Java 开发者 微服务
手写模拟Spring Boot自动配置功能
【11月更文挑战第19天】随着微服务架构的兴起,Spring Boot作为一种快速开发框架,因其简化了Spring应用的初始搭建和开发过程,受到了广大开发者的青睐。自动配置作为Spring Boot的核心特性之一,大大减少了手动配置的工作量,提高了开发效率。
32 0
|
1月前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
48 4
|
1月前
|
Java API 数据安全/隐私保护
(工作经验)优雅实现接口权限校验控制:基于自定义注解、AOP与@ConditionalOnProperty配置开关的通用解决方案
(工作经验)优雅实现接口权限校验控制:基于自定义注解、AOP与@ConditionalOnProperty配置开关的通用解决方案
63 1
|
1月前
|
Java API 数据库
Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐
本文通过在线图书管理系统案例,详细介绍如何使用Spring Boot构建RESTful API。从项目基础环境搭建、实体类与数据访问层定义,到业务逻辑实现和控制器编写,逐步展示了Spring Boot的简洁配置和强大功能。最后,通过Postman测试API,并介绍了如何添加安全性和异常处理,确保API的稳定性和安全性。
38 0
|
30天前
|
Java API Spring
在 Spring 配置文件中配置 Filter 的步骤
【10月更文挑战第21天】在 Spring 配置文件中配置 Filter 是实现请求过滤的重要手段。通过合理的配置,可以灵活地对请求进行处理,满足各种应用需求。还可以根据具体的项目要求和实际情况,进一步深入研究和优化 Filter 的配置,以提高应用的性能和安全性。
|
17天前
|
XML Java 数据安全/隐私保护
Spring Aop该如何使用
本文介绍了AOP(面向切面编程)的基本概念和术语,并通过具体业务场景演示了如何在Spring框架中使用Spring AOP。文章详细解释了切面、连接点、通知、切点等关键术语,并提供了完整的示例代码,帮助读者轻松理解和应用Spring AOP。
Spring Aop该如何使用
|
1月前
|
存储 缓存 Java
Spring高手之路23——AOP触发机制与代理逻辑的执行
本篇文章深入解析了Spring AOP代理的触发机制和执行流程,从源码角度详细讲解了Bean如何被AOP代理,包括代理对象的创建、配置与执行逻辑,帮助读者全面掌握Spring AOP的核心技术。
40 3
Spring高手之路23——AOP触发机制与代理逻辑的执行
|
22天前
|
Java Spring
[Spring]aop的配置与使用
本文介绍了AOP(面向切面编程)的基本概念和核心思想。AOP是Spring框架的核心功能之一,通过动态代理在不修改原代码的情况下注入新功能。文章详细解释了连接点、切入点、通知、切面等关键概念,并列举了前置通知、后置通知、最终通知、异常通知和环绕通知五种通知类型。
30 1
|
27天前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
31 1
下一篇
无影云桌面