AbstractBeanDefinition的MethodOverrides属性及@LookUp

使用场景及问题

在Spring的诸多应用场景中bean都是单例形式,当一个单例bean需要和一个原型bean组合使用或者一个原型bean和另一个原型bean组合使用时,我们通常都是将依赖以属性的方式放到bean中来引用,然后以@Autowired来标记需要注入的属性。

但是这种方式在bean的生命周期不一样的时候将会出现问题,假设单例bean A需要一个原型bean B,我们在A中注入bean B,每次调用bean A中的方法时都会用到bean B,Spring Ioc容器只在容器初始化时执行一次,也就是bean A中的依赖bean B只有一次注入的机会,但是实际上bean B我们需要的是每次调用方法时都获取一个新的对象(原型),所以问题明显就是:我们需要bean B是一个原型bean,而事实上bean B的依赖只注入了一次变成了事实上的单例bean。

代码如下:

public interface Fruit {
    void showFruitName();
}

@Component
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Apple implements Fruit {
    @Override
    public void showFruitName() {
        System.out.println("fruit Name is apple");
    }
}

@Component
public class FruitPlant {
    @Autowired
    Fruit fruit;
    public void showFruit() {
        System.out.println(fruit);
        fruit.showFruitName();
    }
}

// 配置类
@Configuration
@ComponentScan("com.modules.test")
public class TestConfig {}

//Main
public class ProcessorMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(TestConfig.class);
        //从上下文中获取FruitPlant对象
        FruitPlant fruitPlant = (FruitPlant) context.getBean("fruitPlant");
        //第一次显示FruitPlant里的Fruit
        fruitPlant.showFruit();
        //第二次显示FruitPlant里的Fruit
        fruitPlant.showFruit();
    }
}

输出结果如下

com.modules.test.Apple@616ac46a
fruit Name is apple
com.modules.test.Apple@616ac46a
fruit Name is apple

每次输出Apple的HashCode都是一样的,证明我们实际上并没有达到使用原型bean的目的。

解决方法

通过ApplicationContext上下文

引入ApplicationContext,每次调用方法时用上下文的getBean()方法去重新获取bean Fruit的实例。

修改代码如下:

@Component
public class FruitPlant {
    Fruit fruit;
    @Autowired
    private ApplicationContext context;

    public void showFruit() {
        fruit = this.getFruit();
        System.out.println(fruit);
        fruit.showFruitName();
    }
    public Fruit getFruit() {
        return this.context.getBean(Fruit.class);
    }
}

通过@LookUp注解

修改代码:

@Component
public abstract class FruitPlant {
    Fruit fruit;

    public void showFruit() {
        fruit = this.getFruit();
        System.out.println(fruit);
        fruit.showFruitName();
    }
    /**
     * 此方法调用时会被FruitPlant的CGLIB代理对象的
     * CglibSubclassingInstantiationStrategy-LookupOverrideMethodInterceptor拦截器的intercept()方法拦截
     * 其中的createBean()流程,会根据这个bean是singleton还是prototype选择从缓存中取还是重新创建新的bean返回
     */
    @Lookup
    public abstract Fruit getFruit();
}

输出结果如下:

com.modules.test.Apple@1d483de4
fruit Name is apple
com.modules.test.Apple@4032d386
fruit Name is apple

xml形式的lookup-method

xml形式是@LookUp出来之前的,以配置的方式进行设置的方法

<?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">
        <!-- ====================replace-method属性注入==================== -->
        <bean id="apple" class="com.modules.test.Apple" scope="prototype"/>
        <bean id="fruitPlant" class="com.modules.test.FruitPlant">
            <lookup-method name="getFruit" bean="apple" />
        </bean>
</beans>
public class ProcessorMain {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("TestOverrideMethod.xml");
        //从上下文中获取FruitPlant对象
        FruitPlant fruitPlant = (FruitPlant) context.getBean("fruitPlant");
        //第一次显示FruitPlant里的Fruit
        fruitPlant.showFruit();
        //第二次显示FruitPlant里的Fruit
        fruitPlant.showFruit();
    }
}

这种方式,lookup-menthod的信息是在AbstractBeanDefinition的MethodOverrides 属性中,通过@LookUp进行设置的,信息不是在MethodOverrides,而是在AnnotationMetadata中
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值