Spring AOP的几种实现方式

 说道AOP不得不提到几个概念:

  切面:也就是我们自己的一些业务方法。

  通知:用于拦截时出发的操作。

  切点:具体拦截的某个业务点。

  这样说可能还是有点抽象,举个例子,下面是一个纸糊的多面体。

  每个面都是一个业务方法,我们通过刺穿每一个面,都可以进入到内部,这个面就是一个切面

  刺穿的时候会发出声响,这就是一种通知

  而具体从哪个面刺入,这就是一个切入点的选择了。

  这样说,应该能稍微了解一点。

 

  那么下面看一个简单的例子:

  为了便于理清关系,先放上一张相关的类图:

  首先定义个接口

1 public interface IService {
2  public void withAop();
3  public void withoutAop();
4 }

  有了接口,当然需要一个实现类

复制代码
 1 public class TestAOP implements IService {
 2  private String name;
 3  public void withAop() {
 4   System.out.println("with AOP name:"+name);
 5  }
 6  public void withoutAop() {
 7   System.out.println("without AOP name:"+name);
 8  }
 9  public String getName() {
10   return name;
11  }
12  public void setName(String name) {
13   this.name = name;
14  }
15 }
复制代码

  这个实现类实现了接口定义的两个方法,下面我们定义几种拦截方式,这些拦截方式通过拦截的位置或者时机不同而不同。

  通常有方法前拦截方法后拦截,以及异常拦截。通过在这些拦截中编写自己的业务处理,可以达到特定的需求。

  方法前拦截,需要实现MethodBeforeAdvice接口,并填写before方法。这样,当拦截到某个方法时,就会在方法执行前执行这个before()方法。

复制代码
1 public class BeforeAOPInterceptor implements MethodBeforeAdvice{
2  public void before(Method method, Object[] args, Object instance)
3    throws Throwable {
4   System.out.println("before()"+method.getName());
5  }
6 }
复制代码

  同理,方法后拦截,也是如此。需要实现AfterReturningAdvice接口。

复制代码
1 public class AfterAOPInterceptor implements AfterReturningAdvice{
2  public void afterReturning(Object value, Method method, Object[] args,
3    Object instance) throws Throwable {
4   System.out.println("after()"+method.getName());
5  }
6 }
复制代码

  以及异常拦截。

复制代码
1 public class ThrowsAOPInterceptor implements ThrowsAdvice{ 
2  public void afterThrowing(Method method,Object[] args,Object instance,AccountException ex) throws Throwable{
3   System.out.println("after()"+method.getName()+"throws exception:"+ex);
4  }
5  public void afterThrowing(NullPointerException ex) throws Throwable{
6   System.out.println("throws exception:"+ex);
7  }
8 }
复制代码

  接下来就需要配置一下spring的配置文件,把拦截器与切面方法关联起来。

  参考上面的图,可以看到配置文件中的层次关系。

复制代码
 1 <?xml version="1.0" encoding="UTF-8"?> 
 2 <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 3 xmlns="http://www.springframework.org/schema/beans" 
 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 
 5 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> 
 6 <!-- 通过名字匹配 --> 
 7 <!-- 
 8   <bean id="before" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> 
 9     <property name="advice"> 
