spring装配bean的方式
bean是指经历了完整的spring bean生命周期并被spring容器管理的对象,bean是一个对象。
装配bean是指两个及以上的bean之间存在依赖关系,将其中一个bean注入到另一个bean中。
spring装配bean主要有三种类型:一种隐式装配和两种显式装配,即隐式的自动化装配、java代码显式装配和XML配置文件显式装配,经常用的是隐式装配。在项目中应该优先使用隐式的自动装配,其次使用在Java中进行装配,最后再使用在XML中进行装配。
每种装配类型都有两种实现方式:基于构造器注入和基于setter注入(即属性注入)。还有一些方式是根据这两种变化而来的,比如基于注解(如@Autowired和@Resource)的注入方式,底层是通过反射实现的,field.set(value,targetObject),是setter注入方式的变体。
下面使用CompactDisc(光盘CD)和CDPlayer(CD播放器)讲解如何通过这三种方式创建并装配bean。
1、自动化装配
1.1、创建bean(组件扫描)
package cn.spring.entity;
import org.springframework.stereotype.Component;
@Component
public class CompactDisc {
private String title = "Sgt. Peppers's Longly Hearts";
private String artist = "The Beatles";
public void play() {
System.out.println("Playing " + title + " by " + artist);
}
}
上面实现类上使用了@Component注解,该注解表明该实现类会作为组件类,告诉spring容器要为该类创建一个bean,spring会自动帮我们创建。不过组件扫描默认是不开启的,我们需要显式配置一下spring,使用@ComponentScan注解开启组件扫描,从而命令spring去寻找带有@Component注解的类,并为其创建bean。(补充:@Controller、@Service、@Repository注解与@Component注解的作用相同。)
1.2、创建配置类并开启组件扫描
package cn.spring.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = "cn.spring")
public class CDPlayerConfig {
}
@ComponentScan注解能够在spring中开启组件扫描,该注解会使spring默认扫描配置类所在的包及其子包,可以明确设置组件扫描的基础包,查找带有@Component注解的类,在spring上下文中自动创建一个bean。
在注解出现之前,XML配置方式是Spring IoC容器的唯一配置方式。之前,我们将配置信息集中写在XML中,如今使用注解,配置信息的载体由XML文件转移到了Java类中。我们通常将用于存放配置信息的类的类名以“Config”结尾,比如AppDaoConfig.java、AppServiceConfig.java 等等。我们需要在用于指定配置信息的类上加上@Configuration注解,以明确指出该类是Bean配置的信息源。加上@Configuration注解的类就是配置类,该注解的作用就是告诉spring,配置类起到了与XML配置文件相同的作用,好处是不用写那么多复杂的配置文件了。spring应用上下文AnnotationConfigApplicationContext 将配置类中标注了@Bean注解的方法的返回值识别为Spring Bean,并注册到容器中,受IoC 容器管理。用@Configuration注解类,等价于XML中配置beans,用@Bean标注方法等价于XML中配置bean。AnnotationConfigApplicationContext 在解析配置类时,会将配置类自身注册为一个 Bean,因为 @Configuration 注解本身定义时被 @Component 标注了。因此可以说,一个 @Configuration 同时也是一个 @Component。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
String value() default "";
}
1.3、自动装配bean
package cn.spring.entity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CDPlayer {
@Autowired
private CompactDisc cd;
public void play(){
cd.play();
}
}
自动装配就是让spring自动满足bean依赖的一种方法,在满足依赖的过程中,会在spring上下文中寻找匹配某个bean所需求的其他bean,并注入进来。可以使用@Autowired或@Resource注解实现自动装配。
1.4、测试
package cn.spring.test;
import cn.spring.config.CDPlayerConfig;
import cn.spring.entity.CDPlayer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfig.class)
public class SpringBeanTest {
//@Autowired
@Resource
private CDPlayer cdPlayer;
@Test
public void test() {
cdPlayer.play();
}
}
在测试类CDPlayerTest上使用了@RunWith(SpringJUnit4ClassRunner.class)注解,以便在测试开始的时候自动创建spring的应用上下文。
@RunWith : 运行器
@RunWith(JUnit4.class)就是指用JUnit4来运行
@RunWith(SpringJUnit4ClassRunner.class),让测试运行于Spring测试环境
@RunWith(Suite.class)的话就是一套测试集合
在测试类上使用了@ContextConfiguration(classes=CDPlayerConfig.class)注解,该注解会告诉spring需要在CDPlayerConfig配置类中加载应用上下文ApplicationContext配置。如果不加该配置,会报如下异常:java.lang.IllegalStateException: Failed to load ApplicationContext
注意:在测试类中不能通过new的方式创建一个CDPlayer对象,因为通过new方式创建的对象不归spring容器管理,spring容器为我们创建的CDPlayer对象中注入了CompactDisc对象,但是我们没有用它。new方式创建的对象中的实例变量需要被初始化,默认初始值是零值,因此,在CDPlayer中得到的CompactDisc的值就是null,会报空指针异常。
或者使用下面的测试类:
package cn.spring.test;
import cn.spring.config.CDPlayerConfig;
import cn.spring.entity.CDPlayer;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class SpringBeanTest {
@Test
public void test() {
//加载java配置类获取Spring应用上下文
ApplicationContext context = new AnnotationConfigApplicationContext(CDPlayerConfig.class);
CDPlayer cdPlayer = context.getBean(CDPlayer.class);
cdPlayer.play();
}
}
2、java配置装配
package cn.spring.entity;
public class CompactDisc {
private String title = "Sgt. Peppers's Longly Hearts";
private String artist = "The Beatles";
public void play() {
System.out.println("Playing " + title + " by " + artist);
}
}
package cn.spring.entity;
public class CDPlayer {
private CompactDisc cd;
public void setCompactDisc(CompactDisc cd){
this.cd = cd;
}
public void play(){
cd.play();
}
}
package cn.spring.config;
import cn.spring.entity.CDPlayer;
import cn.spring.entity.CompactDisc;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CDPlayerConfig {
@Bean
public CompactDisc getCompactDisc(){
return new CompactDisc();
}
@Bean
public CDPlayer getCDPlayer(CompactDisc cd){
CDPlayer cdPlayer = new CDPlayer();
cdPlayer.setCompactDisc(cd);
return cdPlayer;
}
}
getCompactDisc和getCDPlayer方法必须在同一个配置类中。@Bean注解会告诉spring这个方法将会返回一个对象,该对象要注册为spring应用上下文中的bean,方法体中包含了最终产生bean实例的逻辑。默认情况下,bean的ID与带有@Bean注解的方法名是一样的,在上例中,getCDPlayer方法得到的bean的名字就是getCDPlayer。可以通过name属性为bean指定一个名字,如@Bean(name="myCDPlayer"),在获取bean时就可以通过名字来获取,即context.getBean("myCDPlayer")。
package cn.spring.test;
import cn.spring.config.CDPlayerConfig;
import cn.spring.entity.CDPlayer;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class SpringBeanTest {
@Test
public void test() {
//加载java配置类获取Spring应用上下文
ApplicationContext context = new AnnotationConfigApplicationContext(CDPlayerConfig.class);
CDPlayer cdPlayer = context.getBean(CDPlayer.class);
cdPlayer.play();
}
}
3、XML配置装配
package cn.spring.entity;
public class CompactDisc {
private String title = "Sgt. Peppers's Longly Hearts";
private String artist = "The Beatles";
public void play() {
System.out.println("Playing " + title + " by " + artist);
}
}
package cn.spring.entity;
public class CDPlayer {
private CompactDisc cd;
/*public CDPlayer(CompactDisc compactDisc) {
}*/
public void setCompactDist(CompactDisc cd) {
this.cd = cd;
}
public void play(){
cd.play();
}
}
spring配置文件:
<?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">
<!--配置待创建对象的类-->
<bean id="disc" class="cn.spring.entity.CompactDisc"/>
<bean id="player" class="cn.spring.entity.CDPlayer">
<!--使用setter方法注入,在CDPlayer类中需要创建CompactDisc属性的set方法-->
<property name="compactDist" ref="disc"/>
<!--使用构造方法注入,在CDPlayer类中需要创建含有CompactDisc参数的构造方法-->
<!--<constructor-arg ref="disc"/>-->
</bean>
</beans>
测试类:
package cn.spring.test;
import cn.spring.entity.CDPlayer;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringBeanTest {
@Test
public void test() {
//加载xml配置文件获取Spring应用上下文
ApplicationContext context = new ClassPathXmlApplicationContext("app.xml");
//根据Class类型获取bean
CDPlayer cdPlayer = context.getBean(CDPlayer.class);
//根据bean的名字获取bean
//CDPlayer cdPlayer = (CDPlayer) context.getBean("player");
cdPlayer.play();
}
}