装配Bean
spring提供了三种bean的装配机制
- 一、在XML中进行显示配置;
- 二、在Java中进行显示配置;
- 三、隐式的bean发现机制和自动装配
一、自动化装配bean
CD播放器依赖于CD,只有将CD插入(注入)到CD播放器,CD播放器才能放出音乐。
1、spring从两个角度来实现自动化装配
- 组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean
- 自动装配(autowiring):spring自动满足bean之间的依赖
2、@Component 注解表明该类会作为组件类,并告知spring要为这个类创建bean;组件扫描默认不启用,需组合@ComponentScan 注解使用。
3、@ComponentScan 启用组件扫描,如果没有其他配置,@ComponentScan默认会扫描与配置类相同的包。例,CDPlayerConfig类位于soundsystem包中,因此spring将会扫描这个包及这个包下的所有子包,查找带有@Compnent注解的类,这样就能发现CompactDisc,并且会在spring中自动为其创建一个bean;在xml中的使用:<context:componet-scan base-package="com.spring.soundsystem">。
4、为组件扫描的bean命名:spring应用上下文中所有的bean都会给定一个ID,@Component注解默认为ID为类名首字母小写,可自定义命名@Component("newName");也可以使用Java依赖注入规范中提供的@Named注解为bean设置ID,如:@Named("newName")。大多数场景中两者可相互替换,但推荐使用@Component,因为@Named注解语意不明。
5、设置组件扫描的基础包
@componetScan会以配置类所在的包作为基础包(base package)来扫描组件,如果我们将配置类放在单独的包中,使其与其他的代码区分开来,就需要在@Component的value属性中指明包的名称:
@Configuration @ComponentScan("com.spring.soundsystem") public class CDPlayConfig{ }如果想更加清晰的表明设置的基础包,可以通过basePackage属性进行配置:
@Configuration @ComponentScan(basePackages="com.spring.soundsystem") //@ComponentScan(basePackages{"com.spring.soundsystem","com.spring.video"}) public class CDPlayConfig{ }以上方法基础包为String类型,这种方法是类型不安全的(not type-safe),@Componet还提供另外一种方法,那就是将其指定为包中所包含的类或者接口
@Configuration @compnentScan(basePackageClasses={CDPlayer,class,Video.claass}) public class CDPlayerConfig{ }6、通过为bean添加注解实现自动装配
@autowired注解可用在类的任何方法上,不仅能够用在构造器上,还能用在属性的Setter方法上;如果没有匹配的bean,那么在应用上下文创建的时候,spring会抛出一个异常,为避免异常,可将@autowired的required属性设置为false,即:@autowired(required=false);将required属性设置为false时,spring会尝试执行自动装配,如果没有匹配的bean,sping将会让这个bean处于为装配的状态。同时,需要先在代码中进行null检查,否则会出现NullPointerException。如果有多个bean都能满足依赖关系,spring会抛出一个异常,表明没有明确指定选择哪个bean进行自动装配。可使用Java依赖注入规范的@Inject注解替换@Autowired。
二、通过Java代码装配bean(JavaConfig)
1、创建JavaConfig类的关键在于为其添加@Configruration注解,@Configuration注解表明这个类是一个配置类,该类应包含在spring应用上下文中如何创建bean的细节。
2、声明简单的bean
// @Bean注解会告诉spring这个方法返回一个对象,该对象要注册为spring应用上下文的bean // 默认情况下,bean的id与带有@Bean注解的方法名是一样的 // 如果想设置一个不同的名字,可以重命名该方法,也可以通过name属性指定一个不同的名字,@Bean(name = "lonelyHeartsClubBand") @Bean public CompactDisc sgtPeppers(){ return new SgtPeppers(); }3、借助JavaConfig实现注入
在JavaConfig中装配bean的最简单方式就是引用创建的bean的方法。
// 看起来CompactDisc是通过调用sgtPeppers()得到的,但情况并非完全如此。因为sgtPeppers()方法上添加了@Bean注解,spring将会拦截所有对它的调用,并确保直接返回该方法所创建的bean,而不是每次都对其进行实际的调用 @Bean public CDPlayer cdPlayer(){ return new CDPlayer(sgtPeppers()) ; } // 另一种理解起来更为简单的方式,CDPlayer的构造器实现DI @Bean pubic CDPlayer cdPlayer(CompactDisc compactDisc){ return new CDPlayer(compactDisc); } // Setter方法注入 public CDPlayer cdPlayer(CompactDisc compactDisc){ CDPlayer cdPlayer = new CDPlayer(compactDisc); cdPlayer.setCompactDisc(compactDisc); return cdPlayer; }
三、通过XML装配Bean
1、创建一个XML文件,必须以<beans>元素为根,最为简单的Spring 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 http://www.springframework.org/schema/context"> </beans>2、声明一个简单的<bean>
// 默认的ID"soundsystem.SgtPeppers#0","#0"是一个计数形式 <bean class="soundsystem.SgePeppers" /> // 为bena设置一个id <bean id="compactDisc" class="soundsystem.SgePeppers" />当spring发现这个<bean>元素时,它将会调用SgtPeppers的默认构造器来创建bean。需要注意的是,在这个简单的<bean>声明中,我们将bean的类型以字符串的形式设置在了class属性中。谁能保证设置给class属性的值是真正的类呢?Spring的XML配置并不能从编译器的类型检查中受益。即便它所引用的是实际的类型,如果你重命名了类,会发生什么呢?
3、借助构造器注入初始化bean
具体到构造器注入,有两种基本的配置方案可供选择:
· <constructor-arg>元素
· 使用spring 3.0所引用的c-命名空间
3.1、构造器注入bean引用
<bean id ="cdPlayer" class="soundsystem.CDPlayer"> <constructor-arg ref="compactDisc" /> </bean>当spring遇到<bean>元素时,他会创建一个CDPlayer实例。<constructor-arg>元素会告知spring要将一个ID为compactDisc的bean引用传递到CDPlayer的构造器中。
c-命名空间是在spring3.0中引入的,他是在XML中更为简洁第描述构造器参数的方式,要使用它必须要在XML的顶部声明其模式。
<bean id="cdPlayer" class="soundsystem.CDPlayer" c:cd-ref="compactDisc">4、将字面变量注入到构造器中
假设要创建CompactDisc的一个新实现
<bean id="compactDIs" class="soundsystem.BlankDisc"> <constructor-arg value="Sgt. Pepper's Loney Hearts Club Band" /> <constructor-arg value="The Beatles" /> </bean>这里没有使用“ref”属性来引用其他的bean,而是使用了value属性,通过该属性表明给定的值要以字面量的形式注入待构造器中(“ref”,引用传递,“value”,值传递)。
5、设置属性
如何选择构造器注入还是属性注入?对于强依赖使用构造器注入,二队可选性的依赖使用属性注入。
<bean id="cdPlayer" class="soundsystem.CDPlayer"> <property name="compactDisc" ref="compactDisc" /> </bean><property>元素为属性的Setter方法所提供的功能与<constructor-arg>元素为构造器所提供的功能是一样的。在本例中,它引用了ID为compactDisc的bean(通过ref属性),并将其注入到compactDisc属性中(通过setCompactDisc()方法)。
spring为<constructoer-arg>元素提供了c-命名空间作为替代方案,与之类似,sping提供了更加简洁的p-命名空间,作为<property>元素的替代方案。
使用p-命名空间需要进行声明:
我们可以使用p-命名空间,按照以下的方式装配compactDisc属性
<bean id="cdPlayer" class="soundsystem.CDPlayer" p:compactDisc-ref="compactDisc">"p:"前缀表明我们设置的是一个属性,属性名称以“-ref”提示spring要进行装备的是引用而不是字面量。
将字面量注入到属性中