Java框架Spring

【Spring】08- 动态代理

2020-10-17  本文已影响0人  itlu

1. 动态代理的特点

  1. 字节码随用随创建,随用随加载。
  2. 它与静态代理的区别也在于此。因为静态代理是字节码一上来就创建好,并完成加载。
  3. 装饰者模式就是静态代理的一种体现。

2. 动态代理常用的有两种方式

2.1 基于接口的动态代理

  1. 提供者: JDK 官方的 Proxy 类。
  2. 要求:被代理类最少实现一个接口。

2.1.1 创建一个新项目

  1. 在pom.xml文件中修改打包方式为 jar。
   <packaging>jar</packaging>
  1. 创建对生产商的要求:生产者接口
  public interface Producer {

    /**
     * 销售商品
     * @param money 
     */
    void sale(Float money) ;

    /**
     * 售后服务
     * @param money
     */
    void afterService(Float money);
}
  1. 创建生产者接口的实现类 :
  public class ProducerImpl implements Producer {


    public void sale(Float money) {
        System.out.println("销售电脑,并收获:" + money);
    }

    public void afterService(Float money) {
        System.out.println("提供售后服务,并收获:" + money);
    }
}
  1. 使用基于接口的动态代理实现对 sale方法的增强 :
    public class Client {

    public static void main(String[] args) {
        final Producer producer = new ProducerImpl();
        /*
         * 动态代理特点 : 字节码随用随创建,随用随加载
         * 动态代理的作用:不修改源码的基础上对方法进行增强
         * 动态代理的分类 :
         *       基于接口的动态代理
         *       基于子类的动态代理
         *
         * 基于接口的动态代理
         * */

        Producer producerProxyed = (Producer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
                producer.getClass().getInterfaces(),
                new InvocationHandler() {

                    /**
                     * 作用:执行被代理对象中的任何接口方法都会经过该方法
                     *
                     * @param proxy  代理对象的引用
                     * @param method 当前执行的方法
                     * @param args   当前执行方法所需的参数
                     * @return 和被代理对象方法有相同的返回值
                     * @throws Throwable
                     */
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Object res = null;
                        // 提供增强的代码
                        // 1. 获取方法执行的参数
                        Float money = (Float) args[0];
                        // 判断当前方法是不是销售
                        if ("sale".equals(method.getName())) {
                            res = method.invoke(producer, money * 0.8f);
                        }
                        return res;
                    }
                });

        producerProxyed.sale(1000.f);
    }
}

2.2 基于子类的动态代理

  1. 提供者:第三方的 CGLib,如果报 asmxxxx 异常,需要导入 asm.jar。
   <dependencies>
        <!-- https://mvnrepository.com/artifact/cglib/cglib -->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.1_3</version>
        </dependency>
    </dependencies>
  1. 要求:被代理类不能用final 修饰的类(最终类)。
  1. 创建生产商类 Producer
    public class Producer{


    public void sale(Float money) {
        System.out.println("销售电脑,并收获:" + money);
    }

    public void afterService(Float money) {
        System.out.println("提供售后服务,并收获:" + money);
    }
}
  1. 基于子类的动态代理代码编写 :
    public class Client {

    public static void main(String[] args) {
        final Producer  producer = new Producer();


        /*
         *  基于子类的动态代理 
         */
        Producer cglibProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
            /**
             * @param proxy       代理对象的引用
             * @param method      当前执行的方法
             * @param args        当前执行方法所需的参数
             *                    以上三个参数和基于接口的动态代理中invoke的方法的参数是一样的
             * @param methodProxy 当前执行方法的代理对象
             * @return
             * @throws Throwable
             */
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                // 提供增强的代码
                Object res = null;
                // 提供增强的代码
                // 1. 获取方法执行的参数
                Float money = (Float) args[0];
                // 判断当前方法是不是销售
                if ("sale".equals(method.getName())) {
                    res = method.invoke(producer, money * 0.8f);
                }
                return res;
            }
        });

        cglibProducer.sale(10000.0f);
    }
}

3. 使用动态代理实现事务的控制

  1. 续前节: 07 - Spring account transfer 案例

  2. 编写 BeanFactory 类 创建代理对象,实现对方法的增强。

    public class BeanFactory {

    private AccountService accountService;

    private TransactionManager txManager;

    /**
     * 用于注入 AccountService 对象
     *
     * @param accountService
     */
    public final void setAccountService(AccountService accountService) {
        this.accountService = accountService;
    }

    /**
     * 用于注入 TransactionManager 对象
     *
     * @param txManager
     */
    public void setTxManager(TransactionManager txManager) {
        this.txManager = txManager;
    }

    public AccountService getAccountService() throws IllegalAccessException, InstantiationException {
        return (AccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader(),
                accountService.getClass().getInterfaces(),
                new InvocationHandler() {
                    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                        Object res = null;
                        try {
                            // 1. 开启事务
                            txManager.beginTransaction();
                            // 2. 执行操作
                            res = method.invoke(accountService, objects);
                            // 3. 提交事务
                            txManager.commitTransaction();
                            // 4. 返回结果集
                            return res;
                        } catch (Exception e) {
                            // 5. 回滚事务
                            throw new RuntimeException(e);
                        } finally {
                            // 6. 释放连接
                            txManager.closeTransaction();
                        }
                    }
                });
    }
}
  1. 删除AccountServiceImpl 实现类中所有事务控制的代码

  2. 修改 bean.xml文件 配置

      <!-- 配置代理的service对象  -->
    <bean id="proxyAccountService" factory-bean="beanFactory" factory-method="getAccountService"/>

    <!-- 配置 BeanFactory -->
    <bean id="beanFactory" class="com.lyp.factory.BeanFactory">
        <property name="txManager" ref="txManager"/>
        <property name="accountService" ref="accountService"/>
    </bean>
  1. 此时的bean容器中将会出现 两个 AccountService类型的对象 ,所以在测试类中注入的AccountService对象将会出现冲突的情况。所以需要使用 @Qualifier注解指定需要注入的具体的 AccountService对象的名称。
     @Autowired
    @Qualifier("proxyAccountService") // 这里使用的是动态代理对象
    private AccountService as;
  1. 通过在代码中制造异常和删除异常来测试事务控制是否成功!
通过在代码中制造异常和删除异常来测试事务控制是否成功!
上一篇 下一篇

猜你喜欢

热点阅读