首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Spring-方法注入lookup、方法替换MethodReplacer接口

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

作者头像
小小工匠
发布2021-08-16 16:34:14
发布2021-08-16 16:34:14
5760
举报
文章被收录于专栏:小工匠聊架构小工匠聊架构

问题

无状态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

代码语言:javascript
复制
package com.xgj.ioc.lookup;


public class Plane {

    public void fly() {
        System.out.println("ready to fly");
    }
}

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

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

代码语言:javascript
复制
package com.xgj.ioc.lookup;

public interface MagicPilot {

    Plane getPlane();

}

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

代码语言:javascript
复制
<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">

    
    <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接口提供动态实现。 等同于方式二 。

测试类

代码语言:javascript
复制
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"));

    }
}

测试结果:

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

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

代码语言:javascript
复制
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;

    }
}

修改配置文件

代码语言:javascript
复制
<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">

    
    <bean id="plane" class="com.xgj.ioc.lookup.Plane" scope="prototype" />

    <bean id="magicPilotImpl" class="com.xgj.ioc.lookup.MagicPilotImpl"/>

beans>

修改测试类

代码语言:javascript
复制
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"));
    }
}

运行结果

每次调用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方法。


实例

POJO

代码语言:javascript
复制
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

代码语言:javascript
复制
package com.xgj.ioc.methodReplace;

public class PilotOne {

    public Plane getPlane() {
        Plane plane = new Plane();
        plane.setBrand("PilotOne-F22");
        return plane;
    }
}

POJO

代码语言:javascript
复制
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;
    }
}

配置文件

代码语言:javascript
复制
<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">


    
    <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>

测试类

代码语言:javascript
复制
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();
    }
}

运行结果

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


小结

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


总结

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017/08/02 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 问题
  • lookup方法注入
    • 概述
    • 实例
      • 方法一 通过在配置文件中配置的方式实现
      • 方法二 通过实现接口代码的方式实现
    • 小结
  • 方法替换MethodReplacer接口
    • 概述
    • 实例
    • 小结
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档