spring 4.0.2.RELEASE 自动化装配
Spring从两个角度实现了自动化装配:
组件扫描(component scanning)
:Spring会自动扫描上下文中所创建的bean
自动装配(autowiring)
:Spring自动满足bean之间的依赖
创建可被发现的bean
以音乐专辑为例,一张音乐专辑(CD)需要一个CD播放器才能放出音乐,在这期间需要将CD插入(注入)CD播放器中才能达到我们想要的效果,而这个CD与CD播放器之间的依赖关系能够生动的向我们阐述DI是如何运行的。
首先我们要定义一个CD的概念:创建接口类Disc
package demo;
public interface IDisk {
public void play();
}
根据这个接口类声明一个带有@Component注解的实现类fristAlbum
package demo.impl;
import demo.IDisk;
import org.springframework.stereotype.Component;
@Component
public class firstAlbum implements IDisk {
private static String songTitle = "first song";
private static String artist = "carl";
public void play() {
System.out.println("Playing " + songTitle + "by " + artist);
}
}
这里的@Component注解是标识这个类为组件类并告知Spring为其创建bean对象。与以往不同的是不需要再xml中显示的配置bean,Spring会根据你所做的标识来完成配置工作。
Spring会根据注解来扫描指定包中需要创建bean对象的类,但Spring的组件扫描默认是不启动的,所以我们还需要进行一些配置,来启用组件扫描。
启用组件扫描
启用组件扫描有两种方式,一种是以java注解的形式启用,另外一种则是在xml中配置启用
java注解启用
package demo.impl;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan
public class CDPlayerConfig {
}
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"
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">
<context:component-scan base-package="demo.impl"/>
</beans>
在用两种配置方法配置之后,我们来测试一下Spring是否能够扫描到注解配置过得实现类,这里编写测试用例来测试一下。
package demo.demoTest;
import demo.IDisk;
import demo.impl.CDPlayerConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration(locations = "classpath:applicationContext.xml")
@ContextConfiguration(classes = CDPlayerConfig.class)
public class demoTest {
@Autowired
private IDisk disk;
@Test
public void run(){
disk.play();
}
}
注释掉的一行就是测试xml配置的注解,@Autowired注解则是让Spring创建的bean注入到disk对象中,由此来验证是否扫描成功。(这里使用了spring-test包,maven加一下依赖就好了)
为被扫描的bean命名
我们假设一种情况:如果有两个实体类实现了同一个接口类,那么在依赖注入的时候会出现冲突因为@Autowired是根据类型注入的,这时候有两个相同类型的bean要注入那必然会出现冲突。这里就需要对bean进行命名,我们创建一个新的实体类secondAlbum并依旧使用@Conponent 注解,只是这回要为它加上参数
package demo.impl;
import demo.IDisk;
import org.springframework.stereotype.Component;
@Component("secondAlbum")
public class secondAlbum implements IDisk {
private static String title = "second song";
private static String artist = "carl";
public void play() {
System.out.println("Playing "+ title + " by " + artist);
}
}
这样就可以在注入的时候根据名称判断需要注入的是哪个bean,对测试用例进行以下修改
public class demoTest {
@Autowired
@Qualifier("secondAlbum")
private IDisk disk;
@Test
public void run(){
disk.play();
}
}
或者
public class demoTest {
@Resource(name = "secondAlbum")
private IDisk disk;
@Test
public void run(){
disk.play();
}
}
这里要提一句的是,@Autowired要和@Qualifier一起用才能达到ByName的目的,因为Autowired是负责ByType注入的,然后Qualifier再根据Name选择注入哪个Bean,从而实现对应Name注入需要的Bean。
另一种方法使用了@Resource注解,这个注解用于将一个_named bean_注入。当然,可以同时限定_name_和_type_来保证注入的bean是需要的bean。
设置多个扫描包或指定扫描类
使用@ComponentScan注解默认是扫描当前包下的所有组件,当然按照需要也可以设置多个扫描包或者扫描类,@ComponentScan注解提供了两个参数,分别是__basePackages__和__basePackageClasses__,前者需要字符串类型的数组作为参数,后者测需要class类型的数组
package demo.impl;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = {"demo.impl","demo.demoTest"})
//@ComponentScan(basePackageClasses = {demo.impl.firstAlbum.class})
public class CDPlayerConfig {
}
通过为bean添加@Component注解实现自动注入
创建__MediaPlayer__接口类
package demo;
public interface MediaPlayer {
public void play();
}
创建__CDPlayer__类并实现MediaPlayer接口
package demo.impl;
import demo.IDisk;
import demo.MediaPlayer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
public class CDPlayer implements MediaPlayer {
@Autowired
@Qualifier("secondAlbum")
private IDisk disk;
public void play() {
disk.play();
}
}
__@Autowired__注解同样可以作用于set方法和构造方法上
//构造方法使用@Autowired
@Autowired
public CDPlayer(@Qualifier("firstAlbum")IDisk disk){
this.disk = disk;
}
//set方法使用Autowired
@Autowired
@Qualifier("firstAlbum")
private void setDisk(IDisk disk){
this.disk = disk;
}
这里注意,@Resource不适用于构造方法和set方法。
测试自动化装配
package demo.demoTest;
import demo.IDisk;
import demo.MediaPlayer;
import demo.impl.CDPlayerConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration(locations = "classpath:applicationContext.xml")
@ContextConfiguration(classes = CDPlayerConfig.class)
public class demoTest {
@Autowired
private MediaPlayer mediaPlayer;
@Test
public void run(){
mediaPlayer.play();
}
}
Done!