美文网首页Java框架Spring
【Spring】08- 动态代理

【Spring】08- 动态代理

作者: itlu | 来源:发表于2020-10-17 17:36 被阅读0次

    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. 通过在代码中制造异常和删除异常来测试事务控制是否成功!
    通过在代码中制造异常和删除异常来测试事务控制是否成功!

    相关文章

      网友评论

        本文标题:【Spring】08- 动态代理

        本文链接:https://www.haomeiwen.com/subject/guzxmktx.html