Spring中单例Bean与原型Bean的区别
1 单例Bean和原型Bean分别是什么?
在面向对象编程中,单例是指当前进程、当前生命周期中只存在一个对象实例(主观),单例Bean就是某个Bean始终是单例,由Spring IOC容器维护,不管是使用@AutoWired
还是@Resource
注解,注入的对象都是容器实例化的同一个对象。
单例Bean默认是在加载配置文件或配置类时创建,但可以给@Componenet
标记的类以及@Bean
标记的配置方法上机上@Lazy
上实现懒加载,即第一次注入时实现对象的创建。
原型Bean即每次注入时都会创建一个对象,即使是调用同一个方法,不同位置、时间调用,每一次调用也会注入新的对象。
2 定义单例Bean和原型Bean
2.1 在配置中定义
@Configuration
public class MyConfiguration {
@Bean
public MySingletonBean mySingletonBean() {
return new MySingletonBean();
}
@Bean
@Scope("prototype")
public MyPrototypeBean myPrototypeBean() {
return new MyPrototypeBean();
}
}
mySingletonBean
是单例Bean,myPrototypeBean
是原型Bean。
2.2 @Component
注解定义
定义单例Bean
@Component
public class User {
private int id;
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void test() {
System.out.println(this.hashCode());
}
}
定义原型Bean
@Component
@Scope(value = BeanDefinition.SCOPE_PROTOTYPE)
public class User {
private int id;
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void test() {
System.out.println(this.hashCode());
}
}
3 @Autowired注入原型Bean的特殊方法
3.1 使用@Scope
注解proxyMode
属性解决
- ScopedProxyMode.TARGET_CLASS 代理目标类,也就是使用
CGLIB
完成动态代理。 - ScopedProxyMode.TARGET_CLASS 代理目标类的所有接口,使用
JDK
动态代理去完成代理。
@Scope(value = BeanDefinition.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
3.2 使用@Lookup
方式解决
使用@Lookup
注解需要配置在返回类型为具体类型的方法上
@Lookup
public User getUser() {
return this.user;
}
调用
for (int i = 0; i < 5; i++) {
System.out.println(getUser());
}
3.3 getBean手动获取原型Bean
public User getUser() {
return beanFactory.getBean(User.class);
}
实现BeanFactoryAware或者是ApplicationContextAware接口去注入BeanFactory,手动在getter当中去进行getBean
3.4 使用TargetSource配置获取原型Bean
TargetSource,从这个名字当中我们可以知道,它是一个用来获取Target的Source,也就是一个用来获取"目标对象"的"源"。对于如何获取Target的Source,这当然是可以自定义的,这也是Spring留给我们的扩展点。使用TargetSource需要配置Spring容器中默认的执行SpringAOP的AbstractAutoProxyCreator组件的相关内容,我们如何去进行配置?我们可以手写一个BeanPostProcessor去拦截下来AbstractAutoProxyCreator的执行,然后去进行配置。需要注意的是:我们既然想对AbstractAutoProxyCreator进行拦截,那么我们肯定得优先级比它还高从而去保证比这个组件在创建时,AbstractAutoProxyCreator这个组件还没完成创建!因此我们这里采用了实现PriorityOrdered接口,去保证优先级比较高。我们类似编写如下的一个配置类:
@Configurationpublic class Config implements BeanPostProcessor, PriorityOrdered, BeanFactoryAware {
BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
AbstractBeanFactoryBasedTargetSource targetSource = new AbstractBeanFactoryBasedTargetSource() {
@Override
public Object getTarget() throws Exception {
return getBeanFactory().getBean(getTargetBeanName());
}
};
if (bean instanceof AbstractAutoProxyCreator) {
AbstractBeanFactoryBasedTargetSourceCreator creator = new AbstractBeanFactoryBasedTargetSourceCreator() {
@Override
protected AbstractBeanFactoryBasedTargetSource createBeanFactoryBasedTargetSource(Class<?> beanClass, String beanName) {
if (User.class.isAssignableFrom(beanClass)) {
return targetSource;
}
return null;
}
};
creator.setBeanFactory(beanFactory);
((AbstractAutoProxyCreator) bean).setCustomTargetSourceCreators(creator);
}
return bean;
}}
我们主要通过setCustomTargetSourceCreators方法往AbstractAutoProxyCreator组件当中添加一个自定义的TargetSourceCreator。这个TargetSourceCreator组件的作用是根据自定义相关的逻辑,最终返回一个AbstractBeanFactoryBasedTargetSource类型的TargetSource对象,它是一个典型的工厂模式!对于我们想要实现的AbstractBeanFactoryBasedTargetSource组件,我们只需要实现它的getTarget方法即可,这个getTarget方法主要体现之前所讲述的那个词,"目标对象"的"源",“源”是从什么地方体现的呢?就是在这里,我们可以自定义逻辑获取Bean!但是这里的getBeanFactory().getBean(getTargetBeanName()),其实比较有意思,这个getBeanFactory获取到的并不是我们Spring的IOC容器,而是一个克隆出来的新的BeanFactory。
发表回复