装配Bean
Spring装配Bean的三种方式
- 在XML中进行显示配置
- 在Java中进行显示配置
- 隐式的Bean发现和自动装配
- 组件扫描:Spring会自动发现应用上下文所创建的Bean
- 自动装配:Spring自动满足Bean之间的依赖
尽可能地的使用自动配置的机制。显示的配置越少越好,当你必须要显示配置Bean的时候(比如:有些源码不是由你来维护的,而当你需要为这些代码配置的时候),推荐使用类型安全(因为在Java配置中可以利用编译器检查)并且比XML更加强大的JavaConfig。最后,只有当你想要使用便利的XML命名空间,并且在JavaConfig中没有同样的实现时,才应该使用XML。
下面利用一个案例
手机和手机电池是一个依赖关系,在正常情况下,如果手机离开了手机电池,那么其实也不会起到什么作用,故以此为一个案例展开Spring Bean的装配。
手机接口:
1 2 3 4 5
|
public interface MobilePhone {}
|
电池接口:
1 2
| public interface Battery {}
|
手机实现类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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 + '}'; } }
|
电池实现类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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注入方式,我们在上一篇其实已经接触到了。
1 2 3 4 5 6 7 8 9 10
| <?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方式,测试类:
1 2 3 4 5 6 7
| @Test public void testDIXml(){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("phone.xml"); HuaweiMobilePhone bean = context.getBean(HuaweiMobilePhone.class); System.out.println(bean); }
|
Java显示配置
PhoneConfig 配置类:
1 2 3 4 5 6 7 8 9 10
| public class PhoneConfig { @Bean public HuaweiBattery huaweiBattery(){ return new HuaweiBattery(); } @Bean public HuaweiMobilePhone huaweiMobilePhone(){ return new HuaweiMobilePhone(huaweiBattery()); } }
|
使用注解,测试,如下:
1 2 3 4 5 6 7
| @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
注解进行自动装配。
手机实现类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @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 + '\'' + '}'; } }
|
手机电池实现类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @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配置类:
1 2
| @ComponentScan(basePackages = "com.ooyhao.spring") public class AutoWirePhoneConfig {}
|
测试类:
1 2 3 4 5 6 7
| @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配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?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>
|
测试实例不变,测试结果:
1
| HuaweiMobilePhone{battery=HuaweiBattery{brand='Huawei', name='华为手机'}}
|
为了了解更多的类型注入,下面新增耳机接口和实现类,并将手机的实现类进行修改,如下:
耳机接口类:
1 2
| public interface EarPhone {}
|
耳机实现类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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 + '}'; } }
|
手机类:增加手机颜色和耳机
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| 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配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| <?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>
<bean id="earPhone1" class="com.ooyhao.spring.bean.HuaweiEarPhone"> <constructor-arg name="color" value="白色"/> <constructor-arg name="price" value="59.9"/> </bean>
<bean id="earPhone2" class="com.ooyhao.spring.bean.HuaweiEarPhone"> <constructor-arg value="黑色"/> <constructor-arg value="60.5"/> </bean>
<bean id="earPhone3" class="com.ooyhao.spring.bean.HuaweiEarPhone"> <constructor-arg index="1" value="66.6"/> <constructor-arg index="0" value="粉色"/> </bean>
<bean id="earPhone4" class="com.ooyhao.spring.bean.HuaweiEarPhone" c:_0="蓝色" c:_1="88.8"/>
<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> <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>
|
测试结果如下:
1
| 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配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| <?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>
<bean id="earPhone1" class="com.ooyhao.spring.bean.HuaweiEarPhone"> <property name="color" value="白色"/> <property name="price" value="59.9"/> </bean>
<bean id="earPhone2" class="com.ooyhao.spring.bean.HuaweiEarPhone" p:color="蓝色" p:price="88.88"/>
<bean id="huaweiMobilePhone" class="com.ooyhao.spring.bean.HuaweiMobilePhone"> <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>
|
测试结果:
1
| 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配置文件相互引用
手机实现类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| 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 + '}'; } }
|
电池实现类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| 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:
1 2 3 4 5 6 7 8 9 10 11
| <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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <?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>
|
测试:
1 2 3 4 5 6 7 8
| @Test public void testRef(){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("phone.xml"); HuaweiMobilePhone bean = context.getBean(HuaweiMobilePhone.class); System.out.println(bean); }
|
在实际开发中,不可能将所有的配置信息写到一个XML文件中,所以存在相互引用,上述代码可以测试出,在xml文件
如何引用另外一个xml文件。一般,可以单独创建一个xml文件统一管理,如下:
1 2 3 4 5 6 7 8 9 10
| <?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文件。如下:
1 2 3 4 5 6 7
| @Test public void testRef(){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("combineXml.xml"); HuaweiMobilePhone bean = context.getBean(HuaweiMobilePhone.class); System.out.println(bean); }
|
Java配置类相互引用
BatteryConfig配置类:
1 2 3 4 5 6
| public class BatteryConfig { @Bean public HuaweiBattery huaweiBattery(){ return new HuaweiBattery("Huawei","华为手机电池"); } }
|
PhoneConfig配置类:
1 2 3 4 5 6 7 8 9 10 11 12
| @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); } }
|
测试类:
1 2 3 4 5 6 7 8
| @Test public void testJavaConfigRef(){ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(PhoneConfig.class); HuaweiMobilePhone bean = context.getBean(HuaweiMobilePhone.class); System.out.println(bean); }
|
上述代码可以看出,在JavaConfig配置类之间互相引用其实和Xml文件非常相似,在XML文件中是使用<import resource = 'aa.xml'
而在JavaConfig配置类中导入是使用@import({Config.class})
注解。通过导入的配置类的class类即可。当然,随着项目的不断扩大,JavaConfig配置类也会越来越多,这种方式来维护就会变得比较麻烦,我们还是可以通过使用统一的JavaConfig配置类来管理JavaConfig。如下:
1 2 3 4 5
| @Import({ BatteryConfig.class, PhoneConfig.class }) public class CombineConfig {}
|
Java配置类中引用Xml配置文件
上面已经介绍完了Java配置如何引用Java配置,Xml配置如何引用Xml配置,并且如何将其分散的配置文件或配置类
进行统一的处理。下面我们看一下如何在Java配置类中引用Xml配置文件:
Xml配置文件:
1 2 3 4 5 6 7 8 9 10 11 12
| <?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配置文件信息:
1 2 3 4 5 6 7 8 9 10 11 12
| @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配置类
电池配置类:
1 2 3 4 5 6
| public class BatteryConfig { @Bean public HuaweiBattery huaweiBattery(){ return new HuaweiBattery("Huawei","华为手机电池"); } }
|
在Xml配置文件中引入JavaConfig配置类,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <?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配置文件。