制和自动装配。这三种方式各有各的优点,但也有其不足:
一、使用自动装配创建bean
Spring主要从两个角度来实现自动化装配:①组件扫描;②自动装配。组件扫描指的是Spring会自动扫描指定包及其子包下的所有bean,并将其放入spring容器中进行管理,而自动装配则是指对于有相互依赖关系的bean,Spring会将其自动装配到目标bean中,如将repository层的bean自动装配到service层中。
自动装配的方式创建bean主要是使用一个被专门用来当做配置的接口(或类)来实现的。配置接口上主要使用两个注解:@Configuration和@ComponentScan。@Configuration来标注该接口是用于定义配置的,而@ComponentScan则是用于指定扫描的bean的文件夹的,默认情况下Spring会扫描该配置接口所在包及其子包下的所有bean。以下是一段配置接口的示例代码:
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan
public interface CDPlayerConfig {
}
上述代码定义了Spring查找bean的方式,而创建bean则可以使用@Component注解来实现,该注解可以传一个参数value,用来指定要创建的bean的名称,默认使用类名并且首字母小写。以下代码演示了如何使用@Component创建一个bean:
public interface CompactDisc {
void play();
}
@Component
public class SgtPeppers implements CompactDisc {
private String title = "Sgt. Pepper's Lonely Hearts Club Band";
private String artist = "The Beatles";
public void play() {
System.out.println("Playing " + title + " by " + artist);
}
}
这样Spring就可以自动创建一个SgtPeppers的实例,并且将其放到Spring容器中进行管理,另外我们也可以使用@Named注解来创建一个bean。
上面只是讲了如何创建一个bean,而自动装配还有另一方面的概念:依赖注入。其是指Spring会将一个bean所依赖的bean自动装配进来。依赖注入是通过@Autowired或@Resource来实现的,当一个bean需要另一个bean作为其属性的时候,其只需要声明该bean的一个变量,并且对该属性使用@Autowired或@Resource标注一下即可。以下是一个依赖注入的实例:
@Component
public class CDPlayer implements MediaPlayer {
@Autowired
private CompactDisc compactDisc;
public void setCompactDisc(CompactDisc compactDisc) {
this.compactDisc = compactDisc;
}
public void play() {
compactDisc.play();
}
}
这里Spring就会在bean容器中查找CompactDisc实例,即sgtPeppers,并将其注入进来。以下是一段测试代码,用来验证这里的bean都创建了,并且也都注入进来了:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfig.class)
public class CDPlayerTest {
@Autowired
private CompactDisc cd;
@Autowired
private CDPlayer cdPlayer;
@Test
public void cdShouldNotBeNull() {
Assert.assertNotNull(cd);
cd.play();
}
@Test
public void cdPlayerShouldNotBeNull() {
Assert.assertNotNull(cdPlayer);
cdPlayer.play();
}
}
二、在java中进行显示的配置
这种方式创建bean则不需要进行组件扫描了,其是通过在配置类中通过方法显示的创建一个bean,该方法则需要使用@Bean注解进行标识。如下是一段在java配置中显示的声明bean的代码:
@Configuration
public class CDPlayerConfig {
@Bean
public CompactDisc sgtPeppers() {
return new SgtPeppers();
}
@Bean
public CDPlayer cdPlayer(CompactDisc compactDisc) {
CDPlayer cdPlayer = new CDPlayer();
cdPlayer.setCompactDisc(compactDisc);
return cdPlayer;
}
}
如上的代码中去掉了@ComponentScan注解,并且在配置类中使用@Bean注解显示的声明了CompactDisc和CDPlayer的两个实例,这两个实例的名称默认情况下是和方法名称是一样的。这里需要说明的是,①在@Bean标注的方法上如果有参数,那么Spring会自动在容器中查找是否有参数类型的bean,并将其注入进来;②如果不使用参数的方式注入依赖的bean,可以使用调用方法的方式,如下代码演示了如何调用方法注入bean:
@Configuration
public class CDPlayerConfig {
@Bean
public CompactDisc sgtPeppers() {
return new SgtPeppers();
}
@Bean
public CDPlayer cdPlayer() {
return new CDPlayer(sgtPeppers());
}
}
上面的代码中,在创建CDPlayer类型的bean时会调用一次sgtPeppers()方法,而在创建CompactDisc类型的bean时也会调用一次该方法,这样会造成Spring容器中有两个同类型的bean,这与Spring的bean都是单例的相悖,为了避免这个问题,Spring会拦截创建CDPlayer类型的bean时调用的sgtPeppers()方法,而直接从容器中获取该bean并将其返回。
这里需要注意的一点是,这种方式相对于第一种方式需要对每个bean都进行显示声明,但是其有自己的优点,对于一些额外的类库中的类,我们无法在其中加入Spring的注解,但是却要将其加入到Spring容器中进行管理,那么我们就可以通过这种方式来显示的声明这些类的bean。因而,在项目中我们可以通过将第一种方式和第二种方式结合使用来创建和管理bean。
三、xml中进行显示的配置
在xml文件中配置bean是Spring最先使用的一种方式,在该xml文件中主要是一个<beans></beans>标签,其有一个子标签<bean></bean>,所有的bean的声明都是在<bean>子标签中声明的。<bean>标签主要有两个属性:id和class。id用来指定要声明的bean的名称,如果没指定则创建的bean的默认名称为class属性的值加上#数字,class属性则是用来指定要创建的类(包含包名)的。如下是一段使用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"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="compactDisc" class="soundsystem.SgtPeppers"/>
</beans>
这样就创建了一个名称为compactDisc的SgtPeppers类型的对象。如果要创建的对象的构造器中有参数,我们有两种方式对其进行注入。一种是使用<bean>标签的子标签<constructor-arg>标签,其有两个属性ref和value,如果参数为其他的bean类型,那么就用ref指向其他的bean的id即可,如果参数为基本数据类型,那么就用value执行基本数据类型的值即可。以下是一段示例配置:
<?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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="compactDisc" class="soundsystem.BlankDisc">
<constructor-arg value="Sgt. Pepper's Lonely Hearts Club Band"/>
<constructor-arg value="The Beatles"/>
</bean>
</beans>
这里也可以使用c-命名空间的方式对构造器的参数进行注入,对于引用数据类型,其格式为c:argName-ref,对于基本数据类型,其格式为c:argName,这里argName为参数名称。以下是使用这种方式的示例配置:
<?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:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
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
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<bean id="compactDisc" class="chapter2.example1.soundsystem.BlankDisc"
c:title="Sgt. Pepper's Lonely Hearts Club Band" c:artist="The Beatles"/>
<bean id="cdPlayer" class="chapter2.example1.soundsystem.CDPlayer" c:cd-ref="compactDisc"/>
</beans>
对于一个类的创建,并不一定所有的参数都是必须的,因而Spring提供了一种属性注入的方式,属性注入则必须要求该类中有一个该属性的set方法,比如setCompactDisc,属性名在配置文件中的名称为该set方法后半部分首字母小写的形式。属性注入参数的方式和构造器注入的方式非常类似,其也有两种方式进行注入,即<bean>标签下的<property>子标签和p-命名空间。以下是进行属性注入的示例:
<?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:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
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
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<bean id="compactDisc" class="soundsystem.BlankDisc"
c:title="Sgt. Pepper's Lonely Hearts Club Band" c:artist="The Beatles"/>
<bean id="cdPlayer" class="soundsystem.CDPlayer">
<property name="compactDisc" ref="compactDisc"/>
</bean>
</beans>
<?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:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
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
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<bean id="compactDisc" class="soundsystem.BlankDisc"
c:title="Sgt. Pepper's Lonely Hearts Club Band" c:artist="The Beatles"/>
<bean id="cdPlayer" class="soundsystem.CDPlayer"
p:compactDisc-ref="compactDisc"/>
</beans>
对于使用子标签和使用命名空间进行的属性注入,在基本数据类型和基本对象类型的处理上基本相似,但是如果参数为集合,那么命名空间将无法处理,而只能使用子标签的方式来进行集合参数的注入,以下是一段示例配置:
<?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:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
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">
<bean id="compactDisc" class="soundsystem.BlankDisc">
<constructor-arg value="Sgt. Pepper's Lonely Hearts Club Band"/>
<!--<constructor-arg><null/></constructor-arg>-->
<constructor-arg value="The Beatles"/>
<constructor-arg ref="trackList"/>
</bean>
<util:list id="trackList">
<value>Sgt. Pepper's Lonely Hearts Club Band</value>
<value>With a Little Help from My Friends</value>
<value>Lucy in the Sky with Diamonds</value>
<value>Getting Better</value>
<value>Fixing a Hole</value>
</util:list>
</beans>
以上就是Spring创建bean的三种方式的介绍,可以看出,使用xml进行显示的配置这种方式比较复杂,而且因为其使用的是字符串,这不是类型安全的,使用JavaConfig则只能创建少量的bean,但是其可以为第三方库的类创建bean,而使用隐式的bean发现机制和自动装配则可以扫描大量的bean,但其不能将第三方库的bean收录到Spring容器中,因为第三方库的代码中不能添加Spring的注解。总而言之,后两种方式是现在比较常用的方式,并且可以结合起来使用,以实现对bean的控制。