Spring in Action 第二章
@(Spring in Action)[spring, bean]
Spring装配bean
1.在XML中进行显式配置
2.在Java中进行显式配置
3.隐式的bean发现机制和自动装配
显式配置越少越好。当你必须要显式配置bean的时候(比如,有些源码不是由你来维护的,而当你需要为这些代码配置bean的时候),推荐使用类型安全并且比XML更加强大的JavaConfig。下面将先介绍自动化装配。
自动化装配bean
Spring从两个角度来实现自动化装配:
组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean。
自动装配(autowiring):Spring自动满足bean之间的依赖。
组件扫描和自动装配组合在一起就能发挥出强大的威力,它们能够将你的显式配置降低到最少。
创建可被发现的bean
创建接口:
package com.springTest;
/**
* @Author: YLBG-YCY-1325
* @Description:
* @Date: 2017/7/21
*/
public interface CompactDisc {
void play();
}
创建带有@Component注解的CompactDisc实现类,这个简单的注解表明该类会作为组件类,并告知Spring要为这个类创建
bean。没有必要显式配置SgtPeppersbean,因为这个类使用了@Component注解,所以Spring会为你把事情处理妥当。
package com.springTest;
import org.springframework.stereotype.Component;
/**
* @Author: YLBG-YCY-1325
* @Description:
* @Date: 2017/7/21
*/
@Component
public class SgtPepper implements CompactDisc {
private String title = "this is SgtPepper title";
private String artist = "The beatiles";
@Override
public void play() {
System.out.println("play " + title + ": artist" + artist);
}
}
如果你更倾向于使用XML来启用组件扫描的话,那么可以使用Spring context命名空间的<\context:component-scan>元素。
<?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:contex="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-3.2.xsd">
<!--自动扫描包-->
<contex:component-scan base-package="com.lemontree.web"/>
</beans>
创建配置类,@Configuration注解表明这个类是一个配置类
package com.springTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* @Author: YLBG-YCY-1325
* @Description: 设置配置类
* @Date: 2017/7/21
*/
@Configuration //@Configuration注解表明这个类是一个配置类
@ComponentScan // 没有包名默认是当前类包
public class CDplayerConfig {
}
创建测试类, 测试组件扫描能够发现CompactDisc::
package com.springTest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @Author: YLBG-YCY-1325
* @Description:
* @Date: 2017/7/21
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDplayerConfig.class)
@ActiveProfiles("dev")
public class CDplayerTest {
@Autowired
private CompactDisc cd;
@Test
public void testName() throws Exception {
cd.play();
}
}
CDPlayerTest使用了Spring的SpringJUnit4ClassRunner,以便在测试开始的时候自动创建Spring的应用上下文。注解
@ContextConfiguration会告诉它需要在CDPlayerConfig中加载配置。因为CDPlayerConfig类中包含了@ComponentScan,因此最终的应用上下文中应该包含CompactDiscbean。
为组件扫描的bean命名
两种方式命名bean:
第一种
@Component(名字)
第二种 Java依赖注入规范(Java Dependency Injection)中所提供的@Named注解来为bean设置ID
@Name(名字)
建议用@Component
设置组件扫描的基础包
为了指定不同的基础包,你所需要做的就是在@ComponentScan的value属性中指明包的名称:
@Configuration
// 没有包名默认是当前类包
// @ComponentScan("com.lemontree.springTest")
// @ComponentScan(basePackages = "com.lemontree.springTest")
@ComponentScan(basePackages = {"com.lemontree.springTest,com.lemontree.xxxx"})
public class CDplayerConfig {
}
在上面的例子中,所设置的基础包是以String类型表示的。我认为这是可以的,但这种方法是类型不安全(not type-safe)的。如果你重构代码的话,那么所指定的基础包可能就会出现错误了。
除了将包设置为简单的String类型之外,@ComponentScan还提供了另外一种方法,那就是将其指定为包中所包含的类或接口:
/**
* @Author: YLBG-YCY-1325
* @Description:
* @Date: 2017/7/21
*/
@Configuration
// @ComponentScan(basePackageClasses = CDplayer.class)
@ComponentScan(basePackageClasses = {CDplayer.class,CompactDisc.class)
public class CDplayerConfig {
}
basePackageClasses属性所设置的数组中包含了类,这些类所在的包将会作为组件扫描的基础包。
通过为bean添加注解实现自动装配
@Autowired注解可以用在类的任何方法上
// 在CDplaer中驻入CompactDisc
@Component("cDplayer")
public class CDplayer {
private CompactDisc sgtPepper;
public CDplayer(CompactDisc sgtPepper) {
this.sgtPepper = sgtPepper;
}
@Autowired
public void test(CompactDisc sgtPepper){
System.out.println("test 方法 驻入");
this.sgtPepper = sgtPepper;
}
public CompactDisc getSgtPepper() {
return sgtPepper;
}
public void setSgtPepper(CompactDisc sgtPepper) {
this.sgtPepper = sgtPepper;
}
}
// 测试例子
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDplayerConfig.class)
@ActiveProfiles("dev")
public class CDplayerTest {
@Autowired
private CompactDisc cd;
@Autowired
private CDplayer cDplayer;
@Test
public void cdshoudNotNull() throws Exception {
cDplayer.getSgtPepper().play();
Assert.notNull(cDplayer);
}
}
上面的例子会先输出test 方法 驻入,然后打印playe()方法中的文字。如果有两个@Autowired那么会出现两次驻入
通过Java代码装配bean(显示装配)
你想要将第三方库中的组件装配到你的应用中,在这种情况下,是没有办法在它的类上添加@Component和@Autowired注解的,因此就不能使用自动化装配的方案了
声明简单的bean
注意到下例中将componentScan给去掉了,同时在方法上添加@Bean注解。因为sgtPeppers()方法上添加了@Bean注解,
Spring将会拦截所有对它的调用,并确保直接返回该方法所创建的bean,而不是每次都对其进行实际的调用。
默认情况下,Spring中的bean都是单例的,我们并没有必要为第二个CDPlayer bean创建完全相同的SgtPeppers实例。所以,Spring会拦截对sgtPeppers()的调用并确保返回的是Spring所创建的bean,也就是Spring本身在调用sgtPeppers()时所创建的CompactDiscbean。因此,两个CDPlayer bean会得到相同的SgtPeppers实例。
如果把@Bean注释掉,那么测试类那边是无法进行驻入的。
@Configuration
public class CDplayerConfig {
@Bean
public CompactDisc sgtPerppers(){
return new SgtPepper();
}
@Bean
public CDplayer cDplayer(){
return new CDplayer(new SgtPepper());
}
}
通过这种下面方式引用其他的bean通常是最佳的选择,因为它不会要求将CompactDisc声明到同一个配置类之中。在这里甚至没有要求CompactDisc必须要在JavaConfig中声明,实际上它可以通过组件扫描功能自动发现或者通过XML来进行配置。
@Configuration
public class CDplayerConfig {
@Bean
public CompactDisc sgtPerppers(){
return new SgtPepper();
}
@Bean
public CDplayer cDplayer(CompactDisc sgtPepper){
return new CDplayer(sgtPepper);
}
}
带有@Bean注解的方法可以采用任何必要的Java功能来产生bean实例。构造器和Setter方法只是@Bean方法的两个简单样
例。这里所存在的可能性仅仅受到Java语言的限制。
命名空间
通过命名空间来简易化xml配置:
导入和混合配置
在JavaConfig中引用XML配置(还有java配置)
我们临时假设CDPlayerConfig已经变得有些笨重,我们想要将其进行拆分。
方式一:采用@Import,在一个JavaConfig中导入另一个JavaConfig(类似于xml配置中的 <\import resource=”xxxx”/>)
package com.lemontree.spring;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* Created by lemontree on 2017/8/2.
*/
@Configuration
@Import(SpringConfig.class)
public class MyConfig {
}
方式二:一个更好的方法创建一个高级的JavaConfig,将另外两个导入
package com.lemontree.spring;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* Created by lemontree on 2017/8/2.
*/
@Configuration
//@Import(SpringConfig.class)
@Import({SpringConfig.class,SpringMVCConfig.class})
public class MyConfig {
}
方式三:导入Java的配置文件还不够?那就导入xml文件呗
注意:实例中没有列出复数的形式该如何写,点源码看下立马就能知道该如何写了!
package com.lemontree.spring;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;
/**
* Created by lemontree on 2017/8/2.
*/
@Configuration
@Import(SpringConfig.class)
@ImportResource("classpath:spring.xml")
//@ImportResource(locations = {"classpath:spring-mvc.xml","classpath:spring-mybatis.xml"})
public class MyConfig {
}
在XML配置中引用JavaConfig
假设你正在使用Spring基于XML的配置并且你已经意识到XML逐渐变得无法控制,在被无数的尖括号淹没之前,我们决定将XML配置文件进行拆分。
xml中引另外一个xml配置文件很简单,但是如何去引一个java的配置文件那?想必都能猜到,xml文件是吧,不是有<
bean/>这个标签在么,java的配置也是java类啊,直接用bean去引不就得了,哈哈。
总结
对于xml和java的配置作者是如何取舍的,看看书上原话:
总结我同时建议尽可能使用自动化配置,以避免显式配置所带来的维护成本。但是,如果你确实需要显式配置Spring的话,应该优先选择基于Java的配置,它比基于XML的配置更加强大、类型安全并且易于重构。