• lookup-method和replace-method注入


    官方文档说明:

    在Spring5核心的1.4.6章节

    在大多数应用程序方案中,容器中的大多数bean都是 singletons 。当单例bean需要与另一个单例bean协作或非单例bean需要与另一个非单例bean协作时,通常通过将一个bean定义为另一个bean的属性来处理依赖关系。当bean生命周期不同时会出现问题。假设单例bean A需要使用非单例(原型)bean B,可能是在A上的每个方法调用上。容器只创建一次单例bean A,因此只有一次机会来设置属性。每次需要时,容器都不能为bean A提供bean B的新实例。

    解决方案是放弃一些控制反转。您可以通过实现 ApplicationContextAware 接口 make bean A aware of the container ,并通过 making a getBean("B") call to the container 在每次Bean A需要时请求(通常是新的)bean B实例。以下示例显示了此方法:

    // a class that uses a stateful Command-style class to perform some processing
    package fiona.apple;
    ​
    // Spring-API imports
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    ​
    public class CommandManager implements ApplicationContextAware {
    ​
        private ApplicationContext applicationContext;
    ​
        public Object process(Map commandState) {
            // grab a new instance of the appropriate Command
            Command command = createCommand();
            // set the state on the (hopefully brand new) Command instance
            command.setState(commandState);
            return command.execute();
        }
    ​
        protected Command createCommand() {
            // notice the Spring API dependency!
            return this.applicationContext.getBean("command", Command.class);
        }
    ​
        public void setApplicationContext(
                ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }
    }

    前面的内容是不可取的,因为业务代码知道并耦合到Spring Framework。方法注入是Spring IoC容器的一个高级功能,可以让您干净地处理这个用例。

    查找方法注入

    Lookup方法注入是容器覆盖容器管理bean上的方法并返回容器中另一个命名bean的查找结果的能力。查找通常涉及原型bean,如 the preceding section 中描述的场景。 Spring Framework通过使用CGLIB库中的字节码生成来动态生成覆盖该方法的子类来实现此方法注入。

    • 为了使这个动态子类工作,Spring bean容器子类不能 final 的类,以及要重写的方法也不能 final

    • 对具有 abstract 方法的类进行单元测试要求您自己对该类进行子类化,并提供 abstract 方法的存根实现。

    • 组件扫描也需要具体方法,这需要具体的类来获取。

    • 另一个关键限制是查找方法不适用于工厂方法,特别是配置类中没有 @Bean 方法,因为在这种情况下,容器不负责创建实例,因此无法动态创建运行时生成的子类。

    对于前面代码片段中的 CommandManager 类,Spring容器动态地覆盖 createCommand() 方法的实现。 CommandManager 类没有任何Spring依赖项,因为重写的示例显示:

    package fiona.apple;
    ​
    // no more Spring imports!
    public abstract class CommandManager {
    ​
        public Object process(Object commandState) {
            // grab a new instance of the appropriate Command interface
            Command command = createCommand();
            // set the state on the (hopefully brand new) Command instance
            command.setState(commandState);
            return command.execute();
        }
    ​
        // okay... but where is the implementation of this method?
        protected abstract Command createCommand();
    }

    在包含要注入的方法的客户端类(在本例中为 CommandManager )中,要注入的方法需要以下形式的签名:

    <public|protected> [abstract] <return-type> theMethodName(no-arguments);

    如果方法是 abstract ,则动态生成的子类实现该方法。否则,动态生成的子类将覆盖原始类中定义的具体方法。请考虑以下示例:

    <!-- a stateful bean deployed as a prototype (non-singleton) -->
    <bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
        <!-- inject dependencies here as required -->
    </bean><!-- commandProcessor uses statefulCommandHelper -->
    <bean id="commandManager" class="fiona.apple.CommandManager">
        <lookup-method name="createCommand" bean="myCommand"/>
    </bean>

    标识为 commandManager 的bean在需要 myCommand bean的新实例时调用自己的 createCommand() 方法。如果实际需要的话,您必须小心将 myCommandbean部署为原型。如果是 singleton ,则每次都返回 myCommand bean的相同实例。

    或者,在基于注释的组件模型中,您可以通过 @Lookup 注释声明查找方法,如以下示例所示:

    public abstract class CommandManager {
    ​
        public Object process(Object commandState) {
            Command command = createCommand();
            command.setState(commandState);
            return command.execute();
        }
    ​
        @Lookup("myCommand")
        protected abstract Command createCommand();
    }

    或者,更具惯用性,您可以依赖于针对查找方法的声明返回类型解析目标bean:

    public abstract class CommandManager {
    ​
        public Object process(Object commandState) {
            MyCommand command = createCommand();
            command.setState(commandState);
            return command.execute();
        }
    ​
        @Lookup
        protected abstract MyCommand createCommand();
    }

    请注意,您通常应该使用具体的存根实现来声明这种带注释的查找方法,以使它们与Spring的组件扫描规则兼容,其中默认情况下抽象类被忽略。此限制不适用于显式注册或显式导入的bean类。

    任意方法更换

    与查找方法注入相比,一种不太有用的方法注入形式是能够使用另一个方法实现替换托管bean中的任意方法。您可以安全地跳过本节的其余部分,直到您确实需要此功能。

    使用基于XML的配置元数据,您可以使用 replaced-method 元素将已存在的方法实现替换为已部署的bean。考虑以下类,它有一个我们想要覆盖的名为computeValue 的方法:

    public class MyValueCalculator {
    ​
        public String computeValue(String input) {
            // some real code...
        }
    ​
        // some other methods...
    }

    实现 org.springframework.beans.factory.support.MethodReplacer 接口的类提供新的方法定义,如以下示例所示:

    /**
     * meant to be used to override the existing computeValue(String)
     * implementation in MyValueCalculator
     */
    public class ReplacementComputeValue implements MethodReplacer {
    ​
        public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
            // get the input value, work with it, and return a computed result
            String input = (String) args[0];
            ...
            return ...;
        }
    }

    部署原始类并指定方法覆盖的bean定义类似于以下示例:

    <bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
        <!-- arbitrary method replacement -->
        <replaced-method name="computeValue" replacer="replacementComputeValue">
            <arg-type>String</arg-type>
        </replaced-method>
    </bean><bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>

    您可以在 <replaced-method/> 元素中使用一个或多个 <arg-type/> 元素来指示被覆盖的方法的方法签名。仅当方法重载且类中存在多个变体时,才需要参数的签名。为方便起见,参数的类型字符串可以是完全限定类型名称的子字符串。例如,以下全部匹配 java.lang.String

    java.lang.String
    String
    Str

    代码演示:

    look-up:

    public abstract class Car {
        
    
        //用于lookup-method注入
        @Lookup("taxi")
        public abstract Taxi createTaxi();
    
        private Taxi taxi;
    
        public Taxi getTaxi() {
            return taxi;
        }
    
        //setter注入
        public void setTaxi(Taxi taxi) {
            this.taxi = taxi;
        }
    
    }
    public class Taxi {
        public void say() {
            System.out.println("I am a Taxi...");
        }
    }

    xml文件

    <!-- ====================lookup-method属性注入==================== -->
        <bean id="car" class="spring2.Car">
            <!--注意:下面这句配置和lookup-method注入没有关系,我们只是为了出于演示和说明配置该bean -->
                <property name="taxi" ref="taxi" /> 
            <!--lookup-method注入 -->
                <lookup-method name="createTaxi" bean="taxi" />  
        </bean>
        <bean id="taxi"
            class="spring2.Taxi" scope="prototype" />

    测试类:

    public class Test1 {
        
           ClassPathXmlApplicationContext xmlBeanFactory = null;
    
            @Before
            public void initXmlBeanFactory() {
                System.out.println("
    ========测试方法开始=======
    ");
                xmlBeanFactory = new ClassPathXmlApplicationContext("spring2.xml");
            }
    
            @After
            public void after() {
                System.out.println("
    ========测试方法结束=======
    ");
            }
    
            @Test
            public void test8() {
                // 测试lookup-method注入
                Car car1 = xmlBeanFactory.getBean("car", Car.class);
                Car car2 = xmlBeanFactory.getBean("car", Car.class);
    
                System.out.println("Car:singleton,所以animal1==animal2应该为" + (car1 == car2));
    
                Taxi dog1 = car1.getTaxi();
                Taxi dog2 = car2.getTaxi();
                System.out.println("Taxi:prototype,Car:singleton,未使用lookup-method注入所以dog1==dog2应该为" + (dog1 == dog2));
    
                //注意:这里是通过createDog()方法获取
                Taxi taxi3 = car1.createTaxi();
                Taxi taxi4 = car2.createTaxi();
                System.out.println("Taxi:prototype,Car:singleton,使用了lookup-method注入所以dog3==dog4应该为" + (taxi3 == taxi4));
    
            }
    
    }

    结果:

    ========测试方法开始=======
    Car:singleton,所以animal1==animal2应该为true
    Taxi:prototype,Car:singleton,未使用lookup-method注入所以dog1==dog2应该为true
    Taxi:prototype,Car:singleton,使用了lookup-method注入所以dog3==dog4应该为false
    
    ========测试方法结束=======

    replaced-method

    public class ReplaceDog implements MethodReplacer {
        @Override
        public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
            System.out.println("Hello, I am a white dog...");
    
            Arrays.stream(args).forEach(str -> System.out.println("参数:" + str));
            return obj;
        }
    }
    public class OriginalDog {
        public void sayHello() {
            System.out.println("Hello,I am a black dog...");
        }
    
        public void sayHello(String name) {
            System.out.println("Hello,I am a black dog, my name is " + name);
        }
    }

    xml配置

    <!-- ====================replace-method属性注入==================== -->
    <bean id="dogReplaceMethod" class="com.lyc.cn.v2.day01.method.replaceMethod.ReplaceDog"/>
    <bean id="originalDogReplaceMethod" class="com.lyc.cn.v2.day01.method.replaceMethod.OriginalDog">
        <replaced-method name="sayHello" replacer="dogReplaceMethod">
            <arg-type match="java.lang.String"></arg-type>
        </replaced-method>
    </bean>

    测试类:

    @Test
    public void test9() {
        //测试replace-method注入
        OriginalDog originalDog = xmlBeanFactory.getBean("originalDogReplaceMethod", OriginalDog.class);
        originalDog.sayHello("输出结果已经被替换了。。。");
    }

    结果:

    ========测试方法开始=======
    
    十二月 09, 2019 3:59:33 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
    信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3caeaf62: startup date [Mon Dec 09 15:59:33 CST 2019]; root of context hierarchy
    十二月 09, 2019 3:59:33 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
    信息: Loading XML bean definitions from class path resource [spring2.xml]
    Hello, I am a white dog...
    参数:输出结果已经被替换了。。。
    
    ========测试方法结束=======
  • 相关阅读:
    kubernetes 配置示例 Lifecycle
    Kubernetes 集群部署(5) kubectl 常用命令
    Kubernetes 配置示例 一个Pod 包含多个容器
    实现lighttpd+tomcat
    tomcat解决中文乱码问题
    linux安装tomcat
    zz Tomcat+JSP经典配置实例
    jdbc查询大量数据内存溢出的解决方法
    汉明距离
    log4j乱码问题
  • 原文地址:https://www.cnblogs.com/dalianpai/p/12009977.html
Copyright © 2020-2023  润新知