声明Bean
在spring容器中,声明bean有两种方式,一种是@Bean,另一种是@Component注解(包括@Service、@Controller、@Repository、@Configuration)。
- 对于@Bean注解,如果指定了name参数,以参数为准;如果未指定name参数,会以方法名作为bean的名称。
- 对于@Component注解,如果指定了参数,以参数为准;如果未指定参数,会以类名作为bean的名称;如果类名开头是两个或者两个以上大写字母,bean名同类名完全一致;如果开头只有一个大写字母,bean名是类名首字母小写版。
Bean的作用域
- Scope(“singleton”):单例模式,即每次调用将使用同一个bean实例,spring的默认scope。
- Scope(“prototype”):原型模式,每次获取这个bean都会创建一个新的实例。
- Scope(“request”):在web应用中,给每个请求创建一个bean实例。
- Scope(“session”):在web应用中,给每个会话创建一个bean实例。
例子:
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Test { ... }
这里使用了ConfigurableBeanFactory.SCOPE_PROTOTYPE,当然也可以使用“prototype”。
下面说一下Scope为session的例子:
@Component
@Scope(
value=WebApplicationContext.SCOPE_SESSION,
proxyMode=ScopedProxyMode.INTERFACES
)
public class TestImpl implements Test { ... }
@Service
public class T {
@Autowired
private Test test;
}
因为T是一个单例的bean,但是,Test是会话类型的,所以T中要调用会话对应的bean,如何指定呢?这时proxyMode就派上用场了。ScopedProxyMode.INTERFACES表明这个的代理要实现Test接口(必须是接口),并将调用委托给bean实现。作用域代理能够延迟注入请求和会话作用域的bean。
流程图: 单例bean <— 接口(作用域代理) <— 接口(会话、请求作用域的bean)
注意: 如果是类的话必须使用CGLib来生成基类的代理,并把proxyMode属性设置成ScopedProxyMode.TARGET_CLASS,表明要以生成目标类扩展的方式创建代理。
Bean的线程安全问题
- singleton:是无状态的bean,解决线程安全问题就是bean里不会定义有状态的变量,如果必须有成员变量,可以用ThreadLocal修饰。
- prototype:每次都是一个新bean,显然没有线程安全问题
- request和session对象:除非显示的使用多线程,需要注意一下线程安全问题,在大多数情况,bean都是线程安全的。
Bean对象初始化钩子
@PostConstruct:会在bean初始化前调用,@PreDestroy:会在bean销毁前调用,其实@Bean注解有initMethod和destroyMethod属性。
Bean注入
使用@Autowired注解。@Autowire(required = false)表名被注入对象能为null。
如果一个服务有多个实现,spring是不能识别出该注入那个实现的,这时@Primary和@Qualifer注解就派上用场了。
@Primary标志着是首选bean,也就是当service有多个实现时,没有指明注入那个时,默认选择有@Primary注解所在Service。
@Qualifer指定选择的bean的id