zoukankan      html  css  js  c++  java
  • spring之lookup-method、replaced-method

    在开发中大部分使用到的Bean对象都是单例的,如果有一单例对象依赖一多实例对象时。由于Spring容器在启动后就初始化好了单实例对象,所以依赖的多实例对象也会进行创建好,但是这样会造成一个问题即:单实例对象有且仅有一次机会装配这个多实例对象

    lookup-method 注入

    lookup-method 注入底层是依赖了CGLIB 库提供的方法可以实现动态Bean的实现,下面是其简单示例需求:

    有一电子工厂可以生产电子产品例如手机、电脑等,电子经销商希望每次进货的时候都可以拿到最新生产的产品。

    public abstract class AbstractFactory {
         // 获取商品的抽象方法
        protected abstract Product getProduct();
    }
    
    //------------------------------------------
    @Data
    public class Product {
    
        private String productName;
    
        private String productPrice;
    
        private String productAddress;
    }
    
    //-----------------------------------------------
    public class Phone extends Product {
        public Phone() {
            System.out.println("生产的是手机");
        }
    }
    
    //----------------------------------------------
    public class Computer extends Product {
    
        public Computer() {
            System.out.println("生产的是电脑");
        }
    }

    xml的配置

        <bean id="phone" class="com.codegeek.ioc.day2.lookup.Phone" scope="prototype"/>
        <bean id="computer" class="com.codegeek.ioc.day2.lookup.Computer" scope="prototype"/>
    
        <bean class="com.codegeek.ioc.day2.lookup.AbstractFactory">
            <!--name属性指定抽象工厂的抽象方法名。而bean的值即bean的id值-->
            <lookup-method bean="phone" name="createProduct"></lookup-method>
        </bean>

    测试:

        @Test
        public void testLookup() {
            // 获取工厂
            AbstractFactory bean = (AbstractFactory) applicationContext.getBean(AbstractFactory.class);
            // 调用生产产品的方法
            bean.createProduct();
        }
    //输出
    /**
    生产的是手机
    */

    上述使用的xml配置进行实现,也可以使用注解去实现如下所示:

    • 修改手机类与电脑类如下
    @Component
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public class Phone extends Product {
        public Phone() {
            System.out.println("生产的是手机");
        }
    }
    
    @Component
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public class Computer extends Product {
    
        public Computer() {
            System.out.println("生产的是电脑");
        }
    }

    抽象工厂类

    @Component
    public abstract class AbstractFactory {
        // 在指定方法上指定创建Bean的名称即可
        @Lookup("computer")
        public abstract Product createProduct();
    }

    注意:

    以上我们并没有实现此createProduct()抽象方法但是运行结果依然可以生产手机或者电脑,这是由于Spring底层使用CGLIB代理动态生成了此抽象工厂的子类以及重写实现了其抽象方法。这里需要注意的是代理的对象不能是final修饰,其方法也不能是final修饰。否则Spring无法使用CGLIB代理动态生成子类方法创建对象。所以一般我们将被代理的类设置为抽象类,被代理类的方法设置为抽象方法,而且除此之外需要注意的是一般注入的对象的scope 设置为多实例的,否则每次生成的都是同一对象。

    replace-method
    replace-method是Spring 动态借助CGLIB改变bean中的方法,通过改变方法逻辑注入对象,该方法的使用需要依赖Spring提供的MethodReplacer 接口实现。

    定义一个打印输出用户名的方法然后使用replace-method 改变方法的输出值

    定义接口以及原生实现

    public interface UserService {
    
        void findUserNameById(String userId);
    }
    
    public class UserServiceImpl implements UserService {
        @Override
        public void findUserNameById(String userId) {
            String desc = userId == "1" ? "主角" : "路人";
            System.out.println(desc);
        }
    }
    
    //定义MethodReplacer实现
    public class UserReplaceMethod implements MethodReplacer {
    
        /***
         *
         *   @Override
         *     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         *
         *         return proxy;
         *     }
         */
        @Override
        public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
            System.out.println("执行的方法:" + method.getName());
            System.out.println("参数:"+ Arrays.stream(args).findFirst().get()) ;
            System.out.println("我是MethodReplacer......替换后的方法");
            return obj;
        }
    }

    xml配置

        <bean id="userService" class="com.codegeek.ioc.day2.replacemethod.UserServiceImpl">
            <replaced-method name="findUserNameById" replacer="replaceMethod">
                <arg-type>java.lang.String</arg-type>
            </replaced-method>
        </bean>
    
        <!-- ====================replace-method属性注入==================== -->
        <bean id="replaceMethod" class="com.codegeek.ioc.day2.replacemethod.UserReplaceMethod"/>

    测试:

        @Test
        public void testReplaceMethod() {
            UserService bean = applicationContext.getBean("userService",UserServiceImpl.class);
            bean.findUserNameById("1");
        }
    /**输出
    ...替换后的方法
    
    */

    看到MethodReplacer 的实现是不是感觉和JDK的InvocationHandler接口非常类似呢?其实可以知道Spring的实现也是使用了反射以及底层CGLIB的实现完成方法替换。

    lookup-method与@Autowired依赖注入的区别
    Autowired用于给一个单例对象注入另一个单例对象。 但是无法注入另外一个多实例对象,这是由于单例的bean只会初始化一次,所以这个多实例bean实际上可以看成是一个“单例bean”。除了可以使用applicationContext.getBean去获取最新的实例对象,最完美的方式是使用lookup-method 完成注入,由于采用CGLIB底层动态实现类以及重写类方法可以完美做到零耦合,开发中建议使用此种方式完成方法注入。


    ————————————————
    版权声明:本文为CSDN博主「codegeekgao」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/javaee_gao/article/details/106265081

  • 相关阅读:
    linux查看CPU和内存信息
    linux yum命令详解
    查看文件中关键字前后几行的内容
    vue.js+web storm安装及第一个vue.js
    android GPS: code should explicitly check to see if permission is available
    ASP.NET MVC Identity 使用自己的SQL Server数据库
    阿里云服务器,tomcat启动,一直卡在At least one JAR was scanned for TLDs yet contained no TLDs就不动了
    ASP.NET MVC4 MVC 当前上下文中不存在名称“Scripts”
    python 将windows字体中的汉字生成图片的方法
    Java android DES+Base64加密解密
  • 原文地址:https://www.cnblogs.com/wangbin2188/p/15268140.html
Copyright © 2011-2022 走看看