夯实spring(十四):lookup-method和replaced-method怎么使用

通常情况下,我们使用的bean都是单例的,如果一个bean需要依赖于另一个bean的时候,可以在当前bean中声明另外一个bean引用,然后注入依赖的bean,此时被依赖的bean在当前bean中自始至终都是同一个实例。

1,lookup-method (方法查找)

直接看一个案例

public class Service1{
}
public class Service2{
    private Service1 service1;

    public Service1 getService1() {
        return service1;
    }

    public void setService1(Service1 service1) {
        this.service1 = service1;
    }
}

上面2个类,Service1和Service2,而Service2中需要用到Service1 ,可以通过setService1将Service1注入到Service2中。

bean.xml

<?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-4.3.xsd">

    <bean id="service1" class="com.chen.chat.chen.Service1" scope="prototype"/>
    
    <bean id="service2" class="com.chen.chat.chen.Service2">
        <property name="service1" ref="service1"/>
    </bean>
</beans>

上面service1的scope是prototype,表示service1是多例的,每次从容器中获取serviceA都会返回一个新的对象。

而service2的scope没有配置,默认是单例的,通过property元素将serviceA注入。

测试输出

public class Main  {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:bean.xml");
        //从容器中按照类型查找Service1对应的bean。
        System.out.println(context.getBean(Service1.class));//1
        System.out.println(context.getBean(Service1.class));//2

        System.out.println("service2中的service1");
        Service2 service2 = context.getBean(Service2.class);
        //获取service2中的service1对象
        System.out.println(service2.getService1());//3
        System.out.println(service2.getService1());//4
    }
}
com.chen.chat.chen.Service1@6ddf90b0
com.chen.chat.chen.Service1@57536d79
service2中的service1
com.chen.chat.chen.Service1@3b0143d3
com.chen.chat.chen.Service1@3b0143d3

可以看出,1和2输出了不同的Service1,而3和4输出的是同一个serviceA,这是因为service2是单例的,service2中的service1会在容器创建service2的时候,从容器中获取一个service1将其注入到service2中,所以自始至终service2中的service1都是同一个对象。

如果我们希望service2中每次使用Service1的时候,Service1都是一个新的实例,我们怎么实现呢?

lookup-method可以实现这个

我们修改一下上面的案例

public class Service1{
}
public class Service2{
    private Service1 service1;

    public void printService1(){
        this.service1 = this.getService1();
        System.out.println("this:" + this + ",Service1:" + this.service1);
    }

    public Service1 getService1() {
        return null;
    }
}

注意上面的getService1(),这个方法中返回了一个null对象,我们通过spring来创建上面2个bean对象,然后让spring对上面的getService1方法进行拦截,返回指定的bean。

bean.xml

<?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-4.3.xsd">

    <bean id="service1" class="com.chen.chat.chen.Service1" scope="prototype"/>

    <bean id="service2" class="com.chen.chat.chen.Service2">
        <lookup-method name="getService1" bean="service1"/>
    </bean>
</beans>
<lookup-method name="getService1" bean="service1"/>

当我们调用service2中的getService1方法的时候,这个方法会拦截,然后会按照lookup-method元素中bean属性的值作为bean的名称去容器中查找对应bean,然后作为getService1的返回值返回,即调用getServiceA方法的时候,会从spring容器中查找id为service1的bean然后返回。

测试输出

public class Main  {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:bean.xml");
        //从容器中按照类型查找Service1对应的bean。
        System.out.println(context.getBean(Service1.class));
        System.out.println(context.getBean(Service1.class));

        System.out.println("service2中的service1");
        Service2 service2 = context.getBean(Service2.class);
        //获取service2中的service1对象
        service2.printService1();
        service2.printService1();
    }
}
com.chen.chat.chen.Service1@66d1af89
com.chen.chat.chen.Service1@8646db9
service2中的service1
this:com.chen.chat.chen.Service2$$EnhancerBySpringCGLIB$$4271b21e@37374a5e,Service1:com.chen.chat.chen.Service1@4671e53b
this:com.chen.chat.chen.Service2$$EnhancerBySpringCGLIB$$4271b21e@37374a5e,Service1:com.chen.chat.chen.Service1@2db7a79b

