Spring AOP源码分析(七)ProxyFactoryBean介绍

简介:
这篇文章里面就要说说Spring自己的AOP,搞清楚哪种方式是Spring自己实现的AOP,哪种方式是Spring引入aspectj的AOP。 
Spring自己的AOP实现在于ProxyFactoryBean。先看下使用案例(仍和之前的案例是一样的):接口AService、实现类AServiceImpl、通知MyBeforeAdvice
 
?
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
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
6
7
8
9
public class MyBeforeAdvice implements MethodBeforeAdvice{
 
     @Override
     public void before(Method method, Object[] args, Object target)
             throws Throwable {
         System.out.println( "run my before advice" );
     }
 
}

然后就是xml的配置:  
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<bean id= "aServiceImpl" class = "com.lg.aop.service.impl.AServiceImpl" />
     <bean id= "myBeforAdvice" class = "com.lg.aop.MyBeforeAdvice" />
     
     <bean class = "org.springframework.aop.framework.ProxyFactoryBean" >
         <property name= "interfaces" value= "com.lg.aop.service.AService" />
         <property name= "target" >
             <ref bean= "aServiceImpl" />
         </property>
          <property name= "interceptorNames"
             <list> 
                 <value>myBeforAdvice</value> 
             </list> 
         </property> 
     </bean>

然后就可以使用了:  
?
1
2
3
4
5
6
7
@Autowired
     private AService aService;
     
     @Test
     public void testAOP(){
         aService.barA();
     }

运行这个单元测试,然后你就会看到报如下错误:  
?
1
No qualifying bean of type [com.lg.aop.service.AService] is defined: expected single matching bean but found 2 : aServiceImpl,org.springframework.aop.framework.ProxyFactoryBean# 0

