spring中bean的作用域:
众所周知,在Spring容器中,bean的scope默认是singleton单例的。
如果在singleton的bean中依赖了prototype的bean,那么会出现下面的问题,原型的bean每次获取的都是同一个对象。
看一个例子:
1)单例Bean:
package com.along.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
/**
* scope默认是singleton
*/
@Repository
public class OrderDao {
@Autowired
private IndexDao indexDao;
//这里分别打印当前orderDao的hashCode和它所依赖indexDao的hashCode
public void order(){
System.out.println("orderDao:"+this.hashCode());
System.out.println("indexDao:"+indexDao.hashCode());
}
}
2)原型Bean:
package com.along.dao;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Repository;
@Repository
@Scope("prototype")
public class IndexDao {
public void test(){
System.out.println("IndexDao#test()");
}
}
3)测试:
package com.along.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.along")
public class AppConfig {
public static void main( String[] args ) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(AppConfig.class);
context.refresh();
OrderDao orderDao = (OrderDao) context.getBean("orderDao");
orderDao.order();
orderDao.order();
context.close();
}
}
运行:
orderDao:1513712028
indexDao:1018547642
orderDao:1513712028
indexDao:1018547642
原因:因为OrderDao只实例化了一次,那么他只有一次机会为他的依赖IndexDao去设置属性,由于OtderDao是单例的,所以没办法再为IndexDao去设置属性。
解决方法:
在spring官网上介绍了两种解决方法:https://docs.spring.io/spring/docs/5.0.14.RELEASE/spring-framework-reference/core.html#beans-factory-method-injection
在大多数应用程序场景中,容器中的大多数bean都是单例的。当一个单例bean需要与另一个单例bean协作,或者一个非单例bean需要与另一个非单例bean协作时,通常通过将一个bean定义为另一个bean的属性来处理依赖关系。当bean的生命周期不同时,就会出现问题。假设单例bean A需要使用非单例(原型)bean B,可能是在A上的每个方法调用上。容器只创建单例bean A一次,因此只有一次机会设置属性。容器不能每次需要bean B的新实例时都向bean A提供一个。
1)实现ApplicationContextAware接口:
您可以通过实现applicationcontext - ware接口,以及在bean A每次需要bean B实例时对容器发出getBean(“B”)调用来让bean A知道容器。
package com.along.dao;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Repository;
@Repository
public class OrderDao implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Autowired
public IndexDao indexDao;
public void order(){
System.out.println("orderDao:"+this.hashCode());
indexDao = (IndexDao) applicationContext.getBean("indexDao");
System.out.println("indexDao:"+indexDao.hashCode());
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
实现 ApplicationContextAware 接口的,并重写setApplicationContext方法(),并通过applicationContext获取bean,每次获取到的就是不同的bean。
这个是第一种实现方法,但是这个严重依赖于Spring的API,侵入性太强。
2)Lookup注解:
package com.along.dao;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.stereotype.Repository;
@Repository
public abstract class OrderDao {
@Lookup("indexDao")
public abstract IndexDao getIndexDao();
public void order(){
System.out.println("orderDao:"+this.hashCode());
System.out.println("indexDao:"+getIndexDao().hashCode());
}
}