• Spring 中如何自动创建代理(spring中的三种自动代理创建器)


    Spring 提供了自动代理机制,可以让容器自动生成代理,从而把开发人员从繁琐的配置中解脱出来 。 具体是使用 BeanPostProcessor 来实现这项功能。

    这三种自动代理创建器 为:BeanNameAutoProxyCreator     ,  DefaultAdvisorAutoProxyCreator  ,    AbstractAdvisorAutoProxyCreator。

    1BeanPostProcessor

    BeanPostProcessor 代理创建器的实现类可以分为 3 类:

    类型实现类
    基于 Bean 配置名规则 BeanNameAutoProxyCreator
    基于 Advisor 匹配规则 DefaultAdvisorAutoProxyCreator
    基于 Bean 中的 AspectJ 注解标签的匹配规则 AnnotationAwareAspectJAutoProxyCreator

    BeanPostProcessor 类继承关系

    所有的自动代理器类都实现了 BeanPostPorcessor ,在容器实例化 Bean 时, BeanPostProcessor 将对它进行加工处理,所以自动代理创建器能够对满足匹配规则的 bean 自动创建代理对象。

    2 BeanNameAutoProxyCreator

    假设有以下两个实体类(用户与充电宝)。

    用户类:

    public class User {
    
        public void rent(String userId) {
            System.out.println("User:租赁【充电宝】");
        }
    
        public void back(String userId){
            System.out.println("User:归还【充电宝】");
    
        }
    }

    充电宝:

    public class Charger {
    
        public void rent(String userId) {
            System.out.println("Charger:【充电宝】被租赁");
        }
    }

    我们希望通过 BeanNameAutoProxyCreator 通过 Bean 的名称来自动创建代理,实现增强:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:p="http://www.springframework.org/schema/p"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
    
        <bean id="user" class="net.deniro.spring4.aop.User"/>
        <bean id="charger" class="net.deniro.spring4.aop.Charger"/>
    
        <!-- 前置增强-->
        <bean id="rentBeforeAdvice" class="net.deniro.spring4.aop.RentBeforeAdvice"/>
    
        <!-- 使用 BeanNameAutoProxyCreator-->
        <bean
                class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"
                p:beanNames="*er"
                p:interceptorNames="rentBeforeAdvice"
                p:optimize="true"
                ></bean>
    </beans>

    BeanNameAutoProxyCreator的 beanNames 属性允许指定一组需要自动代理的 Bean 名称, 这里可以使用 * 通配符 。

    因为我们需要代理的类名分别是 user 与 charger,都是以 er 结尾的,所以我们这里定义为 *er

    也可以通过 beanNames 的 value 值来明确指定需要代理的 Bean 名称,多个以逗号分隔(更常用)。

    <!-- 指定自动代理的 Bean 名称-->
    <property name="beanNames" value="user,charger">
    </property>

    也可以通过 list 方式来指定 beanNames 的值:

    <property name="beanNames">
        <list>
            <value>user</value>
            <value>charger</value>
        </list>
    </property>

    p:optimize 设置为 true,则表示使用 CGLib 动态代理技术。

    通过这样的配置之后,容器在创建 user 和 charger Bean 的实例时,就会自动为它们创建代理对象,而这一操作对于使用者来说完全是透明的 。

    单元测试:

    User user = (User) context.getBean("user");
    Charger charger = (Charger) context.getBean("charger");
    
    String userId = "001";
    user.rent(userId);
    charger.rent(userId);

    输出结果:

    准备租赁的用户 ID:001
    User:租赁【充电宝】
    准备租赁的用户 ID:001
    Charger:【充电宝】被租赁

    3 DefaultAdvisorAutoProxyCreator

    切面 Advisor 是切点和增强的复合体,而 DefaultAdvisorAutoProxyCreator 能够扫描 Advisor, 并将 Advisor 自动织入到匹配的目标 Bean 中。

    <bean id="user" class="net.deniro.spring4.aop.User"/>
    <bean id="charger" class="net.deniro.spring4.aop.Charger"/>
    
    <!-- 前置增强-->
    <bean id="rentBeforeAdvice" class="net.deniro.spring4.aop.RentBeforeAdvice"/>
    
    <!-- 静态正则表达式方法名匹配-->
    <bean id="regexpAdvisor"
          class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
          p:advice-ref="rentBeforeAdvice">
        <!-- 匹配模式-->
        <property name="patterns">
            <list>
                <!-- 匹配字符串-->
                <value>.*rent.*</value>
            </list>
        </property>
    </bean>
    
    <!-- 使用 DefaultAdvisorAutoProxyCreator-->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

    首先我们配置了以静态正则表达式方法名匹配的切面,然后直接配置了 DefaultAdvisorAutoProxyCreator Bean。

    测试代码与输出结果与上一小节的 BeanNameAutoProxyCreator 相同。

    JDK 动态代理是通过接口来实现方法拦截,所以必须确保要拦截的目标在接口中有定义。

    CGLib 动态代理是通过动态生成代理子类来实现方法拦截,所以必须确保要拦截的目标方法可以被子类所访问,也就是目标方法必须定义为非 final, 且非私有实例方法 。

  • 相关阅读:
    OS快速开发必备
    YYmodel 郭耀源 底层分析
    UITableView使用总结和性能优化
    文件操作
    DOM解析
    SAX解析
    Gallery
    菜单(menu)
    计时器(Chronometer)、标签(TabHost)
    ListActivity
  • 原文地址:https://www.cnblogs.com/weixupeng/p/10482122.html
Copyright © 2020-2023  润新知