2.1 Spring 配置的可选方案
- 在XML中进行显式配置
- 在Java中进行显式配置
- 隐式的bean发现机制和自动装配
JavaConfig比XML更加强大且类型安全
2.2 自动化装配bean
自动装配的过程
- 组件扫描(component scanning):Spring会自动发现应用上下文中创建的bean
- 自动装配(autowiring):Spring自动满足bean之间的依赖
使用java配置开启组件扫描
/** @Configuration注解说明是一个配置类 @ComponentScan开启组件扫描(默认扫描当前配置类所在包) */ @Configuration @ComponentScan public class JavaDemoConfig{...}
使用@ContextConfiguration(classes=JavaDemoConfig)可以加载JavaDemoConfig配置
使用XML配组文件开启组件扫描
<context:component-scan base-package="soundsystem"/>
2.2.2 为组件扫描的Bean命名
@Component(“组件id”),如果没有声明组件id,那么Spring会通过自动为该组件注册一个id,将该类类名第一个字母变为小写。
也可以使用@Named(“组件id”)进行声明,不常见。
2.2.3 设置组件扫描的基础包
@ComponentScan默认扫描当前类所在的包。
@ComponentScan(basePackages="soundsystem")
指定扫描"soundsystem"包,当然如果需要扫描多个包,写成
@ComponentScan(basePackages={"soundsystem","video"})
上面这种方式是使用String类型表示包名,如果代码重构后包名发生了变化,就会出现错误。
@ComponentScan提供了另外一种方法,就是指定包中所包含的类或者接口:
@ComponentSacn(basePackageClasses={CDPlayer.class,DVDPlayer.class})
当在注解中声明了类之后,Spring会将该类所在的包作为扫描的对象。(可以手动添加一个空的接口专门为标记对象,避免引用实际需要的类,因为这些类以后代码重构可能不存在)
2.2.4 通过为bean添加注解实现自动装配
@Autowired可以使用在构造器、Setter方法甚至类的任何方法上。
/** 普通方法使用@Autowired */ @Autowired public void setCompactDisc(CompactDisc cd){ this.cd = cd; }
当应用上下文创建的时候,Spring会去寻找对应的bean,如果没有找到对应的bean,那么会报错。可以将@Autowired的required属性设置为false。
/** 普通方法使用@Autowired */ @Autowired(required=false) public void setCompactDisc(CompactDisc cd){ this.cd = cd; }
这样使用之后,当没有对应的bean时,Spring不会报错,但是这个bean可能会出现空指针异常。
@Autowired是Spring特有的注解。也可以替换成java依赖注入的规范@Inject,大部分情况下他们是可以互相替换。
2.3 通过Java代装配Bean
有时候自动装配行不通(比如你要将第三方的组件配置到你的代码中,不可能在它的类上加上@Autowired注解)时,就需要显式配置Bean
JavaConfig是配置代码,应当没有任何业务逻辑,可以将它放入单独的包里面,就像XML配置文件一样和业务分开。
2.3.1 创建配置类
@Configuration注解表明该Java类是一个配置类。
@ComponentScan开启组件扫描
2.3.2 声明简单的bean
在JavaConfig中创建一个方法,然后创建所需类型的实例,最后为这个方法添加@Bean注解。
@Bean public CompactDisc sgtPeppers(){ return new SgtPeppers(); }
默认情况下,bean的ID与@Bean注解的方法名是一样,上面例子的Bean的名字就会是sgtPeppers。如果想指定成其他的名字,在@Bean注解的name属性中声明。
@Bean(name="lonelyHeartsClubBand") public CompactDisc sgtPeppers(){ return new SgtPeppers(); }
可以玩一些更有意思的操作。
@Bean public CompactDisc randomBeatlesCD(){ int choice = (int)Math.floor(Math.random()*4); switch (choice){ case 0:return new SgtPeppers(); case 1:return new WhiteAlbum(); case 2:return new HardDaysNight(); case 3:return new Revolver(); } }
2.3.3 借助JavaConfig实现注入
@Bean public void cdPlayer(){ return new CDPlayer(sgtPeppers()); }
上面这个例子中创建了一个Bean,名字叫做cdPlayer,在这个方法返回的Bean的实例中,在构造器中使用sgtPeppers()方法传入了一个CompactDisc的对象。
看上去CompactDisc是通过调用sgtPeppers()这样一个方法来得到的。实际上sgtPeppers()由于声明了@Bean注解,Spring将会拦截所有对该方法的调用,保证直接返回该方法创建的bean,并不是每次都对其进行了实际的调用。
因为Spring中的Bean都是单例的,所以Spring会拦截sgtPeppers()方法,然后返回同一个已经创建完成的SgtPeppers实例。
除了调用方法注入外,当然也可以这么写:
@Bean
public CDPlayer cdPlayer(CompactDisc compactDisc){
return new CDPlayer(compactDisc);
}
这里Spring会在cdPlayer创建的时候,将CompactDisc以自动装配的形式注入到cdPlayer的构造器中。这种方法通常较好,它不要求COmpactDisc和cdPlayer声明在同一个配置类中,甚至没有要求CompactDisc在JavaCon中声明(可以将配置分散到多个配置类、XML文件,或者通过自动扫描来装配)
2.4通过XML装配Bean
2.4.1创建XML配置规范
最简单的SpringXMl配置如图所示:
<beans></beans>是整个配置文件的根元素。
首先需要声明顶部的多个XML模式(XSD)文件,这些文件定义了配置文件中的XML元素。
2.4.2声明一个简单的<bean>
<bean>类似于JavaConfig中的@Bean注解,它声明了一个Bean。
<bean class="soundsystem.SgtPeppers"/>
也可以为它指定一个id
<bean id="sgtPeppers" class="soundsystem.SgtPeppers"/>
如果没有为它指定id,Spring会根据全限定类名来进行命名。如"soundsystem.SgtPeppers#0",当你又声明了一个SgtPeppers,并且没有指定id,那么Spring会为他自动创建一个id:“soundsystem.SgrPeppers#1”。
自动生成在引用时可能不太便利,建议主动为bean声明id。
XML配置和JavaConfig相差在哪里
- 在XML中,bean是由Spring调用该类的构造器自动生成的实例。但是在JavaConfig中你可以用任何方式返回一个实例
- 在XML中,bean的class属性是用字符串的形式表示的。(但是IDE的强大确保你能够检查到XML文件的合法性)
- (XSD文件的引入不小心就引入错误的,导致冲突)
2.4.3借助构造器注入初始化Bean
有两种可选的构造器注入的方案:
- <constructor-arg>元素
- 使用Spring3.0所引入的c-命名空间
<constructor-arg>比c-命名空间更加冗长,但是有些事情<constructro-arg>可以做
到,但是c-命名空间做不到。
这里使用了<constructor-arg>为csPlayer这个Bean注入了另外一个Bean-compactDisc的引用。
<bean id="cdPlayer" class="soundsystem.CDPlayer">
<constructor-arg ref="compactDisc" />
</bean>
如果使用c-命名空间:
<bean id="csPlayer" class="soundsystem.CDPlayer" c:cd-ref="compapctDisc">
c-命名空间的规则:
这里的构造器参数指明了cd,如果cd这个参数名称发生了变化,就会出现错误,所以可以将cd换成_0、_1这样指定构造器的顺序,如果只有一个参数,甚至可以只用一个下划线_来表示。(xml不允许直接数字开头,所以加下划线)
<bean id="csPlayer" class="soundsystem.CDPlayer" c:_-ref="compapctDisc">
前面都是诸如对象的引用,现在来试试装配字面量。
首先在一个类中有这样的构造方法。
public BlankDisc(String title,String artist){
this.titile = title;
this.artist = artist;
}
然后在XML中配置:
<bean id="compactDisc" class="soundsystem.BlankDisc">
<constructor-arg value="Sgt.Peppers's Lonely Hearts Club Band"/>
<constructor-arg value="The Beatles"/>
</bean>
如果换成使用c-命名空间:
<bean id="compactDisc" class="soundsystem.BlankDisc"
c:_0="Sgt.Peppers's Lonely Hearts Club Band"
c:_1=The Beatles"/>
</bean>
装配集合
如果一个类的构造函数需要传入集合,假设他的构造函数如下:
public BlankDisc(String title,String artist,List<String> tracks){
this.titile = title;
this.artist = artist;
this.tracks = tracks;
}
那么你可以在xml这么设置:
<bean id="compactDisc" class="soundsystem.BlankDisc">
<constructor-arg value="Sgt.Peppers's Lonely Hearts Club Band"/>
<constructor-arg value="The Beatles"/>
<constructor-arg>
<list>
<value>...</value>
<value>...</value>
<value>...</value>
<value>...</value>
<!-- ... -->
</list>
</constructor-arg>
</bean>
c-命名空间目前还不能使用集合装配
2.4.4设置属性
使用进行属性设置。
<bean id="cdPlayer" class="soundsystem.CDPlayer">
<property name="compactDisc" ref="compactDisc"/>
</bean>
同样的,Spring为属性设置提供了一个p-命名空间,所以也可以这么写
<bean id="cdPlayer" class="soundsystem.CDPlayer"
p:compactDisc-ref="compactDisc"/>
p-命名空间的规则
同样的p-也不支持注入集合。如果你不想在Bean中定义太长,你也可以使用Spring util-命名空间来简化Bean的定义
<util:list id="trackList">
<value>...</value>
<value>...</value>
<value>...</value>
<!-- ... -->
</util:list>
这时候你可以将这个list的id像其他bean一样用ref进行注入。
util-命名空间的元素
元素 | 描述 |
---|---|
util:constant | 引用某个类型的public static域,并将其暴露为bean |
util:list | 创建一个java.util.List类型的bean,其中包含值或引用 |
util:map | 创建一个java.util.Map类型的bean,其中包含值或引用 |
util:propertise | 创建一个java.util.Properties类型的bean |
util:property-path | 引用一个bean的属性(或者内嵌属性),将其暴露为bean |
util:set | 创建一个java.util.Set类型的bean,其中包含值或者引用 |
2.5 导入和混合配置
2.5.1 在JavaConfig中引入XML配置
假如现在有两个JavaConfig,如果将两个配置整合?
- 在一个JavaConfig上@Import(xxx.class)导入另外一个JavaConfig
- 创建一个更高级的JavaConfig,然后@Import({xxx.class,xxx.class})将他们整合
如果有一个是XML配置文件的话。
使用@ImportResource(“classpath:xxx.xml”)整合类路径下的XML配置文件。
2.5.2 在XML配置中引用JavaConfig
加入现在有两个XML配置文件,如何将两个配置整合?
使用XML的元素引用另外的文件,和@ImportResource注解一样同样是使用类路径。
如何整合XML和JavaConfig?
直接在XML文件中将JavaConfig声明成一个Bean
<bean class="soundsystem.CDConfig"/>