Spring-方法注入lookup、方法替换MethodReplacer接口

简介: Spring-方法注入lookup、方法替换MethodReplacer接口

问题


无状态Bean的作用域一般可以配置为singleton(单例模式),如果我们往singleton的Pilot类中注入prototype的Plane类,并希望每次调用Pilot的getPlane()方法都能返回一个新的plane Bean ,该怎么办呢?


如果我们使用传统的注入方式将无法实现这样的需求, 因为Singleton的Bean注入关联Bean的动作仅有一次,虽然 plane Bean的作用范围是prototype,但是 Pilot通过getPlane()方法返回的对象还是最开始注入的那个plane Bean .


如果希望每次每次调用getPlane()方法都返回一个新的plane Bean, 一种可选的方法是让Pilot类实现BeanFactoryAware接口,且能够访问容器的引用。


但是上面的方法依赖SPring框架接口,十分不友好。 有没有其他办法呢?


通过方法注入的方案完美的解决这个问题。


lookup方法注入


概述


Spring IoC容器拥有复写Bean方法的能力,主要源于CGLib类包。

CGlib可以找运行期间动态操作class字节码,为Bean动态创建子类或者实现类。


实例

代码已托管到Github—> https://github.com/yangshangwei/SpringMaster


20170802040725720.jpg

package com.xgj.ioc.lookup;
public class Plane {
    public void fly() {
        System.out.println("ready to fly");
    }
}

方法一 通过在配置文件中配置的方式实现

我们声明一个MagicPilot接口,并声明一个getPlane()接口方法

package com.xgj.ioc.lookup;
public interface MagicPilot {
    Plane getPlane();
}


下面不编写任何实现类,仅通过配置为该接口提供动态的实现,让getPlane接口方法每次都返回新的plane Bean。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- prototype类型的Bean -->
    <bean id="plane" class="com.xgj.ioc.lookup.Plane" scope="prototype" />
    <!-- 实施方法注入 -->
    <bean id="magicPilot" class="com.xgj.ioc.lookup.MagicPilot">
        <lookup-method name="getPlane" bean="plane" />
    </bean>
</beans>


通过lookup-method元素标签为MagicPlane的getPlane方法提供动态实现,返回prototype类型的Plane bean , 这样Spring将在运行期为MagicPlane接口提供动态实现。 等同于方式二 。


测试类

package com.xgj.ioc.lookup;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class LookupTest {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "classpath:com/xgj/ioc/lookup/beans.xml");
        Plane plane = ctx.getBean("magicPilot", MagicPilot.class).getPlane();
        plane.fly();
        System.out.println(ctx.isPrototype("plane"));
    }
}


测试结果:

20170802041235305.jpg

方法二 通过实现接口代码的方式实现

编写MagicPilot的实现类,实现MagicPilot 和 ApplicationContextAware 接口

package com.xgj.ioc.lookup;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class MagicPilotImpl implements MagicPilot, ApplicationContextAware {
    private ApplicationContext applicationContext;
    @Override
    public Plane getPlane() {
        return (Plane) applicationContext.getBean("plane");
    }
    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        this.applicationContext = applicationContext;
    }
}


修改配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- prototype类型的Bean -->
    <bean id="plane" class="com.xgj.ioc.lookup.Plane" scope="prototype" />
    <bean id="magicPilotImpl" class="com.xgj.ioc.lookup.MagicPilotImpl"/>
</beans>


修改测试类

package com.xgj.ioc.lookup;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class LookupTest {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "classpath:com/xgj/ioc/lookup/beans.xml");
        MagicPilotImpl magicPilotImpl = ctx.getBean("magicPilotImpl",
                MagicPilotImpl.class);
        magicPilotImpl.getPlane().fly();
        System.out.println(ctx.isPrototype("plane"));
    }
}


运行结果

20170802041630823.jpg

每次调用MagicPlane的getPlane方法都会从容器中获取plane bean, 由于plane Bean的作用域是prototype,因此每次都能返回新的plane实例。


如果将plane Bean的作用域设置为默认的singleton ,虽然也可以润兴,但是这个时候lookup所提供的方法注入就没有意义了。 因为我们可以很轻松的编写一个magicPlane的实现类,用属性注入的方式达到相同的目的 ,因此lookup 方法注入是有一定使用范围的,一般在希望通过一个singleton Bean获取一个prototype Bean时使用


小结


lookup 方法的使用场景: 一般在希望通过一个singleton Bean获取一个prototype Bean时使用


方法替换MethodReplacer接口

概述


使用某个Bean的方法替换另外一个Bean的方法。

必须实现 org.springframework.beans.factory.support.MethodReplacer 接口,重写reimplement方法。


实例

20170802045016092.jpg


POJO

package com.xgj.ioc.methodReplace;
public class Plane {
    private String brand;
    public void setBrand(String brand) {
        this.brand = brand;
    }
    public String getBrand() {
        System.out.println("brand:" + brand);
        return brand;
    }
}


