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,至此整个过程就连接通了。 

目录
相关文章
|
20天前
|
负载均衡 监控 Java
Spring Cloud Gateway 全解析:路由配置、断言规则与过滤器实战指南
本文详细介绍了 Spring Cloud Gateway 的核心功能与实践配置。首先讲解了网关模块的创建流程,包括依赖引入(gateway、nacos 服务发现、负载均衡)、端口与服务发现配置,以及路由规则的设置(需注意路径前缀重复与优先级 order)。接着深入解析路由断言,涵盖 After、Before、Path 等 12 种内置断言的参数、作用及配置示例,并说明了自定义断言的实现方法。随后重点阐述过滤器机制,区分路由过滤器(如 AddRequestHeader、RewritePath、RequestRateLimiter 等)与全局过滤器的作用范围与配置方式,提
Spring Cloud Gateway 全解析:路由配置、断言规则与过滤器实战指南
|
12天前
|
Java 关系型数据库 MySQL
Spring Boot自动配置:魔法背后的秘密
Spring Boot 自动配置揭秘:只需简单配置即可启动项目,背后依赖“约定大于配置”与条件化装配。核心在于 `@EnableAutoConfiguration` 注解与 `@Conditional` 系列条件判断,通过 `spring.factories` 或 `AutoConfiguration.imports` 加载配置类,实现按需自动装配 Bean。
|
13天前
|
人工智能 Java 开发者
【Spring】原理解析:Spring Boot 自动配置
Spring Boot通过“约定优于配置”的设计理念,自动检测项目依赖并根据这些依赖自动装配相应的Bean,从而解放开发者从繁琐的配置工作中解脱出来,专注于业务逻辑实现。
|
2月前
|
Java Spring
Spring Boot配置的优先级?
在Spring Boot项目中,配置可通过配置文件和外部配置实现。支持的配置文件包括application.properties、application.yml和application.yaml,优先级依次降低。外部配置常用方式有Java系统属性(如-Dserver.port=9001)和命令行参数(如--server.port=10010),其中命令行参数优先级高于系统属性。整体优先级顺序为:命令行参数 &gt; Java系统属性 &gt; application.properties &gt; application.yml &gt; application.yaml。
541 0
|
4月前
|
监控 安全 Java
Spring AOP实现原理
本内容主要介绍了Spring AOP的核心概念、实现机制及代理生成流程。涵盖切面(Aspect)、连接点(Join Point)、通知(Advice)、切点(Pointcut)等关键概念,解析了JDK动态代理与CGLIB代理的原理及对比,并深入探讨了通知执行链路和责任链模式的应用。同时,详细分析了AspectJ注解驱动的AOP解析过程,包括切面识别、切点表达式匹配及通知适配为Advice的机制,帮助理解Spring AOP的工作原理与实现细节。
|
6天前
|
缓存 Java 应用服务中间件
Spring Boot配置优化:Tomcat+数据库+缓存+日志,全场景教程
本文详解Spring Boot十大核心配置优化技巧,涵盖Tomcat连接池、数据库连接池、Jackson时区、日志管理、缓存策略、异步线程池等关键配置,结合代码示例与通俗解释,助你轻松掌握高并发场景下的性能调优方法,适用于实际项目落地。
129 4
|
13天前
|
传感器 Java 数据库
探索Spring Boot的@Conditional注解的上下文配置
Spring Boot 的 `@Conditional` 注解可根据不同条件动态控制 Bean 的加载,提升应用的灵活性与可配置性。本文深入解析其用法与优势,并结合实例展示如何通过自定义条件类实现环境适配的智能配置。
探索Spring Boot的@Conditional注解的上下文配置
|
5月前
|
安全 Java API
深入解析 Spring Security 配置中的 CSRF 启用与 requestMatchers 报错问题
本文深入解析了Spring Security配置中CSRF启用与`requestMatchers`报错的常见问题。针对CSRF,指出默认已启用,无需调用`enable()`,只需移除`disable()`即可恢复。对于`requestMatchers`多路径匹配报错,分析了Spring Security 6.x中方法签名的变化,并提供了三种解决方案:分次调用、自定义匹配器及降级使用`antMatchers()`。最后提醒开发者关注版本兼容性,确保升级平稳过渡。
661 2
|
1月前
|
人工智能 监控 安全
如何快速上手【Spring AOP】?核心应用实战(上篇)
哈喽大家好吖~欢迎来到Spring AOP系列教程的上篇 - 应用篇。在本篇,我们将专注于Spring AOP的实际应用,通过具体的代码示例和场景分析,帮助大家掌握AOP的使用方法和技巧。而在后续的下篇中,我们将深入探讨Spring AOP的实现原理和底层机制。 AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的核心特性之一,它能够帮助我们解决横切关注点(如日志记录、性能统计、安全控制、事务管理等)的问题,提高代码的模块化程度和复用性。
|
1月前
|
安全 算法 Java
在Spring Boot中应用Jasypt以加密配置信息。
通过以上步骤,可以在Spring Boot应用中有效地利用Jasypt对配置信息进行加密,这样即使配置文件被泄露,其中的敏感信息也不会直接暴露给攻击者。这是一种在不牺牲操作复杂度的情况下提升应用安全性的简便方法。
635 10