《Spring in action 4》(二)装配Bean
装配Bean
莫道君行早 更有早行人
Spring装配Bean的三种方式
- 在XML中进行显示配置
- 在Java中进行显示配置
- 隐式的Bean发现和自动装配
- 组件扫描:Spring会自动发现应用上下文所创建的Bean
- 自动装配:Spring自动满足Bean之间的依赖
尽可能地的使用自动配置的机制。显示的配置越少越好,当你必须要显示配置Bean的时候(比如:有些源码不是由你来维护的,而当你需要为这些代码配置的时候),推荐使用类型安全(因为在Java配置中可以利用编译器检查)并且比XML更加强大的JavaConfig。最后,只有当你想要使用便利的XML命名空间,并且在JavaConfig中没有同样的实现时,才应该使用XML。
下面利用一个案例
手机和手机电池是一个依赖关系,在正常情况下,如果手机离开了手机电池,那么其实也不会起到什么作用,故以此为一个案例展开Spring Bean的装配。
手机接口:
/**
* 案例分析:
* 手机是依赖于手机电池存在的。
*/
public interface MobilePhone {}
电池接口:
/*电池接口*/
public interface Battery {}
手机实现类:
public class HuaweiMobilePhone implements MobilePhone {
private Battery battery;
public HuaweiMobilePhone(){}
public HuaweiMobilePhone(Battery battery){
this.battery = battery;
}
@Override
public String toString() {
return "HuaweiMobilePhone{" +
"battery=" + battery +
'}';
}
}
电池实现类:
public class HuaweiBattery implements Battery{
private String brand;
private String name;
public HuaweiBattery(){}
public HuaweiBattery(String brand, String name){
this.brand = brand;
this.name = name;
}
@Override
public String toString() {
return "HuaweiBattery{" +
"brand='" + brand + '\'' +
", name='" + name + '\'' +
'}';
}
}
XML显示配置
下面展示了基本的Bean注入方式,我们在上一篇其实已经接触到了。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="battery" class="com.ooyhao.spring.bean.HuaweiBattery"/>
<bean id="huaweiMobilePhone" class="com.ooyhao.spring.bean.HuaweiMobilePhone">
<constructor-arg name="battery" ref="battery"/>
</bean>
</beans>
使用Xml方式,测试类:
@Test
public void testDIXml(){
ClassPathXmlApplicationContext context
= new ClassPathXmlApplicationContext("phone.xml");
HuaweiMobilePhone bean = context.getBean(HuaweiMobilePhone.class);
System.out.println(bean);
}
Java显示配置
PhoneConfig 配置类:
public class PhoneConfig {
@Bean
public HuaweiBattery huaweiBattery(){
return new HuaweiBattery();
}
@Bean
public HuaweiMobilePhone huaweiMobilePhone(){
return new HuaweiMobilePhone(huaweiBattery());
}
}
使用注解,测试,如下:
@Test
public void testAutoWire(){
AnnotationConfigApplicationContext context
= new AnnotationConfigApplicationContext(PhoneConfig.class);
HuaweiMobilePhone phone = context.getBean(HuaweiMobilePhone.class);
System.out.println(phone);
}
自动发现和装配
使用Bean自动发现和装配的方式,则需要在Bean类上用@Component
注解进行标识。通过@CompentScan(basePackages="")
进行组件扫描。使用@AutoWired
注解进行自动装配。
手机实现类:
@Component
public class HuaweiBattery implements Battery{
private String brand;
private String name;
public HuaweiBattery(){}
public HuaweiBattery(String brand, String name){
this.brand = brand;
this.name = name;
}
@Override
public String toString() {
return "HuaweiBattery{" +
"brand='" + brand + '\'' +
", name='" + name + '\'' +
'}';
}
}
手机电池实现类:
@Component
public class HuaweiMobilePhone implements MobilePhone {
@Autowired
private Battery battery;
public HuaweiMobilePhone(){}
public HuaweiMobilePhone(Battery battery){
this.battery = battery;
}
@Override
public String toString() {
return "HuaweiMobilePhone{" +
"battery=" + battery +
'}';
}
}
Java配置类:
@ComponentScan(basePackages = "com.ooyhao.spring")
public class AutoWirePhoneConfig {}
测试类:
@Test
public void testAutoWire(){
AnnotationConfigApplicationContext context
= new AnnotationConfigApplicationContext(AutoWirePhoneConfig.class);
HuaweiMobilePhone phone = context.getBean(HuaweiMobilePhone.class);
System.out.println(phone);
}
总结:
- 创建应用对象之间的协作关系行为通常称为装配,这也是依赖注入(DI)的本质。
@ComponentScan
默认会扫描与配置类相同的包。组件扫描默认是不启动的。@ComponentScan
除了basePackages属性,还有basePackageClasses属性。@Bean
注解会告诉Spring这个方法将会返回一个对象。默认情况下,bean的ID与带有@Bean
注解的方法名一样。@component
默认是使用类名首字母小写作为Bean的id。当然也可以用其属性自定义指定。
属性注入
构造注入
XML配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="battery" class="com.ooyhao.spring.bean.HuaweiBattery">
<constructor-arg name="brand" value="Huawei"/>
<constructor-arg name="name" value="华为手机"/>
</bean>
<bean id="huaweiMobilePhone" class="com.ooyhao.spring.bean.HuaweiMobilePhone">
<constructor-arg name="battery" ref="battery"/>
</bean>
</beans>
测试实例不变,测试结果:
HuaweiMobilePhone{battery=HuaweiBattery{brand='Huawei', name='华为手机'}}
为了了解更多的类型注入,下面新增耳机接口和实现类,并将手机的实现类进行修改,如下:
耳机接口类:
/*耳机接口类*/
public interface EarPhone {}
耳机实现类:
public class HuaweiEarPhone implements EarPhone {
private String color;
private Double price;
public HuaweiEarPhone() {}
public HuaweiEarPhone(String color, Double price) {
this.color = color;
this.price = price;
}
@Override
public String toString() {
return "HuaweiEarPhone{" +
"color='" + color + '\'' +
", price=" + price +
'}';
}
}
手机类:增加手机颜色和耳机
public class HuaweiMobilePhone implements MobilePhone {
private Battery battery;
//手机颜色
private List<String> colors;
private Set<EarPhone> earPhones;
public HuaweiMobilePhone(){}
public HuaweiMobilePhone(Battery battery){
this.battery = battery;
}
public HuaweiMobilePhone(List<String> colors) {
this.colors = colors;
}
public HuaweiMobilePhone(Battery battery, List<String> colors){
this.battery = battery;
this.colors = colors;
}
public HuaweiMobilePhone(Battery battery,List<String> colors,
Set<EarPhone> earPhones) {
this.battery = battery;
this.colors = colors;
this.earPhones = earPhones;
}
@Override
public String toString() {
return "HuaweiMobilePhone{" +
"battery=" + battery +
", colors=" + colors +
", earPhones=" + earPhones +
'}';
}
}
修改XML配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="battery" class="com.ooyhao.spring.bean.HuaweiBattery">
<constructor-arg name="brand" value="Huawei"/>
<constructor-arg name="name" value="华为手机"/>
</bean>
<!--显示用name和value进行指定参数-->
<bean id="earPhone1" class="com.ooyhao.spring.bean.HuaweiEarPhone">
<constructor-arg name="color" value="白色"/>
<constructor-arg name="price" value="59.9"/>
</bean>
<!--通过默认的Index顺序,顺序不能错-->
<bean id="earPhone2" class="com.ooyhao.spring.bean.HuaweiEarPhone">
<constructor-arg value="黑色"/>
<constructor-arg value="60.5"/>
</bean>
<!--通过显示指定Index,顺序可以随意调换-->
<bean id="earPhone3" class="com.ooyhao.spring.bean.HuaweiEarPhone">
<constructor-arg index="1" value="66.6"/>
<constructor-arg index="0" value="粉色"/>
</bean>
<!--通过c-index命名空间-->
<bean id="earPhone4" class="com.ooyhao.spring.bean.HuaweiEarPhone"
c:_0="蓝色" c:_1="88.8"/>
<!--通过c-name命名空间-->
<bean id="earPhone5" class="com.ooyhao.spring.bean.HuaweiEarPhone"
c:color="绿色" c:price="90.8"/>
<bean id="huaweiMobilePhone" class="com.ooyhao.spring.bean.HuaweiMobilePhone">
<!--手机电池 引用构造注入-->
<constructor-arg name="battery" ref="battery"/>
<!--手机颜色 集合字面量注入-->
<constructor-arg>
<list>
<value>蓝色</value>
<value>红色</value>
<value>黑色</value>
</list>
</constructor-arg>
<!--手机耳机 集合Bean注入-->
<constructor-arg name="earPhones">
<set>
<ref bean="earPhone1"/>
<ref bean="earPhone2"/>
<ref bean="earPhone3"/>
<ref bean="earPhone4"/>
<ref bean="earPhone5"/>
</set>
</constructor-arg>
</bean>
</beans>
测试结果如下:
HuaweiMobilePhone{battery=HuaweiBattery{brand='Huawei', name='华为手机'}, colors=[蓝色, 红色, 黑色], earPhones=[HuaweiEarPhone{color='白色', price=59.9}, HuaweiEarPhone{color='黑色', price=60.5}, HuaweiEarPhone{color='粉色', price=66.6}, HuaweiEarPhone{color='蓝色', price=88.8}, HuaweiEarPhone{color='绿色', price=90.8}]}
Set注入
下面案例展示如何使用Set方法进行属性注入,所以需要将 手机实现类、电池实现类和手机耳机实现类均添加上set方法。(此处不展示了)
Xml配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="battery" class="com.ooyhao.spring.bean.HuaweiBattery">
<property name="brand" value="Huawei"/>
<property name="name" value="华为手机"/>
</bean>
<!--显示用name和value进行指定参数-->
<bean id="earPhone1" class="com.ooyhao.spring.bean.HuaweiEarPhone">
<property name="color" value="白色"/>
<property name="price" value="59.9"/>
</bean>
<!--通过p-name命名空间-->
<bean id="earPhone2" class="com.ooyhao.spring.bean.HuaweiEarPhone"
p:color="蓝色" p:price="88.88"/>
<bean id="huaweiMobilePhone" class="com.ooyhao.spring.bean.HuaweiMobilePhone">
<!--手机电池 引用set注入-->
<property name="battery" ref="battery"/>
<!--手机颜色 集合字面量注入-->
<property name="colors">
<set>
<value>蓝色</value>
<value>红色</value>
<value>黑色</value>
</set>
</property>
<!--手机耳机 集合引用类型注入-->
<property name="earPhones">
<list>
<ref bean="earPhone1"/>
<ref bean="earPhone2"/>
</list>
</property>
</bean>
</beans>
测试结果:
HuaweiMobilePhone{battery=HuaweiBattery{brand='Huawei', name='华为手机'}, colors=[蓝色, 红色, 黑色], earPhones=[HuaweiEarPhone{color='白色', price=59.9}, HuaweiEarPhone{color='蓝色', price=88.88}]}
注意:
可以使用<null/>
或者 <null></null>
标签可以将null注入。
set 和 list 的区别
<set>
和 <list>
元素的区别不大,其中最重要的不同在于当Spring创建要装配的集合时,所创建的时java.util.Set 还是 java.util.List。 如果时Set的话,所有重复值都会被忽略掉,存放顺序也不会得以保证。不过无论在哪种情况下,<set>
和 <list
都可以用来装配List和Set甚至时数组。
p 和 c-arg 区别
<property>
元素为属性的setter方法所提供的功能与 <constructor-arg>
元素为构造器所提供的功能时一样的。Spring为<Constructor-arg>
元素提供了c-命名空间作为替代方案,与之类似,Spring提供了更加简洁的p-命名空间,作为 <property>
元素的替代方案。
导入和混合配置
在实际开发中,也许时Xml显示配置和JavaConfig显示配置都存在,并且需要XML与XML相互引用,甚至的XML和JavaConfig交叉引用。所以,下面我们来看一下如何进行相互使用:
XML配置文件相互引用
手机实现类:
public class HuaweiMobilePhone implements MobilePhone {
private Battery battery;
//手机颜色
private List<String> colors;
public HuaweiMobilePhone(){}
public HuaweiMobilePhone(Battery battery){
this.battery = battery;
}
public HuaweiMobilePhone(List<String> colors) {
this.colors = colors;
}
public HuaweiMobilePhone(Battery battery, List<String> colors){
this.battery = battery;
this.colors = colors;
}
public Battery getBattery() {
return battery;
}
public void setBattery(Battery battery) {
this.battery = battery;
}
public List<String> getColors() {
return colors;
}
public void setColors(List<String> colors) {
this.colors = colors;
}
@Override
public String toString() {
return "HuaweiMobilePhone{" +
"battery=" + battery +
", colors=" + colors +
'}';
}
}
电池实现类:
public class HuaweiBattery implements Battery{
private String brand;
private String name;
public HuaweiBattery(){}
public HuaweiBattery(String brand, String name){
this.brand = brand;
this.name = name;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "HuaweiBattery{" +
"brand='" + brand + '\'' +
", name='" + name + '\'' +
'}';
}
}
电池XMl:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="huaweiBattery" class="com.ooyhao.spring.bean.HuaweiBattery">
<property name="brand" value="Huawei"/>
<property name="name" value="华为荣耀手机"/>
</bean>
</beans>
手机XML:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<import resource="Battery.xml"/>
<bean id="huaweiMobilePhone" class="com.ooyhao.spring.bean.HuaweiMobilePhone">
<!--手机电池 引用构造注入-->
<constructor-arg name="battery" ref="huaweiBattery"/>
<!--手机颜色 集合字面量注入-->
<constructor-arg>
<list>
<value>蓝色</value>
<value>红色</value>
<value>黑色</value>
</list>
</constructor-arg>
</bean>
</beans>
测试:
@Test
public void testRef(){
ClassPathXmlApplicationContext context
= new ClassPathXmlApplicationContext("phone.xml");
HuaweiMobilePhone bean = context.getBean(HuaweiMobilePhone.class);
System.out.println(bean);
}
//HuaweiMobilePhone{battery=HuaweiBattery{brand='Huawei', name='华为荣耀手机'}, colors=[蓝色, 红色, 黑色]}
在实际开发中,不可能将所有的配置信息写到一个XML文件中,所以存在相互引用,上述代码可以测试出,在xml文件
如何引用另外一个xml文件。一般,可以单独创建一个xml文件统一管理,如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="Battery.xml"/>
<import resource="phone.xml"/>
</beans>
此时,将手机xml文件中的<import resource="Battery.xml"/>
删除,而测试引用的使用引用合并的xml文件。如下:
@Test
public void testRef(){
ClassPathXmlApplicationContext context
= new ClassPathXmlApplicationContext("combineXml.xml");
HuaweiMobilePhone bean = context.getBean(HuaweiMobilePhone.class);
System.out.println(bean);
}
Java配置类相互引用
BatteryConfig配置类:
public class BatteryConfig {
@Bean
public HuaweiBattery huaweiBattery(){
return new HuaweiBattery("Huawei","华为手机电池");
}
}
PhoneConfig配置类:
@Import({BatteryConfig.class})
public class PhoneConfig {
@Bean
public HuaweiMobilePhone huaweiMobilePhone(Battery huaweiBattery){
List<String> colors = new ArrayList<String>();
colors.add("白色");
colors.add("黑色");
colors.add("粉色");
return new HuaweiMobilePhone(huaweiBattery,colors);
}
}
测试类:
@Test
public void testJavaConfigRef(){
AnnotationConfigApplicationContext context
= new AnnotationConfigApplicationContext(PhoneConfig.class);
HuaweiMobilePhone bean = context.getBean(HuaweiMobilePhone.class);
System.out.println(bean);
//HuaweiMobilePhone{battery=HuaweiBattery{brand='Huawei', name='华为手机电池'}, colors=[白色, 黑色, 粉色]}
}
上述代码可以看出,在JavaConfig配置类之间互相引用其实和Xml文件非常相似,在XML文件中是使用<import resource = 'aa.xml'
而在JavaConfig配置类中导入是使用@import({Config.class})
注解。通过导入的配置类的class类即可。当然,随着项目的不断扩大,JavaConfig配置类也会越来越多,这种方式来维护就会变得比较麻烦,我们还是可以通过使用统一的JavaConfig配置类来管理JavaConfig。如下:
@Import({
BatteryConfig.class,
PhoneConfig.class
})
public class CombineConfig {}
Java配置类中引用Xml配置文件
上面已经介绍完了Java配置如何引用Java配置,Xml配置如何引用Xml配置,并且如何将其分散的配置文件或配置类
进行统一的处理。下面我们看一下如何在Java配置类中引用Xml配置文件:
Xml配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="huaweiBattery" class="com.ooyhao.spring.bean.HuaweiBattery">
<property name="brand" value="Huawei"/>
<property name="name" value="华为荣耀手机"/>
</bean>
</beans>
在Java配置类中引入Xml配置文件信息:
@ImportResource({"Battery.xml"})
public class PhoneConfig {
@Bean
public HuaweiMobilePhone huaweiMobilePhone(Battery huaweiBattery){
List<String> colors = new ArrayList<String>();
colors.add("白色");
colors.add("黑色");
colors.add("粉色");
return new HuaweiMobilePhone(huaweiBattery,colors);
}
}
由上述代码可以看出,使用@ImportResource
标签来导入一个或多个Xml配置文件,(所以代码中的配置文件
默认存放在ClassPath的根目录下,如果有异,需要自行修改)。
Xml配置文件中引用Java配置类
电池配置类:
public class BatteryConfig {
@Bean
public HuaweiBattery huaweiBattery(){
return new HuaweiBattery("Huawei","华为手机电池");
}
}
在Xml配置文件中引入JavaConfig配置类,如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean class="com.ooyhao.spring.config.BatteryConfig"/>
<context:component-scan base-package="com.ooyhao.spring.config"/>
<bean id="huaweiMobilePhone" class="com.ooyhao.spring.bean.HuaweiMobilePhone">
<!--手机电池 引用构造注入-->
<constructor-arg name="battery" ref="huaweiBattery"/>
<!--手机颜色 集合字面量注入-->
<constructor-arg>
<list>
<value>蓝色</value>
<value>红色</value>
<value>黑色</value>
</list>
</constructor-arg>
</bean>
</beans>
由上述Xml配置文件中可以看出,使用<bean>
标签将配置文件导入,则可以在ref中引用到,但是此时测试还是会报错误,需要使用<component-scan>
配合使用,测试代码就不展示了。
总结:
至此,本节内容就算结束了,其中包括使用 Xml配置文件形式装配Bean,使用 JavaConfig配置文件形式装配Bean, 以及 使用自动发现和装配的方式来装配Bean 三种方式。同时涉及了 XMl文件中属性注入的两种方式,构造注入和set注入,JavaConfig配置文件中注入并未做过多的显示,因为这种方式其实跟普通的代码类似,不做主要记录。当然如果需要单独的引用properties配置文件的内容来装配字面量,可以使用@Value(${})
来操作。
基于实际开发环境的复杂度,往往上述三种方式都会在一个项目中共存的,那么如何在Xml中引用Xml,如何在JavaConfig配置文件中引用JavaConfig配置类,如何在Xml配置文件中引用JavaConfig配置类,如何在JavaConfig配置类中引用Xml配置文件。
案例地址:https://gitee.com/ooyhao/JavaRepo_Public/tree/master/Spring-in-Action
最后
如果觉得不错的话,那就关注一下小编哦!一起交流,一起学习