原因就是对于接口AService,有两个实现类aServiceImpl和ProxyFactoryBean所生产的代理类。所以我们不能使用@Autowired(它是按类型注入),所以要使用按名称注入,我们怎么获取ProxyFactoryBean所产生的代理类的名称呢?其实就是ProxyFactoryBean配置的名称。因为ProxyFactoryBean实现了FactoryBean接口,对于这种接口从容器中获取该bean,不是获取的本身而是获取他的getObject方法所返回的值,看FactoryBean的文档:  
?
1
2
3
4
5
6
7
8
9
10
/**
  * Interface to be implemented by objects used within a { @link BeanFactory}
  * which are themselves factories. If a bean implements this interface ,
  * it is used as a factory for an object to expose, not directly as a bean
  * instance that will be exposed itself.
  *
  * <p><b>NB: A bean that implements this interface cannot be used as a
  * normal bean.</b> A FactoryBean is defined in a bean style, but the
  * object exposed for bean references ({ @link #getObject()} is always
  * the object that it creates.

所以通过beanName找到了ProxyFactoryBean,然而不是返回该对象,而是返回他的getObject方法的返回值,所以我们通过ProxyFactoryBean的id就可以获取到它所产生的代理对象,所以更改如下:  
?
1
2
<bean  id= "aServiceImplProxy" class = "org.springframework.aop.framework.ProxyFactoryBean" >
其他略

在使用注入的时候按名称注入  
?
1
2
@Resource (name= "aServiceImplProxy" )
     private AService aService;

然后就可以正常运行了如下:  
?
1
2
run my before advice
AServiceImpl.barA()

然后我们就要源码分析下这一过程,先看下是如何产生代理对象的,在ProxyFactoryBean的getObject方法中:  
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public Object getObject() throws BeansException {
//重点1
         initializeAdvisorChain();
         if (isSingleton()) {
//重点2
             return getSingletonInstance();
         }
         else {
             if ( this .targetName == null ) {
                 logger.warn( "Using non-singleton proxies with singleton targets is often undesirable. " +
                         "Enable prototype proxies by setting the 'targetName' property." );
             }
             return newPrototypeInstance();
         }
     }

重点1:就是根据我们配置的interceptorNames来获取对应的bean,并却转化成Advisor。如下:  
?
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
         if ( this .advisorChainInitialized) {
             return ;
         }
 
         if (!ObjectUtils.isEmpty( this .interceptorNames)) {
             if ( this .beanFactory == null ) {
                 throw new IllegalStateException( "No BeanFactory available anymore (probably due to serialization) " +
                         "- cannot resolve interceptor names " + Arrays.asList( this .interceptorNames));
             }
 
             // Globals can't be last unless we specified a targetSource using the property...
             if ( this .interceptorNames[ this .interceptorNames.length - 1 ].endsWith(GLOBAL_SUFFIX) &&
                     this .targetName == null && this .targetSource == EMPTY_TARGET_SOURCE) {
                 throw new AopConfigException( "Target required after globals" );
             }
 
             // Materialize interceptor chain from bean names.
             for (String name : this .interceptorNames) {
                 if (logger.isTraceEnabled()) {
                     logger.trace( "Configuring advisor or advice '" + name + "'" );
                 }
 
                 if (name.endsWith(GLOBAL_SUFFIX)) {
                     if (!( this .beanFactory instanceof ListableBeanFactory)) {
                         throw new AopConfigException(
                                 "Can only use global advisors or interceptors with a ListableBeanFactory" );
                     }
                     addGlobalAdvisor((ListableBeanFactory) this .beanFactory,
                             name.substring( 0 , name.length() - GLOBAL_SUFFIX.length()));
                 }
 
                 else {
                     // If we get here, we need to add a named interceptor.
                     // We must check if it's a singleton or prototype.
                     Object advice;
                     if ( this .singleton || this .beanFactory.isSingleton(name)) {
                         // Add the real Advisor/Advice to the chain.
                         advice = this .beanFactory.getBean(name);
                     }
                     else {
                         // It's a prototype Advice or Advisor: replace with a prototype.
                         // Avoid unnecessary creation of prototype bean just for advisor chain initialization.
                         advice = new PrototypePlaceholderAdvisor(name);
                     }
                     addAdvisorOnChainCreation(advice, name);
                 }
             }
         }
 
         this .advisorChainInitialized = true ;
     }

this.advisorChainInitialized:标示是否已进行过初始化,若以初始化则不再进行初始化。然后就是将interceptorNames转化成Advisor。根据interceptorNames所包含的字符串到容器中进行查找,如果含有*则,则表示进行一定的匹配,符合的都会纳入。如官方文档中说的:  
?
1
2
3
4
5
6
7
8
9
10
11
<bean id= "proxy" class = "org.springframework.aop.framework.ProxyFactoryBean" >
     <property name= "target" ref= "service" />
     <property name= "interceptorNames" >
         <list>
             <value>global*</value>
         </list>
     </property>
</bean>
 
<bean id= "global_debug" class = "org.springframework.aop.interceptor.DebugInterceptor" />
<bean id= "global_performance" class = "org.springframework.aop.interceptor.PerformanceMonitorInterceptor" />

这中间页经过了Advice到Advisor的转换,如下:  
?
1
2
3
4
5
6
7
8
9
private void addAdvisorOnChainCreation(Object next, String name) {
         // We need to convert to an Advisor if necessary so that our source reference
         // matches what we find from superclass interceptors.
         Advisor advisor = namedBeanToAdvisor(next);
         if (logger.isTraceEnabled()) {
             logger.trace( "Adding advisor with name '" + name + "'" );
         }
         addAdvisor(advisor);
     }

?
1
2
3
4
5
6
private Advisor namedBeanToAdvisor(Object next) {
         try {
             return this .advisorAdapterRegistry.wrap(next);
         }
         }
     }

?
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);
     }

这个包裹过程已经见过很多遍了,采用了适配器的模式。 
之后又是和其他的AOP方式接轨了,设置一些列要实现的接口和参数,使用DefaultAopProxyFactory先创建出AopProxy,要么是JdkDynamicAopProxy,要么是CglibAopProxy,然后就可以调用AopProxy的getProxy方法来获取代理对象了。这个过程详见上一篇博客http://lgbolgger.iteye.com/blog/2119810。 

这种方式实现的AOP还是比较麻烦的,同时配置一个ProxyFactoryBean仅能实现对一个目标对象的拦截,要想拦截多个目标对象,需要配置多个ProxyFactoryBean。所以大部分还是使用Spring引进的aspectj的AOP方式来进行AOP编程。 

相关文章
|
26天前
|
监控 Java API
掌握 Spring Boot AOP:使用教程
Spring Boot 中的面向切面编程(AOP)为软件开发提供了一种创新方法,允许开发者将横切关注点与业务逻辑相分离。这不仅提高了代码的复用性和可维护性,而且还降低了程序内部组件之间的耦合度。下面,我们深入探讨如何在 Spring Boot 应用程序中实践 AOP,以及它为项目带来的种种益处。
|
1月前
|
安全 Java Spring
Spring之Aop的底层原理
Spring之Aop的底层原理
|
1月前
|
Java 关系型数据库 MySQL
利用Spring AOP技术实现一个读写分离
利用Spring AOP技术实现一个读写分离
34 0
|
4天前
|
Java 开发者 Spring
Spring AOP的切点是通过使用AspectJ的切点表达式语言来定义的。
【5月更文挑战第1天】Spring AOP的切点是通过使用AspectJ的切点表达式语言来定义的。
15 5
|
4天前
|
XML Java 数据格式
Spring AOP
【5月更文挑战第1天】Spring AOP
21 5
|
5天前
|
Java 编译器 开发者
Spring的AOP理解
Spring的AOP理解
|
5天前
|
XML Java 数据格式
如何在Spring AOP中定义和应用通知?
【4月更文挑战第30天】如何在Spring AOP中定义和应用通知?
11 0
|
5天前
|
安全 Java 开发者
在Spring框架中,IoC和AOP是如何实现的?
【4月更文挑战第30天】在Spring框架中,IoC和AOP是如何实现的?
17 0
|
5天前
|
Java 测试技术 开发者
【亮剑】如何通过自定义注解来实现 Spring AOP,以便更加灵活地控制方法的拦截和增强?
【4月更文挑战第30天】通过自定义注解实现Spring AOP,可以更灵活地控制方法拦截和增强。首先定义自定义注解,如`@MyCustomAnnotation`,然后创建切面类`MyCustomAspect`,使用`@Pointcut`和`@Before/@After`定义切点及通知。配置AOP代理,添加`@EnableAspectJAutoProxy`到配置类。最后,在需拦截的方法上应用自定义注解。遵循保持注解职责单一、选择合适保留策略等最佳实践,提高代码可重用性和可维护性。记得测试AOP逻辑。
|
6天前
|
缓存 监控 Java
【Spring系列笔记】AOP
面向切面编程就是面向特定方法编程。通过将横切关注点(cross-cutting concerns)从主要业务逻辑中分离出来,提供一种更好的代码模块化和可维护性。 横切关注点指的是在应用程序中横跨多个模块或层的功能,例如日志记录、事务管理、安全性、缓存、异常处理等。
18 0