• 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

  • 相关阅读:
    软件定义网络基础---REST API的设计规范
    软件定义网络基础---REST API概述
    软件定义网络基础---北向接口协议概述
    软件定义网络基础---SDN控制平面
    软件定义网络基础---NETCONF协议
    判断是否是完全二叉树
    G: 又见模法师
    欧拉定理+欧拉筛选法
    hdu-2036求任意多边形面积
    hdu1754 区间更新查询(单点更新+查询求区间最大值)
  • 原文地址:https://www.cnblogs.com/wangbin2188/p/15268140.html
Copyright © 2020-2023  润新知