POJO

package com.xgj.ioc.methodReplace;
public class PilotOne {
    public Plane getPlane() {
        Plane plane = new Plane();
        plane.setBrand("PilotOne-F22");
        return plane;
    }
}


POJO

package com.xgj.ioc.methodReplace;
import java.lang.reflect.Method;
import org.springframework.beans.factory.support.MethodReplacer;
public class PilotTwo implements MethodReplacer {
    public Object reimplement(Object obj, Method method, Object[] args)
            throws Throwable {
        Plane plane = new Plane();
        plane.setBrand("PilotTwo-F35");
        return plane;
    }
}


配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 使用pilotTwo的MethodReplacer接口方法替换该Bean的getPlane方法 -->
    <bean id="pilotOne" class="com.xgj.ioc.methodReplace.PilotOne">
        <replaced-method name="getPlane" replacer="pilotTwo"/>
    </bean>
    <bean id="pilotTwo" class="com.xgj.ioc.methodReplace.PilotTwo"/>
</beans>


测试类

package com.xgj.ioc.methodReplace;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MethodReplacerTest {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "classpath:com/xgj/ioc/methodReplace/beans.xml");
        ctx.getBean("pilotOne", PilotOne.class).getPlane().getBrand();
    }
}


运行结果

20170802045509271.jpg


返回了 第二个Bean的 brand:PilotTwo-F35 ,可见替换成功。


小结


用于替换他人的Bean必须实现MethodReplacer接口,Spring利用该接口的方法去替换目标Bean的方法。


总结


像lookup和methodreplacer高级功能,在实际中使用的很少,而属性注入、构造函数注入等反而在实际项目中使用的最多,我们了解即可。


相关文章
|
18天前
|
Java Spring
在使用Spring的`@Value`注解注入属性值时,有一些特殊字符需要注意
【10月更文挑战第9天】在使用Spring的`@Value`注解注入属性值时,需注意一些特殊字符的正确处理方法,包括空格、引号、反斜杠、新行、制表符、逗号、大括号、$、百分号及其他特殊字符。通过适当包裹或转义,确保这些字符能被正确解析和注入。
|
30天前
|
Java 测试技术 程序员
为什么Spring不推荐@Autowired用于字段注入?
作为Java程序员,Spring框架在日常开发中使用频繁,其依赖注入机制带来了极大的便利。然而,尽管@Autowired注解简化了依赖注入,Spring官方却不推荐在字段上使用它。本文将探讨字段注入的现状及其存在的问题,如难以进行单元测试、违反单一职责原则及易引发NPE等,并介绍为何Spring推荐构造器注入,包括增强代码可读性和维护性、方便单元测试以及避免NPE等问题。通过示例代码展示如何将字段注入重构为构造器注入,提高代码质量。
|
11天前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
16 1
|
12天前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
21 1
|
17天前
|
存储 安全 Java
|
9天前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
14 0
|
1月前
|
缓存 Java Spring
源码解读:Spring如何解决构造器注入的循环依赖?
本文详细探讨了Spring框架中的循环依赖问题,包括构造器注入和字段注入两种情况,并重点分析了构造器注入循环依赖的解决方案。文章通过具体示例展示了循环依赖的错误信息及常见场景,提出了三种解决方法:重构代码、使用字段依赖注入以及使用`@Lazy`注解。其中,`@Lazy`注解通过延迟初始化和动态代理机制有效解决了循环依赖问题。作者建议优先使用`@Lazy`注解,并提供了详细的源码解析和调试截图,帮助读者深入理解其实现机制。
21 1
|
30天前
|
自然语言处理 JavaScript Java
Spring 实现 3 种异步流式接口,干掉接口超时烦恼
本文介绍了处理耗时接口的几种异步流式技术,包括 `ResponseBodyEmitter`、`SseEmitter` 和 `StreamingResponseBody`。这些工具可在执行耗时操作时不断向客户端响应处理结果,提升用户体验和系统性能。`ResponseBodyEmitter` 适用于动态生成内容场景,如文件上传进度;`SseEmitter` 用于实时消息推送,如状态更新;`StreamingResponseBody` 则适合大数据量传输,避免内存溢出。文中提供了具体示例和 GitHub 地址,帮助读者更好地理解和应用这些技术。
144 0
|
1月前
|
存储 NoSQL Java
Spring Boot项目中使用Redis实现接口幂等性的方案
通过上述方法,可以有效地在Spring Boot项目中利用Redis实现接口幂等性,既保证了接口操作的安全性,又提高了系统的可靠性。
32 0
|
XML Java 数据格式
Spring【依赖注入】就是这么简单(二)
在Spring的第二篇中主要讲解了Spring Core模块的使用IOC容器创建对象的问题,Spring Core模块主要是解决对象的创建和对象之间的依赖关系,因此本博文主要讲解如何使用IOC容器来解决对象之间的依赖关系!
133 0
Spring【依赖注入】就是这么简单(二)