bean装配
Spring提供了三种主要的装配机制:
- 在XML中进行显式配置。
- 在Java中进行显式配置。
- 隐式的bean发现机制和自动装配
自动装配
Spring从两个角度来实现自动化装配:
- 组件扫描(component scanning):Spring会自动发现应用上下文
中所创建的bean。 - 自动装配(autowiring):Spring自动满足bean之间的依赖。
@Component
public class TestBean{
}
@Configuration
@ComponentScan
public class ConfigBean{
}
@ComponentScan开启组件扫描,如果没有其它配置默认会扫描与配置类相同的包及其子包。也可以是使用xml开启:
<contest:component-scan base-package="x.x.x"/>
使用spring创建一个测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=someclass.class)
public class Test{
@AutoWire
private TestClass test;
@Test
public void test(){
Assert.assertNotNull(test);
}
}
bean id
spring应用上下文会为所有的bean设定一个id,默认为类名的第一个字母小写。如果想自己设置,可以:
@Component("myid")
也可以:
Named("myid") public class myclass
扫描基础包
@Configuration
@ComponentScan(basePackages={"pacakge1","pacakge2"})
public class ConfigBean{
}
另外一种指定包扫描的方法为:
@ComponentScan(basePackageClasses={A.class,B.class})
这些类所在的包将会成为组件扫描的包。
自动装配
@Autowired注解可以用在构造器,Setter方法或者其它方法,spring会尝试满足方法参数上所声明的依赖。假如有且只有一个bean匹配,那么它将会被这个bean装配进来。如果没有则会抛出异常。为了避免异常,可以:
@Autowired(required=false)
如果有多个bean符合需求,也会抛出异常。
@Inject:java依赖注入规范定义的一个注解。也可以用来自动装配。
@Autowired和@Inject,@Resource
- @Autowired和@Inject
默认 autowired by type
可以 通过@Qualifier 显式指定 autowired by qualifier name。 - @Resource
默认 autowired by field name
如果 autowired by field name失败,会退化为 autowired by type
可以 通过@Qualifier 显式指定 autowired by qualifier name
如果 autowired by qualifier name失败,会退化为 autowired by field name。但是这时候如果 autowired by field name失败,就不会再退化为autowired by type了。 - @Autowired和@Inject基本是一样的,因为两者都是使用AutowiredAnnotationBeanPostProcessor来处理依赖注入。但是@Resource是个例外,它使用的是CommonAnnotationBeanPostProcessor来处理依赖注入。
- @Inject是jsr330规范的实现,而@Autowired是spring的实现,@Resource则是jsr250的实现。
显示装配bean
java方式:在添加了@Configuration注解的配置类中添加方法返回该bean:
@Configuration
public class CDPlayerConfig {
@Bean(name="xxxx")
public CompactDisc compactDisc() {
return new SgtPeppers();
}
//没有name属性的话bean的id默认为方法的名字
@Bean
public CDPlayer cdPlayer(CompactDisc compactDisc) {
return new CDPlayer(compactDisc);
}
}
可以使用多个注解类,如果一个注解类要用到其中的一个注解类的bean的话,可以使用@import导入.如果用到xml声明的bean,可以把相应的配置文件导入。
@Configuration
@Import(CDPlayerConfig.class)
@ImportResource("classpath:cd-config.xml")
public class SoundSystemConfig {
}
在xml中如何引用javaconfig配置的bean? 可以使用标签将配置类导入。就可使用了。也可以创建一个组合xml,将配置文件和配置类一起导入,即根配置,并在根配置中启动组件扫描。
<bean class="soundsystem.CDConfig" />
<bean id="cdPlayer"
class="soundsystem.CDPlayer"
c:cd-ref="compactDisc" />
创建一个根配置
<beans xmlns="">
<bean class="xxxConfig/>
<import resource="xxx.xml"/>
</beans>
条件化bean
@Bean
@Conditional(MagicExistsCondition.class)
public MagicBean magicBean() {
return new MagicBean();
}
```
传给该注解的类可以是任意实现了Condition接口的实现类。该接口根据matches返回的结果来决定是否实例化bean.
public inteface Condition{
boolean matches(ConditionContext ctx,AnnotatedTypeMedtadata metadata);
}
@Profile也使用了@Conditional注解,并且引用ProfileCondition作为Condition实现。
处理自动装配歧义性
当有多个bean满足装配的条件时,如何解决?
有多种方法:
1. 首选bean.使用@Primary注解。当使用xml时可指定的primary属性为true.
2. 使用@Qualifier(“beanid”)指定bean的id.标识唯一性。如果没有则会使用默认的限定符命名方式。在bean声明上添加@Qualifier注解。例
如,它可以与@Component组合使用.
@Component
@Qualifier("xxxx")
public class testBean{
}
spring bean的常见作用域
- 单例(Singletion)
- 原型(Prototype)
- 会话(Session)
- 请求(Request)
声明作用域:
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
使用会话和请求作用域
@Component
@Scope{
value=WebApplicationContext.SOCPE_SESSION,
proxyMode=ScopedProxyMode.INTERFACES)
}
public ShoppingCart cart(){...}
proxyMode解决了这样一个问题,:将会话范围内的bean注入到单例bean中时,如果用户没有请求系统会话bean不存在或多个用户就会有多分session bean。使用proxyMode便会注入一个代理,当真正调用会话bean的方法时,代理会对其进行懒解析并将调用委托
给会话作用域内真正的ShoppingCart bean。,proxyMode属性被设置成了
ScopedProxyMode.INTERFACES,这表明这个代理要实现
ShoppingCart接口,并将调用委托给实现bean。如果ShoppingCart是类的话,要将proxyMode属性设置
为ScopedProxyMode.TARGET_CLASS,以此来表明要以生成目标
类扩展的方式创建代理。
在xml中声明作用域代理
<bean id="cart" class="com.myapp.ShoppingCart" scope="session">
<aop:scoped-proxy/>
</bean>
默认情况下,它会使用CGLib创建目标类的代理。但是我们也可
以将proxy-target-class属性设置为false,进而要求它生成基
于接口的代理:
运行时值注入
声明属性源并通过spring的Enviroment来检索属性:
@Configuration
@PropertySource("classpath:xxx.properties")
public class Config{
@Autowired
Enviroment ev;
@Bean
public Person person(){
return new Person(env.getProperty("name"),evn.getProperty("id"));
}
}
在java配置或xml配置中也可使用占位符的形式。
如果使用组件扫描和自动装配,则用@Value注解
public Person(@Value("${person.id}")String id,
@Value("${person.name}")String name){
this.id=id;
this.name = name;
}
为了使用占位符,必须配置一个PropertySourcePlaceholderConfigurer.
@Bean
public static PropertySourcePlaceholderConfigurer placeholderConfigurer(){
return new PropertySourcePlaceholderConfigurer();
}
xml中的会为你生成PropertySourcePlaceholderConfigurer.
junit基于控制台的输出编写断言:
@Rule
public final StandardOutputStreamLog log = new StandardOutputStreamLog();
@Test
public void test(){
assertEquals("some log",log.getLog());
}