1、装配的混合使用
在上一篇系列博客《Spring使用篇(三)—— Spring中Bean的基本装配方式》中已经详细介绍了Spring中Bean的基本装配方法,在现实中,使用XML或者注解各有道理,建议在自己的工程中所开发的类尽量使用注解的方式,因为使用它并不困难,甚至可以说更为简单,而对于引入第三方包或者服务的类,尽量使用XML方式,这样的好处是可以尽量对三方包或者服务的细节减少理解,也更加清晰和明朗。
Spring同时支持这两种形式的装配,所以可以自由地选择XML或者注解的配置方法,无论采用何种方法都是将Bean装配到Spring IoC容器中,这样就可以通过Spring IoC容器去管理各类资源了。
在在上一篇系列博客《Spring使用篇(三)—— Spring中Bean的基本装配方式》中曾使用CD播放器和CD光盘的例子来讲解通过JavaConfig的显示装配方式去装配Bean,其原始配置代码如下所示:
package com.ccff.spring.assembling.config;
import com.ccff.spring.assembling.pojo.CDPlayer;
import com.ccff.spring.assembling.service.CompactDisc;
import com.ccff.spring.assembling.service.CompactDiscImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CDPlayerConfig {
@Bean
public CompactDisc compactDiscImpl(){
return new CompactDiscImpl();
}
@Bean
public CDPlayer cdPlayer(CompactDisc compactDisc){
return new CDPlayer(compactDisc);
}
}
1.1 使用多个JavaConfig配置类
假设该JavaConfig配置类目前已经变得很笨重,即里面配置了很多Bean。此时需要将其进行拆分,因此准备将CD光盘类从该配置类中拆分出来形成新的配置类进行单独管理。在com.ccff.spring.assembling.config包下创建名为“CDConfig”的CD光盘配置类,具体代码如下所示:
package com.ccff.spring.assembling.config;
import com.ccff.spring.assembling.service.CompactDisc;
import com.ccff.spring.assembling.service.CompactDiscImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CDConfig {
@Bean
public CompactDisc compactDiscImpl(){
return new CompactDiscImpl();
}
}
将CD光盘类拆分出来后,会发现此时的CDPlayerConfig出现了错误,如下图所示,原因和简单,因为CDPlayer和CompactDisc之间具有依赖关系,而此时的CDPlayerConfig中并没有CompactDisc。
想要解决这个错误,这就需要将这两个类组合在一起,第一种解决方法就是在CDPlayerConfig中使用注解@Import将CDConfig配置类导入到CDPlayerConfig中,具体代码如下所示:
package com.ccff.spring.assembling.config;
import com.ccff.spring.assembling.pojo.CDPlayer;
import com.ccff.spring.assembling.service.CompactDisc;
import com.ccff.spring.assembling.service.CompactDiscImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import(CDConfig.class)
public class CDPlayerConfig {
/*@Bean
public CompactDisc compactDiscImpl(){
return new CompactDiscImpl();
}*/
@Bean
public CDPlayer cdPlayer(CompactDisc compactDisc){
return new CDPlayer(compactDisc);
}
}
在com.ccff.spring.assembling.test包下创建名为“TestCD”的测试类,具体代码如下:
package com.ccff.spring.assembling.test;
import com.ccff.spring.assembling.config.CDPlayerConfig;
import com.ccff.spring.assembling.pojo.CDPlayer;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestCD {
private ApplicationContext context;
@Before
public void getContext(){
context = new AnnotationConfigApplicationContext(CDPlayerConfig.class);
}
@Test
public void TestBean(){
CDPlayer cdPlayer = (CDPlayer) context.getBean("cdPlayer");
cdPlayer.playMusic();
}
}
运行上面的测试方法TestBean,查看输出到控制台上的日志信息,发现通过注解@Import的方式可以成功将其他JavaConfig配置类引入,具体结果如下所示:
或者采用第二种更好的办法,也就是不在CDPlayerConfig中使用注解@Import,而是在com.ccff.spring.assembling.config包下创建一个更高级别的名为“SoundSystemConfig”配置类,在这个配置类使用注解@Import将两个配置类组合在一起,具体代码如下所示:
package com.ccff.spring.assembling.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import({
CDPlayerConfig.class,CDConfig.class})
public class SoundSystemConfig {
}
修改测试类TestCD中的getContext方法,在此引入SoundSystemConfig配置类,具体代码如下所示:
package com.ccff.spring.assembling.test;
import com.ccff.spring.assembling.config.CDPlayerConfig;
import com.ccff.spring.assembling.config.SoundSystemConfig;
import com.ccff.spring.assembling.pojo.CDPlayer;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestCD {
private ApplicationContext context;
@Before
public void getContext(){
context = new AnnotationConfigApplicationContext(SoundSystemConfig.class);
}
@Test
public void TestBean(){
CDPlayer cdPlayer = (CDPlayer) context.getBean("cdPlayer");
cdPlayer.playMusic();
}
}
运行上面的测试方法TestBean,查看输出到控制台上的日志信息,发现通过SoundSystemConfig的方式可以成功将其他JavaConfig配置类引入,具体结果如下所示:
1.2 在JavaConfig配置类中引入XML
这里需要注意,不管采用哪种方式,都将CDConfig的配置和CDPlayerConfig的配置分开了,只不过此时还是均采用的是JavaConfig的显示装配方式,若此时需要将CD光盘Bean通过XML的方式进行装配,则需要对CompactDisc接口的实现类CompactDiscImpl中的成员属性添加get和set方法,具体代码如下:
package com.ccff.spring.assembling.service;
public class CompactDiscImpl implements CompactDisc {
private String title = "依然范特西";
private String artist = "周杰伦";
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getArtist() {
return artist;
}
public void setArtist(String artist) {
this.artist = artist;
}
@Override
public void play() {
System.out.println("当前正在播放"+artist+"的专辑《"+title+"》");
}
}
则在spring-config中添加如下的具体配置:
<!--装配CompactDiscImpl,实现XML与注解的混合使用-->
<bean id="compactDiscImpl" class="com.ccff.spring.assembling.service.CompactDiscImpl">
<property name="title" value="叶惠美" />
<property name="artist" value="周杰伦" />
</bean>
此时我们已经将光盘通过XML的方式进行了配置,但是CD播放器还是通过JavaConfig的方式进行了配置,因此如何让Spring同时加载这两种配置方式呢?
答案是在Java Config配置类上使用注解@ImportResource,通过该注解将配置在XML中的Bean引入。在本案例中,在CDPlayerConfig配置类中首先注释掉注解@Import,然后使用注解@ImportResource将配置的XML引入,具体代码如下:
package com.ccff.spring.assembling.config;
import com.ccff.spring.assembling.pojo.CDPlayer;
import com.ccff.spring.assembling.service.CompactDisc;
import com.ccff.spring.assembling.service.CompactDiscImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;
@Configuration
//@Import(CDConfig.class)
@ImportResource("classpath:spring-config.xml")
public class CDPlayerConfig {
/*@Bean
public CompactDisc compactDiscImpl(){
return new CompactDiscImpl();
}*/
@Bean
public CDPlayer cdPlayer(CompactDisc compactDisc){
return new CDPlayer(compactDisc);
}
}
修改测试类TestCD中的getContext方法,让其重新加载CDPlayerConfig配置类,具体代码如下所示:
package com.ccff.spring.assembling.test;
import com.ccff.spring.assembling.config.CDPlayerConfig;
import com.ccff.spring.assembling.pojo.CDPlayer;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestCD {
private ApplicationContext context;
@Before
public void getContext(){
context = new AnnotationConfigApplicationContext(CDPlayerConfig.class);
}
@Test
public void TestBean(){
CDPlayer cdPlayer = (CDPlayer) context.getBean("cdPlayer");
cdPlayer.playMusic();
}
}
运行上面的测试方法TestBean,查看输出到控制台上的日志信息,发现通过注解@ImportResource的方式可以成功将其他XML配置引入,具体结果如下所示:
1.3 在XML中引入JavaConfig配置类
这里为了演示方便,将之前的项目中涉及到的两个Bean(CD唱片CompactDiscImpl和CD播放器CDPlayer)的配置全部修改为通过XML的方式进行配置。
首先在为这两个Bean类添加无参构造方法和get、set方法,在此基础上配置spring-config配置文件如下,在XML中实现对Bean的装配,具体配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
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
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<!--Spring Bean配置代码-->
<!--通过XML配置装配Bean:装配简易值-->
<bean id="school" class="com.ccff.spring.assembling.pojo.School">
<property name="school_name" value="中国科学院大学" />
<property name="school_no" value="Sch001" />
<property name="school_location" value="北京市怀柔区怀北镇怀北庄中国科学院大学雁西湖校区" />
</bean>
<bean id="student" class="com.ccff.spring.assembling.pojo.Student">
<property name="name" value="学生一" />
<property name="studentNo" value="S001" />
<property name="age" value="24" />
<property name="sex" value="男" />
<property name="school" ref="school" />
</bean>
<!--通过XML配置装配Bean:装配字符串集合-->
<bean id="complexAssembly" class="com.ccff.spring.assembling.pojo.ComplexAssembly">
<property name="id" value="1" />
<property name="list">
<list>
<value>value-list-1</value>
<value>value-list-2</value>
<value>value-list-3</value>
</list>
</property>
<property name="map">
<map>
<entry key="key1" value="value-map-1" />
<entry key="key2" value="value-map-2" />
<entry key="key3" value="value-map-3" />
</map>
</property>
<property name="properties">
<props>
<prop key="prop1">value-prop-1</prop>
<prop key="prop2">value-prop-2</prop>
<prop key="prop3">value-prop-3</prop>
</props>
</property>
<property name="set">
<set>
<value>value-set-1</value>
<value>value-set-2</value>
<value>value-set-3</value>
</set>
</property>
<property name="array">
<array>
<value>value-array-1</value>
<value>value-array-2</value>
<value>value-array-3</value>
</array>
</property>
</bean>
<!--通过XML配置装配Bean:装配复杂集合-->
<bean id="role1" class="com.ccff.spring.assembling.pojo.Role">
<property name="roleId" value="1" />
<property name="roleName" value="role-name-1" />
<property name="roleNote" value="role-note-1" />
</bean>
<bean id="role2" class="com.ccff.spring.assembling.pojo.Role">
<property name="roleId" value="2" />
<property name="roleName" value="role-name-2" />
<property name="roleNote" value="role-note-2" />
</bean>
<bean id="user1" class="com.ccff.spring.assembling.pojo.User">
<property name="userId" value="1" />
<property name="userName" value="user-name-1" />
<property name="userNote" value="user-note-1" />
</bean>
<bean id="user2" class="com.ccff.spring.assembling.pojo.User">
<property name="userId" value="2" />
<property name="userName" value="user-name-2" />
<property name="userNote" value="user-note-2" />
</bean>
<bean id="userRoleAssembly" class="com.ccff.spring.assembling.pojo.UserRoleAssembly">
<property name="id" value="1" />
<property name="list">
<list>
<ref bean="role1" />
<ref bean="role2" />
</list>
</property>
<property name="map">
<map>
<entry key-ref="role1" value-ref="user1" />
<entry key-ref="role2" value-ref="user2" />
</map>
</property>
<property name="set">
<set>
<ref bean="role1" />
<ref bean="role2" />
</set>
</property>
</bean>
<!--通过命名空间装配Bean:简单的设值-->
<bean id="role3" class="com.ccff.spring.assembling.pojo.Role" c:_0="3" c:_1="role-name-3" c:_2="role-note-3" />
<bean id="role4" class="com.ccff.spring.assembling.pojo.Role" p:roleId="4" p:roleName="role-name-4" p:roleNote="role-note-4" />
<!--通过命名空间装配Bean:为bean的属性设值-->
<bean id="role5" class="com.ccff.spring.assembling.pojo.Role" c:_0="5" c:_1="role-name-5" c:_2="role-note-5" />
<bean id="role6" class="com.ccff.spring.assembling.pojo.Role" p:roleId="6" p:roleName="role-name-6" p:roleNote="role-note-6" />
<bean id="user3" class="com.ccff.spring.assembling.pojo.User" c:_0="3" c:_1="user-name-3" c:_2="user-note-3" />
<bean id="user4" class="com.ccff.spring.assembling.pojo.User" p:userId="4" p:userName="user-name-4" p:userNote="user-note-4" />
<util:list id="list">
<ref bean="role5" />
<ref bean="role6" />
</util:list>
<util:map id="map">
<entry key-ref="role5" value-ref="user3" />
<entry key-ref="role6" value-ref="user4" />
</util:map>
<util:set id="set">
<ref bean="role5" />
<ref bean="role6" />
</util:set>
<bean id="userRoleAssembly2" class="com.ccff.spring.assembling.pojo.UserRoleAssembly" p:id="2" p:list-ref="list" p:map-ref="map" p:set-ref="set" />
<!--装配CompactDiscImpl,实现XML与注解的混合使用-->
<bean id="compactDiscImpl" class="com.ccff.spring.assembling.service.CompactDiscImpl">
<property name="title" value="七里香" />
<property name="artist" value="周杰伦" />
</bean>
<bean id="cdPlayer" class="com.ccff.spring.assembling.pojo.CDPlayer">
<property name="compactDisc" ref="compactDiscImpl" />
</bean>
</beans>
修改TestBean测试类,在该测试类中添加测试通过XML方式装配的测试方法TestBeanXML,具体代码如下所示:
package com.ccff.spring.assembling.test;
import com.ccff.spring.assembling.config.CDPlayerConfig;
import com.ccff.spring.assembling.pojo.CDPlayer;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context