10       <bean class="com.test.pointcut.beforeAOP"></bean> 
11     </property> 
12     <property name="mappedName" value="withoutAop"></property> 
13   </bean> 
14 --> 
15 <!-- 通过正则表达式 匹配 --> 
16   <bean id="before" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> 
17     <property name="advice"> 
18       <bean class="com.test.pointcut.BeforeAOPInterceptor"></bean> 
19     </property> 
20   <property name="patterns"> 
21     <list> 
22       <value>.*out.*</value> 
23     </list> 
24   </property> 
25   </bean> 
26   <bean id="after" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> 
27     <property name="advice"> 
28       <bean class="com.test.pointcut.AfterAOPInterceptor"></bean> 
29     </property> 
30     <property name="patterns"> 
31       <list> 
32         <value>.*out.*</value> 
33       </list> 
34     </property> 
35   </bean> 
36   <bean id="exception" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> 
37     <property name="advice"> 
38       <bean class="com.test.pointcut.ThrowsAOPInterceptor"></bean> 
39     </property> 
40     <property name="patterns"> 
41       <list> 
42         <value>.*out.*</value> 
43       </list> 
44     </property> 
45   </bean> 
46 <!-- --> 
47   <bean id="aopService" class="org.springframework.aop.framework.ProxyFactoryBean"> 
48     <property name="interceptorNames"> 
49       <list> 
50         <value>before</value> 
51         <value>after</value> 
52         <value>exception</value> 
53       </list> 
54     </property> 
55     <property name="target"> 
56       <bean class="com.test.pointcut.TestAOP"> 
57         <property name="name" value="Hello"></property> 
58       </bean> 
59     </property> 
60   </bean> 
61 </beans>
复制代码

  ProxyFactoryBean下有两个属性,一个想要拦截的目标类,一个是拦截器。而拦截器又包括两种,主要是因为定位方法的不同而分类。分别是:

  RegexpMethodPointcutAdvisor 通过正则表达式来定位业务方法。

  NameMatchMethodPointcutAdvisor 通过名字来定位业务方法。

  定位到了业务方法,还需要添加响应的拦截器,拦截器就是上面的三种。

  最后看一下测试的方法:

复制代码
public class TestMain {
 public static void main(String[] args) {
  XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContextAOP.xml"));
  IService hello = (IService)factory.getBean("aopService");
  hello.withAop();
  hello.withoutAop();
 }
}
复制代码

  我们上面通过正则表达式定位到所有包含out的方法,其实就是withoutAOP方法。这样当执行withoutAop方法时,会触发拦截器的操作。

  执行结果:

复制代码
2014-12-4 16:46:58 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [applicationContextAOP.xml]
with AOP name:Hello
before()withoutAop
without AOP name:Hello
after()withoutAop
复制代码

  总结:

  这是通过定义切入点的方式来实现AOP,通过这种编程方式,可以针对业务方法进行包装或者监控

  举个例子,比如有个业务方法想要进行数据的查询,那么可以再这个查询前面获取JDBC连接池的连接,这样就对用户屏蔽掉了复杂的申请过程。而销毁就可以放在方法后拦截函数里。

  再比如,想要监控某个业务方法呗执行了多少次,那么就可以通过这样一种拦截方式,进行信息的统计,计数或者计时!

  妙处多多,还待完善!




在Spring中AOP有几种配置方式,根据我对spring源码的浏览,发现几种实现方式原理如下:

1. ProxyFactoryBean

   

   

Java代码   收藏代码
  1. <bean name="myController" class="org.springframework.aop.framework.ProxyFactoryBean">  
  2.     <property name="interceptorNames">  
  3.         <list>  
  4.             <value>pointcut.advisor2</value>  
  5.             <value>pointcut.advisor1</value>  
  6.             <value>myRawController</value>  
  7.         </list>  
  8.     </property>  
  9. </bean>  

 

 

    这个属于最费力不讨好类型的,配置起来很麻烦。原理是根据spring的获取bean的方式,继承了FactoryBean接口的bean在取bean的时候会调用对应的bean class的getObject方法。下面是ProxyFactoryBean的getObject方法:

 

Java代码   收藏代码
  1.  public Object getObject()  
  2.         throws BeansException  
  3.     {  
  4.         initializeAdvisorChain();  
  5.         if(isSingleton())  
  6.             return getSingletonInstance();  
  7.         if(targetName == null)  
  8.             logger.warn("Using non-singleton proxies with singleton targets is often undesirable. Enable prototype proxies by setting the 'targetName' property.");  
  9.         return newPrototypeInstance();  
  10.     }  
  11.   
  12. private synchronized Object newPrototypeInstance()  
  13.     {  
  14.         if(logger.isTraceEnabled())  
  15.             logger.trace((new StringBuilder("Creating copy of prototype ProxyFactoryBean config: ")).append(this).toString());  
  16.         ProxyCreatorSupport copy = new ProxyCreatorSupport(getAopProxyFactory());  
  17.         TargetSource targetSource = freshTargetSource();  
  18.         copy.copyConfigurationFrom(this, targetSource, freshAdvisorChain());  
  19.         if(autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass())  
  20.             copy.setInterfaces(ClassUtils.getAllInterfacesForClass(targetSource.getTargetClass(), proxyClassLoader));  
  21.         copy.setFrozen(freezeProxy);  
  22.         if(logger.isTraceEnabled())  
  23.             logger.trace((new StringBuilder("Using ProxyCreatorSupport copy: ")).append(copy).toString());  
  24.         return getProxy(copy.createAopProxy());  
  25.     }  

 

   于是,通过这种方式实现了bean的代理。不过这种方式的缺点也是显而易见的,那就是配置起来相当麻烦。

 

