`
anson_xu
  • 浏览: 514704 次
  • 性别: Icon_minigender_1
  • 来自: 惠州
社区版块
存档分类

Spring2.0简明手册(系列之三 AOP-1.x)aop cglib

阅读更多
Spring2.0简明手册(系列之三 AOP-1.x)

--------------------------------------------------------------------------------

2008-06-19 22:58:08 标签:Spring AOP   [推送到技术圈]


版权声明:原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://robert.blog.51cto.com/374512/83040
一、基本概念:
        (1)目标对象(target)
        就是被代理的对象,也就是具体的业务逻辑。比如OrderService
        (2)切面  (Aspect)
        交叉业务,也就是通用的业务逻辑,比如日志、事务。
        (3)连接点 (Jointpoint)
        切面可以插入的地点,主要有方法、属性
        (4)切入点 (Pointcut)
        指定哪些连接点可以应用切面/通知
        (5)通知(Advice)
        切面的具体实现
二、基本使用
1.代理
1)ProxyFactoryBean
    Spring的AOP实现是基于代理的,而在Spring里创建一个AOP代理的基本方法就是
    使用org.springframework.aop.framework.ProxyFactoryBean 
    ProxyFactoryBean的各个属性,参照Spring参考手册:7.5.2. JavaBean属性 
    这里的配置重点是目标与拦截器。 
    实际创建的代理类型,有两种:代理接口和代理类。 
    更详细的解释需要参照Spring参考手册:7.5.3. 基于JDK和CGLIB的代理。 
    下面我们看一下具体的配置。

(1)代理接口(JDK动态代理):
    <bean id="person"
     class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 需要被代理的接口 -->
     <property name="proxyInterfaces">
         <value>com.mycompany.Person</value>
     </property>
        <!-- 目标对象 -->
     <property name="target">
         <ref local="personTarget"/>
     </property>
        <!-- 需要被应用的通知或拦截器,这里的顺序是很重要的 -->
     <property name="interceptorNames">
         <list>
             <value>myAdvisor</value>
             <value>debugInterceptor</value>
         </list>
     </property>
    </bean>


目标对象也可以用匿名内部bean的方式使用:
    <bean id="person" class="org.springframework.aop.framework.ProxyFactor Bean">
   <property name="proxyInterfaces"><value>com.mycompany.Person</value></property>
   <!-- Use inner bean, not local reference to target -->
   <property name="target">
     <bean class="com.mycompany.PersonImpl">
       <property name="name"><value>Tony</value></property>
       <property name="age"><value>51</value></property>
     </bean>
   </property>
   <property name="interceptorNames">
     <list>
       <value>myAdvisor</value>
       <value>debugInterceptor</value>
     </list>
   </property>
    </bean>
(2)代理类(CGLIB代理):

目标类不存在接口,只能使用CGLIB代理,
去掉proxyInterfaces属性
设置proxyTargetClass属性设为true或者忽略这个属性
目标类即使存在接口,也强制使用CGLIB代理:
设置proxyTargetClass属性设为true,甚至proxyInterfaces属性被设置的情况下仍然将实际使用基于CGLIB的代理。


“全局”advisor
通过在一个拦截器名后添加一个星号,所有bean名字与星号之前部分相匹配的Advice都将被加入到advisor链中。
    <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
   <property name="target" ref="service"/>
   <property name="interceptorNames">
     <list>
       <value>globa *</value>
     </list>
   </property>
    </bean>
    <bean id="global_debug" class="org.springframework.aop.interceptor.DebugInterceptor"/>
    <bean id="global_performance" class="org.springframework.aop.interceptor.PerformanceMonitorInterceptor"/>


2)TransactionProxyFactoryBean
使用TransactionProxyFactoryBean时,我们通常都会用简化代理的方式进行配置。
你也许需要许多相似的代理定义,特别是定义事务性代理的时候。使用父子bean定义,以及内部bean定义,可以让代理定义大大得到极大的简化。
请参照Spring参考手册:7.6. 简化代理定义


2.Advisor
预先了解的概念:Advisor=Advice+Pointcut,所以这里的配置重点是如何把Advice和Pointcut集成到一起。
除了Introduction Advice(和DefaultIntroductionAdvisor一起使用),任何advisor都可以和任何Advice一起工作。
    具体的配置示例如下:
1)NameMatchMethodPointcutAdvisor(内部使用NameMatchMethodPointcut)
      <bean id="helloAdvisor"
            class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
         <property name="mappedName" value="*Newbie"/>
         <property name="advice" ref="logBeforeAdvice"/>
      </bean>


2)RegexpMethodPointcutAdvisor
在背后,如果使用J2SE 1.4或者以上版本,Spring将使用JdkRegexpMethodPointcut,在之前版本的虚拟机上,Spring将退回去使用Perl5RegexpMethodPointcut。
配置示例:
      <bean id="regExpAdvisor"
            class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
         <property name="pattern" value=".*Newbie"/>
         <property name="advice" ref="logBeforeAdvice"/>
      </bean>


3)DefaultPointcutAdvisor(使用外部注入的Pointcut)
  <bean id="defaultAdvisor"
     class="org.springframework.aop.support.DefaultPointcutAdvisor">
     <constructor-arg ref="adviceBean"/>
     <constructor-arg ref="poingcutBean"/>
    </bean>

  <bean id="defaultAdvisor"
     class="org.springframework.aop.support.DefaultPointcutAdvisor">
     <property name="advice" ref="adviceBean"></property>
     <property name="pointcut" ref="pointcutBean"></property>
  </bean>
3.Advice
    这里的重点是编程实现相应的接口。具体参照Spring参考手册,摘要如下:
Around Advice--实现MethodInterceptor接口--参照Spring参考手册:7.3.2.1. 拦截around通知
Before Advice --实现BeforeAdvice接口        --参照Spring参考手册:7.3.2.2. 前置通知
Throw Advice --实现ThrowsAdvice接口      --参照Spring参考手册:7.3.2.3. 异常通知
After Returning Advice--实现AfterReturningAdvice接口--参照Spring参考手册:7.3.2.4. 后置通知
Introduction Advice --实现IntroductionInterceptor接口,并使用DefaultIntroductionAdvisor织入Advice或者,继承DelegatingIntroductionInterceptor,并使用DefaultIntroductionAdvisor织入Advice。Spring 把引入通知(introduction advice)作为一种特殊的拦截通知进行处理。--参照Spring参考手册:7.3.2.5. 引入通知


4.切入点PointCut
    孤立的PointCut没有什么用处,必须结合Advisor一起使用,PointCut和DefaultPointcutAdvisor的结合使用,可以参照:http://www.easyjf.com/bbs.ejf?cmd=appShow&id=4554752
1)静态切入点
(1)正则表达式切入点
包括Perl5RegexpMethodPointcut和JdkRegexpMethodPointcut
两者都是StaticMethodMatcherPointcut的子类,不限制类,只限制方法
Perl5RegexpMethodPointcut依赖Jakarta ORO进行正则表达式匹配,需要把 jakarta-oro-xx.jar 文件放到 classpath 上。
JdkRegexpMethodPointcut需要在 JDK1.4 及以上的环境运行,不需要额外的库
他们均有2个属性:
(1) pattern或patterns:前者表示单个正则表达式,后置表示多个正则表达式,支持<list>配置;
(2) ExcludedPattern或ExcludedPatterns:前者表示排除某个字符串,后者表示排除一组字符串,支持<list>配置;
配置示例:
      <bean id="pointcutBean"
      class="org.springframework.aop.support. JdkRegexpMethodPointcut">
       <property name="pattern">
            <value>.*business.*</value>
         </property>
        <property name="ExcludedPattern">
            <value>business2</value>
         </property>
      </bean>

      <bean id="pointcutBean"
      class="org.springframework.aop.support. Perl5RegexpMethodPointcut">
       <property name="pattern">
            <value>.*business.*</value>
         </property>
        <property name="ExcludedPattern">
            <value>business2</value>
         </property>
      </bean>


(2)NameMatchMethodPointcut
StaticMethodMatcherPointcut的子类,不限制类,只限制方法
NameMatchMethodPointcut只有一个属性mappedName或者mappedNames,前者表示映射单个字符串,后者表示映射一组字符串,支持<list>配置
      <bean id="pointcutBean" class="org.springframework.aop.support.NameMatchMethodPointcut">
       <property name="mappedNames">
       <list>
        <value>business*</value>
         </list>
      </property>
      </bean>


(3)ExpressionPointcut接口
不是StaticMethodMatcherPointcut的子类,可以既限制类,又限制方法
在 Spring2 中,在 Pointcut 的基础上,引入了一个 ExpressionPointcut 接口用来通过切入点表达语言来描述切入点。
有了 ExpressionPointcut,我们可以使用下面更加简单的方式来描述切入点,
如 execution(* Component.business*(..))表示执行所有 Component 的业务方法(此处为 business 打头的方法)。
Spring2 提供了一个 ExpressionPointcut 的实现,即 AspectJExpressionPointcut,
该类的使用很简单,只需要做如下配置即可:
      <bean id="pointcutBean"
      class="org.springframework.aop.aspectj.AspectJExpressionPointcut">
      <property name="expression"
      value="execution(void spring.chapter3.proxy.Component.business*(..))">
      </property>
      </bean>


属性驱动的切入点
一个重要的静态切入点是元数据驱动(metadata-driven)切入点。这使用元数据参数:特别是源代码级别的元数据。
2)动态切入点
    控制流切入点(ControlFlowPointcut)--既限制类,又限制方法
    如果有这样的需求:我们对一个方法进行切入通知,但只有这个方法在一个特定方法中被调用的时候执行通知,我们可以使用ControlFlowPointCut流程切入点

            以实例来说明(参考:http://blog.csdn.net/daryl715/archive/2007/08/14/1743311.aspx)
            当BeanOne的方法被Test类的runfoo方法调用时,织入通知。
            BeanOne.java

package study.springAop;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

public class BeanOne {
    private Logger logger =
        Logger.getLogger(this.getClass().getName());
    public void foo(){
        logger.log(Level.INFO, "executing==>foo-one");
    }
    public void bar(){
        logger.log(Level.INFO, "executing==>bar-one");
    }
}


            SimpleAdvise.java
package study.springAop;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;

public class SimpleAdvise
        implements MethodInterceptor {
    private Logger logger =
        Logger.getLogger(this.getClass().getName());
    public Object invoke(MethodInvocation invocation) throws Throwable {
        logger.log(Level.INFO, "before>>>");
        Object retVal=invocation.proceed();
        logger.log(Level.INFO, "after<<<");
        return retVal;
    }
}




            Test.java
package study.springAop;


public class Test {

    BeanOne beanOne = null;
    public void setBeanOne(BeanOne beanOne) {
  this.beanOne = beanOne;
    }
    public void runfoo() {
        beanOne.foo();
        beanOne.bar();
    }
    public void runbar() {
        beanOne.bar();
        beanOne.foo();
    }
    public void runfoo(BeanOne pBeanOne) {
        pBeanOne.foo();
        pBeanOne.bar();
    }

    public void runbar(BeanOne pBeanOne) {
        pBeanOne.bar();
        pBeanOne.foo();
    }
}




            beans-config.xml
<?xml version="1.0" encoding="UTF-8">
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
 

    <bean id="myTest" class="study.springAop.Test">
        <property name="beanOne" ref="proxyOne"></property>
    </bean>
   
    <!--配置DefaultPointcutAdvisor-->
    <bean id="myFlowControlPointcut"
       class="org.springframework.aop.support.ControlFlowPointcut">
        <constructor-arg value="study.springAop.Test"/>
        <constructor-arg value="runfoo"/>
    </bean>
    <bean id="myAdvise" class="study.springAop.SimpleAdvise"/>
    <bean id="myAdvisor"
       class="org.springframework.aop.support.DefaultPointcutAdvisor">
        <property name="advice" ref="myAdvise"/>
        <property name="pointcut" ref="myFlowControlPointcut"/>
    </bean>
    <!--配置代理-->
    <bean id="beanOne" class="study.springAop.BeanOne"/>
    <bean id="proxyOne"
          class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 目标类没有实现任何接口,一个基于CGLIB的代理将被创建 -->
        <property name="target" ref="beanOne"/>
        <property name="interceptorNames">
            <list>
                <value>myAdvisor</value>
            </list>
        </property>
    </bean>

</beans>




            log4j.properties
log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] %-13c{1}:%L - %m%n


            SpringAOPDemo.java
package study.springAop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.
              support.ClassPathXmlApplicationContext;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
public class SpringAOPDemo {
    private static Logger logger =
        Logger.getLogger(SpringAOPDemo.class.getName());
    public static void main(String[] args) {
  ApplicationContext context = new ClassPathXmlApplicationContext(
    "beans-config.xml");

  Test myTest = (Test) context.getBean("myTest");
  BeanOne proxyOne = (BeanOne) context.getBean("proxyOne");

  logger.log(Level.INFO, "\n the flowing doesn't work...");
  // 直接调用
  logger.log(Level.INFO, "不通过Test类调用BeanOne的方法...");
  proxyOne.foo();
  proxyOne.bar();
  // 通过runbar方法调用
  logger.log(Level.INFO, "通过Test类的runbar(BeanOne pBeanOne)方法调用BeanOne的方法...");
  myTest.runbar(proxyOne);
  logger.log(Level.INFO, "通过Test类的runbar()方法调用BeanOne的方法...");
  myTest.runbar();
  logger.log(Level.INFO, "\n the flowing works...");
  // 通过runfoo方法调用
  logger.log(Level.INFO, "通过Test类的runfoo(BeanOne pBeanOne)方法调用BeanOne的方法...");
  myTest.runfoo(proxyOne);
  logger.log(Level.INFO, "通过Test类的runfoo()方法调用BeanOne的方法...");
  myTest.runfoo();
    }
}


执行结果:
INFO [main] SpringAOPDemo:18 -
the flowing doesn't work...
INFO [main] SpringAOPDemo:20 - 不通过Test类调用BeanOne的方法...
INFO [main] BeanOne      :10 - executing==>foo-one
INFO [main] BeanOne      :13 - executing==>bar-one
INFO [main] SpringAOPDemo:24 - 通过Test类的runbar(BeanOne pBeanOne)方法调用BeanOne的方法...
INFO [main] BeanOne      :13 - executing==>bar-one
INFO [main] BeanOne      :10 - executing==>foo-one
INFO [main] SpringAOPDemo:26 - 通过Test类的runbar()方法调用BeanOne的方法...
INFO [main] BeanOne      :13 - executing==>bar-one
INFO [main] BeanOne      :10 - executing==>foo-one
INFO [main] SpringAOPDemo:28 -
the flowing works...
INFO [main] SpringAOPDemo:30 - 通过Test类的runfoo(BeanOne pBeanOne)方法调用BeanOne的方法...
INFO [main] SimpleAdvise :13 - before>>>
INFO [main] BeanOne      :10 - executing==>foo-one
INFO [main] SimpleAdvise :15 - after<<<
INFO [main] SimpleAdvise :13 - before>>>
INFO [main] BeanOne      :13 - executing==>bar-one
INFO [main] SimpleAdvise :15 - after<<<
INFO [main] SpringAOPDemo:32 - 通过Test类的runfoo()方法调用BeanOne的方法...
INFO [main] SimpleAdvise :13 - before>>>
INFO [main] BeanOne      :10 - executing==>foo-one
INFO [main] SimpleAdvise :15 - after<<<
INFO [main] SimpleAdvise :13 - before>>>
INFO [main] BeanOne      :13 - executing==>bar-one
INFO [main] SimpleAdvise :15 - after<<<


3)AspectJ切入点表达式语言
从2.0开始,Spring中使用的最重要的切入点类型是org.springframework.aop.aspectj.AspectJExpressionPointcut。而且,使用AspectJ切入点表达式通常会更简单一些。格式:execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
更详细的例子参考:6.2.3.4. 示例
三、高级使用
1.自动代理
    推荐一篇文章: http://blog.csdn.net/gabriel80/archive/2008/05/13/2441814.aspx
1)BeanNameAutoProxyCreator
为名字匹配字符串或者通配符的bean自动创建AOP代理。
主要目的是把相同的配置一致地应用到多个对象,并且使用最少量的配置。
  <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
    <property name="beanNames"><value>jdk*,onlyJdk</value></property>
    <property name="interceptorNames">
      <list>
        <value>myInterceptor</value>
      </list>
    </property>
  </bean>


2)DefaultAdvisorAutoProxyCreator

这个类的奇妙之处在于它实现了 BeanPostProcessor 接口。当 ApplicationContext 读入所有 Bean 的配置信息后,
        DefaultAdvisorAutoProxyCreator 将扫描上下文,寻找所有的 Advisor 。
        它将这些 Advisor 应用到所有符合 Advisor 切入点的 Bean 中。这个代理创建器只能与 Advisor 配合使用 。
通常自动代理的好处是它让调用者或者被依赖对象不能得到一个没有通知过的对象。

        在下面例子中,在ApplicationContext上调用getBean("businessObject1")将返回一个AOP代理,而不是目标业务对象。
  <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

  <bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">
    <property name="transactionInterceptor" ref="transactionInterceptor"/>
  </bean>
  <bean id="customAdvisor" class="com.mycompany.MyAdvisor"/>

  <bean id="businessObject1" class="com.mycompany.BusinessObject1">
    <!-- Properties omitted -->
  </bean>
  <bean id="businessObject2" class="com.mycompany.BusinessObject2"/>


DefaultAdvisorAutoProxyCreator支持过滤(通过使用一个命名约定让只有特定的advisor被评估,允许在同一个工厂里使用多个不同配置的AdvisorAutoProxyCreator)和排序。
关于过滤的说明:
        It's possible to filter out advisors - for example, to use multiple post processors of this type in the same factory - by setting the usePrefix property to true, in which case only advisors beginning with the DefaultAdvisorAutoProxyCreator's bean name followed by a dot (like "aapc.") will be used. This default prefix can be changed from the bean name by setting the advisorBeanNamePrefix property. The separator (.) will also be used in this case.



3)AbstractAdvisorAutoProxyCreator

如果在某些情况下框架提供的DefaultAdvisorAutoProxyCreator不能满足你的需要,你可以通过继承AbstractAdvisorAutoProxyCreator这个类(DefaultAdvisorAutoProxyCreator的父类)来创建你自己的自动代理创建器。
4)使用元数据驱动的自动代理
参照:JPetStore示例应用程序的/attributes 目录
2.使用TargetSources
ProxyFactoryBean的targetSource属性,缺省的实现是对于每次调用代理将返回相同的目标。
如果我们想对目标实现 池化(pooling),热切换(hot swappable)等功能时,就需要配置ProxyFactoryBean的targetSource属性了。
  <bean id="businessObject" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="targetSource" ref="配置为适当的TargetSource"/>
    <property name="interceptorNames" value="myInterceptor"/>
  </bean>


如果我们想热切换一个AOP代理的目标--配置为HotSwappableTargetSource
  <bean id="initialTarget" class="mycompany.OldTarget"/>
  <bean id="swapper" class="org.springframework.aop.target.HotSwappableTargetSource">
    <constructor-arg ref="initialTarget"/>
  </bean>


如果我们想池化一个AOP代理的目标--配置为CommonsPoolTargetSource
  <bean id="businessObjectTarget" class="com.mycompany.MyBusinessObject" scope="prototype">
    ... properties omitted
  </bean>
  <bean id="poolTargetSource" class="org.springframework.aop.target.CommonsPoolTargetSource">
    <property name="targetBeanName" value="businessObjectTarget"/>
    <property name="maxSize" value="25"/>
  </bean>


如果我们想为每个进来的请求(即每个线程)创建一个对象--配置为ThreadLocalTargetSource
  <bean id="threadlocalTargetSource" class="org.springframework.aop.target.ThreadLocalTargetSource">
    <property name="targetBeanName" value="businessObjectTarget"/>
  </bean>


    具体实例,可以参照:http://blog.csdn.net/peirenlei/archive/2007/04/12/1562087.aspx
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics