这是一道迷幻题,既不能回答是,也不能回答不是。
先回忆一下在Spring中是如何创建一个bean的?通过添加@Controller、@Service、@Repository 、@Component等注解在一个类上,添加@Bean在一个方法上...还有呢?
创建一个单例、多例的Bean呢?
默认就是单例的bean,要声明多例的通过@Scope注解进行配置。
如果是一个多例Bean,我们还需要考虑线程安全吗?
当然不需要。如果是一个单例Bean,我们一定要考虑线程安全吗?这,,,不一定吧!
再来看看@Controller注解的源码:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any (or empty String otherwise)
*/
@AliasFor(annotation = Component.class)
String value() default "";
}
@Service的源码:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any (or empty String otherwise)
*/
@AliasFor(annotation = Component.class)
String value() default "";
}
Repository 的源码:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any (or empty String otherwise)
*/
@AliasFor(annotation = Component.class)
String value() default "";
}
@Component的源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any (or empty String otherwise)
*/
String value() default "";
}
@Controller和@Service、@Repository最终都指向了@Component。@Component注解中有没有关于线程安全的?没有,它们几个并没有什么不同。接下来看看@Bean的源码:
/**
* bean注解
* 作用在方法上
*/
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
/**
* bean的value,别名name
* @return
*/
@AliasFor("name")
String[] value() default {};
/**
* bean的name,别名value
* @return
*/
@AliasFor("value")
String[] name() default {};
/**
* 是否自动装配默认false,已过期忽略不计
* @return
*/
@Deprecated
Autowire autowire() default Autowire.NO;
/**
* 自动装配到其他bean的候选,默认true
* @return
*/
boolean autowireCandidate() default true;
/**
* 初始化方法,默认空
* @return
*/
String initMethod() default "";
/**
* 销毁方法,默认 AbstractBeanDefinition.INFER_METHOD
* @return
*/
String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
}
@Bean可用于方法上,它的name是value,value既是name。@Bean("xxx") 声明一个名为xxx的bean。initMethod是bean初始化时,所要执行的方法。同样也没有关于线程安全的。@Bean和以上几个注解的不同之处就是@Bean是作用在方法上的。而@Controller、@Service、@Repository、@Compoment是作用在类上的。再看下@Scope的源码:
/**
* Scope注解
* 作用在类/接口/枚举、方法上
* @Date
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {
/**
* 等同于 scopeName
* @see #scopeName
*/
@AliasFor("scopeName")
String value() default "";
/**
* 默认单例
* {@link ConfigurableBeanFactory#SCOPE_SINGLETON SCOPE_SINGLETON}.
* @since 4.2
* @see ConfigurableBeanFactory#SCOPE_PROTOTYPE 原型,每次创建一个新对象
* @see ConfigurableBeanFactory#SCOPE_SINGLETON 单例,默认作用域
* @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST 每次http请求创建一个对象
* @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION 同一个会话共享一个实例
* @see #value
*/
@AliasFor("value")
String scopeName() default "";
/**
* 代理模式,默认不需要
* @see ScopedProxyMode
*/
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
}
@Scope,默认单例(一个spring容器中只创建一个实例),可设置原型(每次创建一个实例)、http请求(一个http请求一个实例)、会话(一个会话一个实例)。这个注解只是声明在Spring容器中实例化的数量,并没有关于线程安全的任何代码。多个实例,每个线程中的实例互不影响,因此多实例的bean是不需要考虑线程安全的。单例Bean又分为两种情况,无状态的bean和有状态的bean。无状态的bean也不需要考虑线程安全,比如service、controller、dao这些都是无状态的bean。有状态的bean,Spring官方提供的bean,通过ThreadLocal去解决线程安全的方法,比如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等,可以看看它们的源码。因此在我们的项目里,如果涉及到有状态的单例Bean,单例bean中有成员变量的,可考虑使用ThreadLocal来保存单例中的成员变量,使线程之间的状态隔离,互不影响,从而达到线程安全的目的。
最后总结Spring容器中的Bean是否线程安全,其实和Sping容器是没有什么关系的。Spring容器只负责创建和管理Bean,并未提供Bean的线程安全策略。因此,Spring容器中的Bean并不具备线程安全的特性。Spring中的Bean又可分为多例bean和单例bean,单例bean又可分为无状态的bean和有状态的bean。多例bean和无状态的bean是不需要考虑线程安全的。只有有状态的单例bean是需要考虑线程安全的,可使用ThreadLocal来管理成员变量,使之线程隔离,以达到线程安全的目的。
网友评论