2.  BeanNameAutoProxyCreator  

 

 

    配置方式如下:

  

Java代码   收藏代码
  1. <bean id="userService" class="com.aop.service.UserService"/>  
  2. <bean id="beforeAdvice" class="com.aop.advice.BeforeAdvice"/>  
  3. <bean id="xxxxxx" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">  
  4.         <property value="beanNames">  
  5.                <list>  
  6.                       <value>*service</value>  
  7.                </list>  
  8.         </property>  
  9.         <property value="interceptorNames">  
  10.                <value>beforeAdvice</value>  
  11.         </property>  
  12. </bean>  

 

  

     这个类实现了BeanPostProcessor接口的子接口:SmartInstantiationAwareBeanPostProcessor,

     每个被这个类care的类在取得bean实例前,会调用以下方法:

    

Java代码   收藏代码
  1. public Object postProcessBeforeInstantiation(Class beanClass, String beanName)  
  2.         throws BeansException  
  3.     {  
  4.         Object cacheKey = getCacheKey(beanClass, beanName);  
  5.         if(!targetSourcedBeans.contains(cacheKey))  
  6.         {  
  7.             if(advisedBeans.contains(cacheKey) || nonAdvisedBeans.contains(cacheKey))  
  8.                 return null;  
  9.             if(isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName))  
  10.             {  
  11.                 nonAdvisedBeans.add(cacheKey);  
  12.                 return null;  
  13.             }  
  14.         }  
  15.         TargetSource targetSource = getCustomTargetSource(beanClass, beanName);  
  16.         if(targetSource != null)  
  17.         {  
  18.             targetSourcedBeans.add(beanName);  
  19.             Object specificInterceptors[] = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);  
  20.             Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);  
  21.             proxyTypes.put(cacheKey, proxy.getClass());  
  22.             return proxy;  
  23.         } else  
  24.         {  
  25.             return null;  
  26.         }  
  27.     }  

  

3.  <aop:config>标签

     通过aop namespace下的一个标签aop:config来实现aop代理,这个也是用起来相当方便的一种配置方式

    

Java代码   收藏代码
  1.  <bean id="fooService" class="DefaultFooService"/>  
  2.   
  3. <!-- this is the actual advice itself -->  
  4.   
  5. <bean id="profiler" class="SimpleProfiler"/>  
  6.   
  7.  <aop:config>  
  8.   
  9. <aop:aspect ref="profiler">  
  10.   
  11.  <aop:pointcut id="aopafterMethod"  
  12.   
  13. expression="execution(* FooService.*(..))"/>  
  14.   
  15. <aop:after pointcut-ref="aopafterMethod"  
  16.   
  17. method="afterMethod"/>  
  18.   
  19.   
  20. <aop:pointcut id="aopBefore"  
  21.   
  22. expression="execution(* FooService.getBefore(String)) and args(myName)"/>  
  23.   
  24. <aop:before pointcut-ref="aopBefore"  
  25.   
  26. method="beforeMethod"/>  
  27.   
  28. </aop:aspect>  
  29.   
  30. </aop:config>  

 

   配置很简短,功能很全面。

   这种配置方式的原理则是在进行配置文件解析的时候,由AopNameSpaceHandler对此标签进行解析,然后

   注册一个“org.springframework.aop.config.internalAutoProxyCreator” bean,这个bean的实现类是:

   org/springframework/aop/aspectj/autoproxy/AspectJAwareAdvisorAutoProxyCreator,此类也实现了

   BeanPostProcessor接口。

 

至此,把大致原理分析了一下。当然,分析的不是很详细,有兴趣的朋友可以跟我联系大家一起交流一下。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值