原创

Spring自动装配引发的一个问题

遇到的问题

我们知道Spring在注册bean的时候,默认单例的,但是在实际开发中(很少),我们使用的这个bean可能会给先给它设置初始的值,然后再去使用它,这个时候就会引发一个问题,如下代码

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

@Service
public class IndexService {
    @Autowired  
    private IndexDao indexDao;

    void test() {
        indexDao.setName("aaaa");//这里我们设置了一个名字
        System.out.println("service");
        indexDao.test();
    }
    void test1() {
        //但是这里我们不需要设置,如果设置了将会导致程序出错。
        System.out.println("service");
        indexDao.test();
    }
    //也就是说,我们每次使用indexDao的时候我们需要的都是新的,需要重新new一个,而不是spring给我创建的单例bean。
    //这里使用的IndexService生命的是单例,所以indexDao只能赋值一次,这样产生的问题就是每次拿到的indexDaO都是同一个,和我们的预期不同。

}

我们看看官方是怎么说的
官方文档 ,下面摘自官方文档

1.4.6. Method Injection
In most application scenarios, most beans in the container are singletons. When a singleton bean needs to collaborate with another singleton bean or a non-singleton bean needs to collaborate with another non-singleton bean, you typically handle the dependency by defining one bean as a property of the other. A problem arises when the bean lifecycles are different. Suppose singleton bean A needs to use non-singleton (prototype) bean B, perhaps on each method invocation on A. The container creates the singleton bean A only once, and thus only gets one opportunity to set the properties. The container cannot provide bean A with a new instance of bean B every time one is needed.

大致意思是

在大多数应用程序场景中,大多数bean容器是单例的。当一个单例的bean需要与另一个单例bean或单体bean需要与另一个单体bean,您通常处理依赖通过定义一个bean的属性。问题是当bean的生命周期不同。假设单例bean需要使用单体(原型)的bean B,每个方法调用的容器创建单例bean只有一次,因此只得到一个机会来设置属性。容器不能每次都给bean提供一个新实例bean B。

解决的方法

官方提供了两种解决方案

官方文档往下拉,就能找到两种方式。

第一种

将上面讲到的bean B声明为原型 @Scope("prototype")
然后再bean中声明一个返回
下面是我的测试代码

package dao;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;

@Repository
@Scope("prototype")//声明此类为原型
public class IndexDaoImp implements IndexDao {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void test() {
        System.out.println("dao1");
    }
}
package dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class IndexService {//第二种写法,需要添加abstract变成抽象类
    @Lookup("indexDaoImp")//思考这里为什么要写indexDaoImp?
    protected IndexDao createIndexDao() {
        return null;
    }
    //第二种写法,官方写法
    // @Lookup("indexDaoImp")
   // protected abstract  IndexDao createIndexDao() ;

    @Autowired
    private IndexDao indexDao;//思考:这里为什么写成这样也能将indexDao注入,而不写成indexDaoImp?

    void test() { 
        System.out.println("service test:"+createIndexDao());//每次打印出来都不一样
        System.out.println("service test:"+indexDao);//每次打印出来都一样
        indexDao.test();
    }
    void test1() {
        System.out.println("service test1:"+createIndexDao());//每次打印出来都不一样
        System.out.println("service test1:"+indexDao);//每次打印出来都一样
        indexDao.test();
    }


}

第二种

因为不适合,所以略…:joy: :joy:
github:https://github.com/Wnghao/Spring-ioc
至此,问题结束!:smirk: :smirk: :smirk:

正文到此结束(点击广告是对作者最大的支持)