可以看到,service1是调用this.getService1()方法获取,源码中这个方法返回的是null,但是spring内部对这个方法进行了拦截,每次调用这个方法的时候,都会去容器中查找service1,然后返回,所以上面最后2行的输出中service1是有值的,并且是不同的service1实例。

小结:

lookup-method:所以我们就知道lookup-method的作用了:方法查找,调用name属性指定的方法的时候,spring会对这个方法进行拦截,然后去容器中查找lookup-method元素中bean属性的值作为名称去查找指定的bean,然后将找到的bean作为方法的返回值返回。

2,replaced-method (方法替换)

比如我们要调用service2中的getService1的时候,我们可以对service2这个bean中的getService1方法进行拦截,把这个调用请求转发到一个替换者处理。这就是replaced-method可以实现的功能,比lookup-method更强大更灵活。

我们来实现一下:

1,定义替换者,需要实现spring中的MethodReplacer接口

MethodReplacer接口

public interface MethodReplacer {
     /**
     *当调用目标对象需要被替换的方法的时候,这个调用请求会被转发到这里的替换者的reimplement方法进行处理。
     * @param obj 被替换方法的目标对象
     * @param method 目标对象的方法
     * @param args 方法的参数
     * @return return value for the method
     */
    Object reimplement(Object var1, Method var2, Object[] var3) throws Throwable;
}

servie2的方法替换者

public class Service2MethodReplacer implements MethodReplacer, ApplicationContextAware {
    private ApplicationContext context;
    @Override
    public Object reimplement(Object o, Method method, Object[] objects) throws Throwable {
        System.out.println("我是Service2MethodReplacer");
        return this.context.getBean(Service1.class);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }
}

ApplicationContextAware
这个接口有一个方法setApplicationContext,这个接口给了自定义的bean中获取applicationContext的能力,当我们的类实现这个接口之后,spring容器创建bean对象的时候,如果bean实现了这个接口,那么容器会自动调用setApplicationContext方法,将容器对象applicationContext传入,此时在我们的bean对象中就可以使用容器的任何方法了。

2,定义替换者bean
 <bean id="service2MethodReplacer" class="com.chen.chat.chen.Service2MethodReplacer"/>
3,通过replaced-method元素配置目标bean需要被替换的方法
    <bean id="service2" class="com.chen.chat.chen.Service2">
        <replaced-method name="getService1" replacer="service2MethodReplacer"/>
    </bean>

总的bean.xml

<?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-4.3.xsd">
    <bean id="service2" class="com.chen.chat.chen.Service2">
        <replaced-method name="getService1" replacer="service2MethodReplacer"/>
    </bean>

    <bean id="service2MethodReplacer" class="com.chen.chat.chen.Service2MethodReplacer"/>
</beans>

注意上面的replaced-method元素的2个属性:

name:用于指定当前bean需要被替换的方法

replacer:替换者,即实现了MethodReplacer接口的类对应的bean

上面配置中当调用service2的getService1的时候,会自动调用service2MethodReplacer这个bean中的reimplement方法进行处理

测试输出

public class Main  {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:bean.xml");
        //从容器中按照类型查找Service1对应的bean。
        System.out.println(context.getBean(Service1.class));
        System.out.println(context.getBean(Service1.class));

        System.out.println("service2中的service1");
        Service2 service2 = context.getBean(Service2.class);
        //获取service2中的service1对象
        service2.printService1();
        service2.printService1();
    }
}
com.chen.chat.chen.Service1@4671e53b
com.chen.chat.chen.Service1@2db7a79b
service2中的service1
我是Service2MethodReplacer
this:com.chen.chat.chen.Service2$$EnhancerBySpringCGLIB$$4271b21e@6950e31,Service1:com.chen.chat.chen.Service1@b7dd107
我是Service2MethodReplacer
this:com.chen.chat.chen.Service2$$EnhancerBySpringCGLIB$$4271b21e@6950e31,Service1:com.chen.chat.chen.Service1@42eca56e

从输出中可以看出结果和lookup-method案例效果差不多,实现了单例bean中使用多例bean的案例。

输出中都有CGLIB这样的字样,说明是通过cglib实现的。

总结

  • lookup-method:方法查找,可以对指定的bean的方法进行拦截,然后从容器中查找指定的bean作为被拦截方法的返回值
  • replaced-method:方法替换,可以实现bean方法替换的效果,整体来说比lookup-method更灵活一些
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值