`

Spring的AOP

    博客分类:
  • SSH
阅读更多

6.2  Spring的AOP

AOP(Aspect Orient Programming),也就是面向切面编程,作为面向对象编程的一种补充。问世的时间并不太长,甚至在国内的翻译还不太统一(有些书翻译成面向方面编程),但它确实极好地补充了面向对象编程的方式。面向对象编程将程序分解成各个层次的对象,而面向切面编程将程序运行过程分解成各个切面。
可以这样理解,面向对象编程是从静态角度考虑程序结构,面向切面编程是从动态角度考虑程序运行过程。
Spring AOP是Spring框架的一个重要组件,极好地补充了Spring IoC容器的功能。Spring AOP将Spring IoC容器与AOP组件紧密结合,丰富了IoC容器的功能。当然,即使不使用AOP组件,依然可以使用Spring的IoC容器。

6.2.1  AOP的基本概念

AOP从程序运行角度考虑程序的流程,提取业务处理过程的切面。AOP面向的是程序运行中各个步骤,希望以更好的方式来组合业务处理的各个步骤。
AOP框架并不与特定的代码耦合,AOP框架能处理程序执行中的特定点,而不是某个具体的程序。AOP框架具有如下两个特征:
  ● 各步骤之间的良好隔离性。
  ● 源代码无关性。
下面是关于面向切面编程的一些术语:
  ● 切面,业务流程运行的某个特定步骤,就是运行过程的关注点,关注点可能横切多个对象。
  ● 连接点,程序执行过程中明确的点,如方法的调用或异常的抛出。Spring AOP中,连接点总是方法的调用,Spring并没有显式地使用连接点。
  ● 处理(Advice),AOP框架在特定的连接点执行的动作。处理有around、before和throws等类型。大部分框架都以拦截器作为处理模型。
  ● 切入点,系列连接点的集合,它确定处理触发的时机。AOP框架允许开发者自己定义切入点,如使用正则表达式。
  ● 引入,添加方法或字段到被处理的类。Spring允许引入新的接口到任何被处理的对象。例如,可以使用一个引入,使任何对象实现IsModified接口,以此来简化缓存。
  ● 目标对象,包含连接点的对象。也称为被处理对象或被代理对象。
  ● AOP代理,AOP框架创建的对象,包含处理。简单地说,代理就是对目标对象的加强。Spring中的AOP代理可以是JDK动态代理,也可以是CGLIB代理。前者为实现接口的目标对象的代理,后者为不实现接口的目标对象的代理。
注意:面向切面编程是比较前沿的知识,而国内大部分翻译人士翻译计算机文献时,总是一边开着各种词典和翻译软件,一边逐词去看文献,不是先从总体上把握知识的架构。因此,难免导致一些术语的翻译词不达意,例如,Socket被翻译成“套接字”等。在面向切面编程的各术语翻译上,也存在较大的差异。对于Advice一词,有翻译为“通知”的,有翻译为“建议”的,如此种种,不一而足。实际上,Advice指AOP框架在特定切面所做的事情,故而笔者翻译为“处理”,希望可以表达Advice的真正含义。

6.2.2  AOP的代理

所谓AOP代理,就是AOP框架动态创建的对象,这个对象通常可以作为目标对象的替代品,而AOP代理提供比目标对象更加强大的功能。真实的情形是,当应用调用AOP代理的方法时,AOP代理会在自己的方法中回调目标对象的方法,从而完成应用的调用。
关于AOP代理的典型例子就是Spring中的事务代理Bean。通常,目标Bean的方法不是事务性的,而AOP代理包含目标Bean的全部方法,而且这些方法经过加强变成了事务性方法。简单地说,目标对象是蓝本,AOP代理是目标对象的加强,在目标对象的基础上,增加属性和方法,提供更强大的功能。
目标对象包含一系列切入点。切入点可以触发处理连接点集合。用户可以自己定义切入点,如使用正则表达式。AOP代理包装目标对象,在切入点处加入处理。在切入点加入的处理,使得目标对象的方法功能更强。
Spring默认使用JDK动态代理实现AOP代理,主要用于代理接口。也可以使用CGLIB代理。实现类的代理,而不是接口。如果业务对象没有实现接口,默认使用CGLIB代理。但面向接口编程是良好的习惯,尽量不要面向具体类编程。因此,业务对象通常应实现一个或多个接口。
下面是一个简单动态代理模式的示例,首先有一个Dog的接口,接口如下:

public interface Dog 
{ 
    //info方法声明 
    public void info(); 
    //run方法声明 
    public void run(); 
} 

 然后,给出该接口的实现类,实现类必须实现两个方法,源代码如下:

public class DogImpl implements Dog 
{ 
    //info方法实现,仅仅打印一个字符串 
    public void info() 
    { 
        System.out.println("我是一只猎狗"); 
    } 
    //run方法实现,仅仅打印一个字符串 
    public void run() 
    { 
        System.out.println("我奔跑迅速"); 
    } 
} 

 上面的代码没有丝毫独特之处,是典型的面向接口编程的模型,为了有更好的解耦,采用工厂来创建Dog实例。工厂源代码如下:

public class DogFactory 
{ 
    //工厂本身是单态模式,因此,将DogFactory作为静态成员变量保存 
    private static DogFactory df; 
    //将Dog实例缓存 
    private Dog gundog; 
    //默认的构造器,单态模式需要的构造器是private 
    private DogFactory() 
    { 
    } 
    //单态模式所需的静态方法,该方法是创建本类实例的唯一方法点 
    public static DogFactory instance() 
    { 
        if (df == null) 
        { 
              df = new DogFactory(); 
        } 
        return df; 
    } 
    //获得Dog实例 
    public Dog getDog(String dogName) 
    { 
        //根据字符串参数决定返回的实例 
        if (dogName.equals("gundog")) 
        { 
            //返回Dog实例之前,先判断缓存的Dog是否存在,如果不存在才创建, 
            否则直接返回缓存的Dog实例 
              if (gundog == null ) 
              { 
                  gundog = new DogImpl(); 
              } 
              return gundog; 
         } 
        return null; 
    } 
} 

 下面是一个通用的处理类,该处理类没有与任何特定的类耦合,它可以处理所有的目标对象。从JDK 1.3起,Java的import java.lang.reflect下增加InvocationHandler接口,该接口是所有处理类的根接口。
该类处理类的源代码如下:

public class ProxyHandler implements InvocationHandler 
{ 
    //需被代理的目标对象 
    private Object target; 
    //执行代理的目标方法时,该invoke方法会被自动调用 
    public Object invoke(Object proxy, Method method, Object[] args)throws 
    Exception 
    { 
        Object result = null; 
        if (method.getName().equals("info")) 
        { 
              System.out.println("======开始事务..."); 
              result =method.invoke(target, args); 
              System.out.println("======提交事务..."); 
        } 
        else 
        { 
              result =method.invoke(target, args); 
        } 
        return result; 
    } 
    //通过该方法,设置目标对象 
    public void setTarget(Object o) 
    { 
        this.target = o; 
    } 
} 

 该处理类实现InvocationHandler接口,实现该接口必须实现invoke(Object proxy, Method method, Object[] args)方法,程序调用代理的目标方法时,自动变成调用invoke方法。
该处理类并未与任何接口或类耦合,它完全是通用的,它的目标实例是Object类型,可以是任何的类型。
在invoke方法内,对目标对象的info方法进行简单加强,在开始执行目标对象的方法之前,先打印开始事务,执行目标对象的方法之后,打印提交事务。
通过method对象的invoke方法,可以完成目标对象的方法调用,执行代码如下:
result =method.invoke(target, args);
下面是代理工厂:

public class MyProxyFactory 
{ 
    /** 
      * 实例Service对象 
      * @param serviceName String 
      * @return Object 
      */ 
    public static Object getProxy(Object object) 
    { 
         //代理的处理类 
        ProxyHandler handler = new ProxyHandler(); 
         //把该dog实例托付给代理操作 
        handler.setTarget(object); 
        //第一个参数是用来创建动态代理的ClassLoader对象,只要该对象能访问Dog接口 
        即可 
        //第二个参数是接口数组,正是代理该接口数组 
        //第三个参数是代理包含的处理实例 
        return Proxy.newProxyInstance(DogImpl.class.getClassLoader(), 
            object.getClass().getInterfaces(),handler); 
    } 
} 

 代理工厂里有一行代码:
Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),handler);
Proxy.newProxyInstance()方法根据接口数组动态创建代理类实例,接口数组通过object.getClass().getInterfaces()方法获得,创建的代理类是JVM在内存中动态创建的,该类实现传入接口数组的全部接口。
因此,Dynamic Proxy要求被代理的必须是接口的实现类,否则无法为其构造相应的动态类。因此,Spring对接口实现类采用Dynamic Proxy实现AOP,而对没有实现任何接口的类,则通过CGLIB实现AOP代理。
下面是主程序:

public class TestDog 
{ 
    public static void main(String[] args) 
    { 
        Dog dog = null; 
        //创建Dog实例,该实例将作为被代理对象 
        Dog targetObject = DogFactory.instance().getDog("gundog"); 
        //以目标对象创建代理 
        Object proxy = MyProxyFactory.getProxy(targetObject); 
        if (proxy instanceof Dog) 
        { 
              dog = (Dog)proxy; 
        } 
        //测试代理的方法 
        dog.info(); 
        dog.run(); 
    } 
} 

 代理实例会实现目标对象实现的全部接口。因此,代理实例也实现了Dog接口,程序运行结果如下:
[java] ======开始事务...
[java] 我是一只猎狗
[java] ======提交事务...
[java] 我奔跑迅速
代理实例加强了目标对象的方法——仅仅打印了两行字符串。当然,此种加强没有实际意义。试想一下,若程序中打印字符串的地方,换成真实的事务开始和事务提交,则代理实例的方法为目标对象的方法增加了事务性。

6.2.3  创建AOP代理

通过前面的介绍,AOP代理就是由AOP框架动态生成的一个对象,该对象可作为目标对象使用,AOP代理包含了目标对象的全部方法。但AOP代理中的方法与目标对象的方法存在差异:AOP方法在特定切面插入处理,在处理之间回调目标对象的方法。
AOP代理所包含的方法与目标对象所包含的方法的示意图,如图6.1所示。



 
 

Spring中AOP代理由Spring的IoC容器负责生成和管理,其依赖关系也由IoC容器负责管理。因此,AOP代理能够引用容器中的其他Bean实例,这种引用由IoC容器的依赖注入提供。
Spring的AOP代理大都由ProxyFactoryBean工厂类产生,如图6.1所示,产生一个AOP代理至少有两个部分:目标对象和AOP框架所加入的处理。因此,配置ProxyFactoryBean时需要确定如下两个属性:
  ● 代理的目标对象。
  ● 处理(Advice)。
注意:关于Advice的更多知识,此处由于篇幅原因,无法深入讨论。实际上,Spring也提供了很多种Advice的实现,如需要更深入了解Spring AOP,请读者参考笔者所著的《Spring2.0宝典》。
所有代理工厂类的父类是org.springframework.aop.framework.ProxyConfig。因此,该类的属性是所有代理工厂的共同属性,这些属性也是非常关键的,包括:
  ● proxyTargetClass,确定是否代理目标类,如果需要代理目标是类,该属性设为true,此时需要使用CGLIB生成代理;如果代理目标是接口,该属性设为false,默认是false。
  ● optimize,确定是否使用强优化来创建代理。该属性仅对CGLIB代理有效;对JDK 动态代理无效。
  ● frozen,确定是否禁止改变处理,默认是false。
  ● exposeProxy,代理是否可以通过ThreadLocal访问,如果exposeProxy属性为true,则可通过AopContext.currentProxy()方法获得代理。
  ● aopProxyFactory,所使用的AopProxyFactory具体实现。该参数用来指定使用动态代理、CGLIB或其他代理策略。默认选择动态代理或CGLIB。一般不需要指定该属性,除非需要使用新的代理类型,才指定该属性。
配置ProxyFactoryBean工厂bean时,还需要指定它的特定属性,ProxyFactoryBean的特定属性如下所示:
  ● proxyInterfaces,接口名的字符串数组。如果没有确定该参数,默认使用CGLIB代理。
  ● interceptorNames,处理名的字符串数组。此处的次序很重要,排在前面的处理,优先被调用。此处的处理名,只能是当前工厂中处理的名称,而不能使用bean引用。处理名字支持使用通配符(*)。
  ● singleton,工厂是否返回单态代理。默认是true,无论 getObject()被调用多少次,将返回相同的代理实例。如果需要使用有状态的处理——例如,有状态的mixin,可改变默认设置,prototype处理。

6.2.4  代理接口

当目标Bean的实现类实现了接口后,Spring AOP可以为其创建JDK动态代理,而无须使用CGLIB创建的代理,这种代理称为代理接口。
创建AOP代理必须指定两个属性:目标Bean和处理。实际上,很多AOP框架都以拦截器作为处理。因为Spring AOP与IoC容器的良好整合,因此配置代理Bean时,完全可以利用依赖注入来管理目标Bean和拦截器Bean。
下面的示例演示了基于AOP的权限认证,它是简单的TestService接口,该接口模拟Service组件,该组件内包含两个方法:
  ● 查看数据。
  ● 修改数据。
接口的源代码如下:

//Service组件接口 
public interface TestService 
{ 
    //查看数据 
    void view(); 
    //修改数据 
    void modify(); 
} 

 该接口的实现类实现两个方法。因为篇幅限制,本示例并未显示出完整的查看数据和修改数据的持久层操作,仅仅在控制台打印两行信息。实际的项目实现中,两个方法的实现则改成对持久层组件的调用,这不会影响示例程序的效果。实现类的源代码如下:
TestService接口的实现类 

public class TestServiceImpl implements TestService 
{ 
    //实现接口必须实现的方法 
    public void view() 
    { 
        System.out.println("用户查看数据"); 
    } 
    //实现接口必须实现的方法 
    public void modify() 
    { 
        System.out.println("用户修改数据"); 
    } 
}

 
示例程序采用Around 处理作为拦截器,拦截器中使用依赖注入获得当前用户名。实际Web应用中,用户应该从session中读取。这不会影响示例代码的效果。拦截器源代码如下:

public class AuthorityInterceptor implements MethodInterceptor 
{ 
    //当前用户名 
    private String user; 
    //依赖注入所必需的setter方法 
    public void setUser(String user) 
    { 
        this.user = user; 
    } 
    public Object invoke(MethodInvocation invocation) throws Throwable 
    { 
        //获取当前拦截的方法名 
        String methodName = invocation.getMethod().getName(); 
        //下面执行权限检查 
        //对既不是管理员,也不是注册用户的情况 
        if (!user.equals("admin") && !user.equals("registedUser")) 
        { 
              System.out.println("您无权执行该方法"); 
              return null; 
        } 
        //对仅仅是注册用户,调用修改数据的情况 
        else if (user.equals("registedUser") && methodName.equals 
        ("modify")) 
        { 
              System.out.println("您不是管理员,无法修改数据"); 
              return null; 
        } 
        //对管理员或注册用户,查看数据的情况 
        else 
        { 
              return invocation.proceed(); 
        } 
    } 
} 

 TestAction类依赖TestService。因篇幅关系,此处不给出TestAction的接口的源代码,TestActionImpl的源代码如下:

public class TestActionImpl 
{ 
    //将TestService作为成员变量,面向接口编程 
    private TestService ts; 
    //依赖注入的setter方法 
    public void setTs(TestService ts) 
    { 
        this.ts = ts; 
    } 
    //修改数据 
    public void modify() 
    { 
        ts.modify(); 
    } 
    //查看数据 
    public void view() 
    { 
        ts.view(); 
    } 
} 

 配置文件如下:

<?xml version="1.0" encoding="GBK"?> 
<!-- 指定Spring配置文件的根元素,以及Spring配置文件的Schema信息 --> 
<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.xsd"> 
    <!-- 配置目标Bean --> 
    <bean id="serviceTarget" class="lee.TestServiceImpl"/> 
    <!-- 配置拦截器,拦截器作为处理使用 --> 
    <bean id="authorityInterceptor" class="lee.AuthorityInterceptor"> 
        <property name="user" value="admin"/> 
    </bean> 
    <!-- 配置代理工厂Bean,负责生成AOP代理 --> 
    <bean id="service" class="org.springframework.aop.framework. 
    ProxyFactoryBean"> 
        <!-- 指定AOP代理所实现的接口 --> 
        <property name="proxyInterfaces" value="lee.TestService"/> 
        <!-- 指定AOP代理所代理的目标Bean --> 
        <property name="target" ref="serviceTarget"/> 
        <!-- AOP代理所需要的拦截器列表 --> 
        <property name="interceptorNames"> 
            <list> 
                <value>authorityInterceptor</value> 
            </list> 
        </property> 
    </bean> 
    <!--  配置Action Bean,该Action依赖TestService Bean --> 
    <bean id="testAction" class="lee.TestActionImpl"> 
        <!-- 此处注入的是依赖代理Bean --> 
        <property name="ts" ref="service"/> 
    </bean> 
</beans> 

 主程序请求testAction Bean,然后调用该Bean的两个方法,主程序如下:

public class BeanTest 
{ 
    public static void main(String[] args)throws Exception 
    { 
        //创建Spring容器实例 
        ApplicationContext ctx = new FileSystemXmlApplicationContext 
        ("bean.xml"); 
        //获得TestAction bean 
        TestAction ta = (TestAction)ctx.getBean("testAction"); 
        //调用bean的两个测试方法 
        ta.view(); 
        ta.modify(); 
    } 
} 

 程序执行结果如下:
[java] 用户查看数据
[java] 用户修改数据
代理似乎没有发挥任何作用。因为配置文件中的当前用户是admin,admin用户具备访问和修改数据的权限,因此代理并未阻止访问。将配置文件中的admin修改成registed- User,再次执行程序,得到如下结果:
[java] 用户查看数据
[java] 您不是管理员,无法修改数据
代理阻止了registedUser修改数据,查看数据可以执行。将registedUser修改成其他用户,执行程序,看到如下结果:
[java] 您无权执行该方法
[java] 您无权执行该方法
代理阻止用户对两个方法的执行。基于AOP的权限检查,可以降低程序的代码量,因为无须每次调用方法之前,手动编写权限检查代码;同时,权限检查与业务逻辑分离,提高了程序的解耦。
示例中的目标Bean被暴露在容器中,可以被客户端代码直接访问。为了避免客户端代码直接访问目标Bean,可以将目标Bean定义成代理工厂的嵌套Bean,修改后的配置文件如下:

<?xml version="1.0" encoding="GBK"?> 
<!-- 指定Spring配置文件的根元素,以及Spring配置文件的Schema信息 --> 
<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.xsd"> 
    <!-- 配置拦截器,拦截器作为处理使用 --> 
    <bean id="authorityInterceptor" class="lee.AuthorityInterceptor"> 
        <property name="user" value="admin"/> 
    </bean> 
    <!-- 配置代理工厂Bean,该工厂Bean将负责创建目标Bean的代理 --> 
    <bean id="service" class="org.springframework.aop.framework. 
    ProxyFactoryBean"> 
        <!-- 指定AOP代理所实现的接口 --> 
        <property name="proxyInterfaces" value="lee.TestService"/> 
        <property name="target"> 
            <!-- 以嵌套Bean的形式定义目标Bean,避免客户端直接访问目标Bean --> 
            <bean class="lee.TestServiceImpl"/> 
        </property> 
        <!-- AOP代理所需要的拦截器列表 --> 
        <property name="interceptorNames"> 
            <list> 
                <value>authorityInterceptor</value> 
            </list> 
        </property> 
    </bean> 
    <!--  配置Action Bean,该Action依赖TestService Bean --> 
    <bean id="testAction" class="lee.TestActionImpl"> 
        <!-- 此处注入的是依赖代理Bean --> 
        <property name="ts" ref="service"/> 
    </bean> 
</beans> 

 由上面介绍的内容可见,Spring的AOP是对JDK动态代理模式的深化。通过Spring AOP组件,允许通过配置文件管理目标Bean和AOP所需的处理。
下面将继续介绍如何为没有实现接口的目标Bean创建CGLIB代理。

6.2.5  代理类

如果目标类没有实现接口,则无法创建JDK动态代理,只能创建CGLIB代理。如果需要没有实现接口的Bean实例生成代理,配置文件中应该修改如下两项:
  ● 去掉<property name="proxyInterfaces"/>声明。因为不再代理接口,因此,此处的配置没有意义。
  ● 增加<property name="proxyTargetClass">子元素,并设其值为true,通过该元素强制使用CGLIB代理,而不是JDK动态代理。
注意:最好面向接口编程,不要面向类编程。同时,即使在实现接口的情况下,也可强制使用CGLIB代理。
CGLIB代理在运行期间产生目标对象的子类,该子类通过装饰器设计模式加入到Advice中。因为CGLIB代理是目标对象的子类,则必须考虑保证如下两点:
  ● 目标类不能声明成final,因为final类不能被继承,无法生成代理。
  ● 目标方法也不能声明成final,final方法不能被重写,无法得到处理。
当然,为了需要使用CGLIB代理,应用中应添加CGLIB二进制Jar文件。

6.2.6  使用BeanNameAutoProxyCreator自动创建代理

这是一种自动创建事务代理的方式,一旦在容器中配置了BeanNameAutoProxyCreator实例,该实例将会对指定名字的Bean实例自动创建代理。实际上,BeanNameAutoProxyCreator是一个Bean后处理器,理论上它会对容器中所有的Bean进行处理,实际上它只对指定名字的Bean实例创建代理。
BeanNameAutoProxyCreator根据名字自动生成事务代理,名字匹配支持通配符。
与ProxyFactoryBean一样,BeanNameAutoProxyCreator需要一个interceptorNames属性,该属性名虽然是“拦截器”,但并不需要指定拦截器列表,它可以是Advisor或任何处理类型。
下面是使用BeanNameAutoProxyCreator的配置片段:

<!-- 定义事务拦截器bean --> 
<bean id="transactionInterceptor" 
    class="org.springframework.transaction.interceptor. 
    TransactionInterceptor"> 
    <!--  事务拦截器bean需要依赖注入一个事务管理器 --> 
    <property name="transactionManager" ref="transactionManager"/> 
    <property name="transactionAttributes"> 
        <!--  下面定义事务传播属性 --> 
        <props> 
            <prop >PROPAGATION_REQUIRED </prop> 
            <prop >PROPAGATION_REQUIRED,readOnly</prop> 
            <prop key="*">PROPAGATION_REQUIRED</prop> 
        </props> 
    </property> 
    </bean> 
<!-- 定义BeanNameAutoProxyCreator Bean,它是一个Bean后处理器, 
        负责为容器中特定的Bean创建AOP代理 --> 
<bean class="org.springframework.aop.framework.autoproxy. 
    BeanNameAutoProxyCreator"> 
    <!-- 指定对满足哪些bean name的bean自动生成业务代理 --> 
    <property name="beanNames"> 
        <list> 
            <!-- 下面是所有需要自动创建事务代理的Bean --> 
            <value>core-services-applicationControllerSevice</value> 
            <value>core-services-deviceService</value> 
            <value>core-services-authenticationService</value> 
            <value>core-services-packagingMessageHandler</value> 
            <value>core-services-sendEmail</value> 
            <value>core-services-userService</value> 
            <!-- 此处可增加其他需要自动创建事务代理的Bean --> 
        </list> 
    </property> 
    <!-- 下面定义BeanNameAutoProxyCreator所需的拦截器 --> 
    <property name="interceptorNames"> 
        <list> 
            <value>transactionInterceptor</value> 
              <!-- 此处可增加其他新的Interceptor --> 
        </list> 
    </property> 
</bean> 

 上面的片段是使用BeanNameAutoProxyCreator自动创建事务代理的片段。Transaction- Interceptor用来定义事务拦截器,定义事务拦截器时传入事务管理器Bean,也指定事务传播属性。
通过BeanNameAutoProxyCreator定义Bean后处理器,定义该Bean后处理器时,通过beanNames属性指定有哪些目标Bean生成事务代理;还需要指定“拦截器链”,该拦截器链可以由任何处理组成。
定义目标Bean还可使用通配符,使用通配符的配置片段如下所示:

<!-- 定义BeanNameAutoProxyCreator Bean,它是一个Bean后处理器, 
        负责为容器中特定的Bean创建AOP代理 --> 
<bean class="org.springframework.aop.framework.autoproxy. 
BeanNameAutoProxyCreator"> 
    <!-- 指定对满足哪些bean name的bean自动生成业务代理 --> 
    <property name="beanNames"> 
        <!-- 此处使用通配符确定目标bean --> 
        <value>*DAO,*Service,*Manager</value> 
    </property> 
    <!-- 下面定义BeanNameAutoProxyCreator所需的事务拦截器 --> 
    <property name="interceptorNames"> 
        <list> 
            <value>transactionInterceptor</value> 
              <!-- 此处可增加其他新的Interceptor --> 
        </list> 
    </property> 
</bean> 

 上面的配置片段中,所有名字以DAO、Service、Manager结尾的bean,将由该“bean后处理器”为其创建事务代理。目标bean不再存在,取而代之的是目标bean的事务代理。通过这种方式,不仅可以极大地降低配置文件的繁琐,而且可以避免客户端代码直接调用目标bean。
注意:虽然上面的配置片段是为目标对象自动生成事务代理。但这不是唯一的,如果有需要,可以为目标对象生成任何的代理。BeanNameAutoProxyCreator为目标对象生成怎样的代理,取决于传入怎样的处理Bean,如果传入事务拦截器,则生成事务代理Bean;否则将生成其他代理,在后面的实例部分,读者将可以看到大量使用BeanNameAutoProxy- Creator创建的权限检查代理。

6.2.7  使用DefaultAdvisorAutoProxyCreator自动创建代理

Spring还提供了另一个Bean后处理器,它也可为容器中的Bean自动创建代理。相比之下,DefaultAdvisorAutoProxyCreator是更通用、更强大的自动代理生成器。它将自动应用于当前容器中的advisor,不需要在DefaultAdvisorAutoProxyCreator定义中指定目标Bean的名字字符串。
这种定义方式有助于配置的一致性,避免在自动代理创建器中重复配置目标Bean 名。
使用该机制包括:
  ● 配置DefaultAdvisorAutoProxyCreator bean定义。
  ● 配置任何数目的Advisor,必须是Advisor,不仅仅是拦截器或其他处理。因为,必须使用切入点检查处理是否符合候选Bean定义。
DefaultAdvisorAutoProxyCreator计算Advisor包含的切入点,检查处理是否应该被应用到业务对象,这意味着任何数目的Advisor都可自动应用到业务对象。如果Advisor中没有切入点符合业务对象的方法,这个对象就不会被代理。如果增加了新的业务对象,只要它们符合切入点定义,DefaultAdvisorAutoProxyCreator将自动为其生成代理,无须额外   配置。
当有大量的业务对象需要采用相同处理,DefaultAdvisorAutoProxyCreator是非常有用的。一旦定义恰当,直接增加业务对象,而不需要额外的代理配置,系统自动为其增加     代理。

<beans> 
    <!-- 定义Hibernate局部事务管理器,可切换到JTA全局事务管理器 --> 
    <bean id="transactionManager" 
         class="org.springframework.orm.hibernate3. 
        HibernateTransactionManager"> 
        <!-- 定义事务管理器时,依赖注入SessionFactory --> 
         <property name="sessionFactory" ref bean="sessionFactory"/> 
    </bean> 
    <!-- 定义事务拦截器 --> 
    <bean id="transactionInterceptor" 
        class="org.springframework.transaction.interceptor. 
        TransactionInterceptor"> 
         <property name="transactionManager" ref="transactionManager"/> 
         <property name="transactionAttributeSource"> 
              <props> 
                  <prop key="*">PROPAGATION_REQUIRED</prop> 
                  <prop >PROPAGATION_REQUIRED,readOnly</prop> 
              </props> 
        </property> 
    </bean> 
    <!-- 定义DefaultAdvisorAutoProxyCreator  Bean,这是一个Bean后处理器 --> 
    <bean class="org.springframework.aop.framework.autoproxy. 
    DefaultAdvisorAutoProxyCreator"/> 
    <!-- 定义事务Advisor --> 
    <bean class="org.springframework.transaction.interceptor. 
    TransactionAttributeSourceAdvisor"> 
         <property name="transactionInterceptor" ref= 
        "transactionInterceptor"/> 
    </bean> 
    <!-- 定义额外的Advisor> 
    <bean id="customAdvisor" class="lee.MyAdvisor"/> 
</beans> 

 DefaultAdvisorAutoProxyCreator支持过滤和排序。如果需要排序,可让Advisor实现org.springframework.core.Ordered接口来确定顺序。TransactionAttributeSourceAdvisor已经实现Ordered接口,因此可配置其顺序,默认是不排序。
采用这样的方式,一样可以避免客户端代码直接访问目标Bean,而且配置更加简洁。只要目标Bean符合切入点检查,Bean后处理器自动为目标Bean创建代理,无须依次指定目标Bean名。
注意:Spring也支持以编程方式创建AOP代理,但这种方式将AOP代理所需要的目标对象和处理Bean等对象的耦合降低到代码层次,因此不推荐使用。

  • 大小: 7.1 KB
分享到:
评论

相关推荐

    spring aop jar 包

    Spring AOP(Aspect Oriented Programming,面向切面编程)是Spring框架的重要组成部分,它提供了一种在不修改源代码的情况下,对程序进行功能增强的技术。这个"spring aop jar 包"包含了实现这一功能所需的类和接口,...

    Spring AOP 16道面试题及答案.docx

    Spring AOP,全称为Aspect Oriented Programming,是面向切面编程的一种编程范式,它是对传统的面向对象编程(OOP)的一种补充。在OOP中,核心是对象,而在AOP中,核心则是切面。切面是关注点的模块化,即程序中的...

    简单spring aop 例子

    Spring AOP(面向切面编程)是Spring框架的重要组成部分,它提供了一种模块化和声明式的方式来处理系统中的交叉关注点问题,如日志、事务管理、安全性等。本示例将简要介绍如何在Spring应用中实现AOP,通过实际的...

    spring aop 自定义注解保存操作日志到mysql数据库 源码

    3、对spring aop认识模糊的,不清楚如何实现Java 自定义注解的 4、想看spring aop 注解实现记录系统日志并入库等 二、能学到什么 1、收获可用源码 2、能够清楚的知道如何用spring aop实现自定义注解以及注解的逻辑...

    死磕Spring之AOP篇 - Spring AOP两种代理对象的拦截处理(csdn)————程序.pdf

    Spring AOP 是一种面向切面编程的技术,它允许我们在不修改源代码的情况下,对应用程序的特定部分(如方法调用)进行增强。在 Spring 中,AOP 的实现主要依赖于代理模式,有两种代理方式:JDK 动态代理和 CGLIB 动态...

    Spring AOP完整例子

    Spring AOP(面向切面编程)是Spring框架的核心特性之一,它允许开发者在不修改源代码的情况下,通过插入切面来增强或改变程序的行为。在本教程中,我们将深入探讨Spring AOP的不同使用方法,包括定义切点、通知类型...

    Spring Aop四个依赖的Jar包

    Spring AOP,全称Aspect-Oriented Programming(面向切面编程),是Spring框架的一个重要模块,它通过提供声明式的方式来实现面向切面编程,从而简化了应用程序的开发和维护。在Spring AOP中,我们无需深入到每个...

    spring aop依赖jar包

    现在,我们回到主题——"springaop依赖的jar包"。在Spring 2.5.6版本中,使用Spring AOP通常需要以下核心jar包: - `spring-aop.jar`:这是Spring AOP的核心库,包含了AOP相关的类和接口。 - `spring-beans.jar`:...

    spring AOP 引入jar包,spring IOC 引入Jar包

    Spring AOP 和 Spring IOC 是 Spring 框架的两个核心组件,它们对于任何基于 Java 的企业级应用开发都至关重要。Spring AOP(面向切面编程)允许开发者在不修改源代码的情况下,通过“切面”来插入新的行为或增强已...

    反射实现 AOP 动态代理模式(Spring AOP 的实现原理)

    面向切面编程(AOP)是一种编程范式,旨在将横切关注点(如日志、安全等)与业务逻辑分离,从而提高模块化。...利用Java反射机制和Spring AOP框架,开发者可以方便地实现AOP,从而提升代码的模块化和可维护性。

    Spring AOP实现机制

    **Spring AOP 实现机制详解** Spring AOP(面向切面编程)是Spring框架的核心特性之一,它允许程序员在不修改源代码的情况下,通过“切面”来插入额外的业务逻辑,如日志、事务管理等。AOP的引入极大地提高了代码的...

    springAOP配置动态代理实现

    Spring AOP(面向切面编程)是Spring框架的重要组成部分,它允许程序员在不修改源代码的情况下,通过在运行时插入额外的行为(如日志记录、性能监控等)来增强对象的功能。动态代理则是Spring AOP实现的核心技术之一...

    Spring AOP面向方面编程原理:AOP概念

    ### Spring AOP面向方面编程原理:AOP概念详解 #### 一、引言 随着软件系统的日益复杂,传统的面向对象编程(OOP)逐渐暴露出难以应对某些横切关注点(cross-cutting concerns)的问题。为了解决这一挑战,面向方面编程...

    小马哥讲 Spring AOP 编程思想 - API 线索图.pdf

    在讨论Spring AOP(面向切面编程)时,首先需要理解几个核心概念。Spring AOP 是Spring框架提供的一个功能模块,它允许开发者将横切关注点(cross-cutting concerns)从业务逻辑中解耦出来,通过在方法调用前后进行...

    spring aop切面拦截指定类和方法实现流程日志跟踪

    ### Spring AOP 实现流程日志跟踪 #### 一、背景与目的 在现代软件开发过程中,为了确保系统的稳定性和可维护性,通常会引入非功能性的需求来增强应用程序的功能,比如日志记录、安全控制等。这些需求往往不是业务...

    spring aop 五个依赖jar

    Spring AOP(面向切面编程)是Spring框架的重要组成部分,它提供了一种模块化和声明式的方式来处理系统中的交叉关注点,如日志、事务管理等。在Java应用中,AOP通过代理模式实现了切面编程,使得我们可以将业务逻辑...

    Spring源码最难问题:当Spring AOP遇上循环依赖.docx

    Spring源码最难问题:当Spring AOP遇上循环依赖 Spring源码中最难的问题之一是循环依赖问题,当Spring AOP遇上循环依赖时,该如何解决? Spring通过三级缓存机制解决循环依赖的问题。 在Spring中,bean的实例化...

    spring AOP依赖三个jar包

    Spring AOP,即Spring的面向切面编程模块,是Spring框架的重要组成部分,它允许开发者在不修改源代码的情况下,对程序进行横切关注点的处理,如日志、事务管理等。实现这一功能,主要依赖于三个核心的jar包:aop...

    spring aop的demo

    在`springAop1`这个压缩包中,可能包含了一个简单的应用示例,展示了如何定义一个切面类,以及如何在该类中定义通知方法。例如,我们可能会看到一个名为`LoggingAspect`的类,其中包含了`@Before`注解的方法,用于在...

Global site tag (gtag.js) - Google Analytics