spring Bean装配的几种方式简单介绍
Spring容器负责创建应用程序中的bean同时通过ID来协调这些对象之间的关系。作为开发人员,我们需要告诉Spring要创建哪些bean并且如何将其装配到一起。
spring中bean装配有两种方式
- 隐式的bean发现机制和自动装配
- 在java代码或者XML中进行显示配置
当然这些方式也可以配合使用。
我们测试代码如下
CompactDisc和MediaPlayer是两个接口 其中MediaPlayer的构造方法参数类型为CompactDisc。
我们主要测试CompactDisc和MediaPlayer的实现有没有注入进来,同时MediaPlayer接口对于CompactDisc的依赖有没有满足
package com.zcs; import com.zcs.service.CompactDisc; import com.zcs.service.MediaPlayer; 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; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertNotNull; @RunWith(SpringJUnit4ClassRunner.class) //XML配置 //@ContextConfiguration(locations ="spring-config.xml") //Java代码配置 @ContextConfiguration(classes =CDPlayerConfig.class) public class CDPlayerTest { @Autowired private CompactDisc cd; @Autowired private MediaPlayer player; @Test public void cdShouldNotBeNull(){ assertNotNull(cd); } @Test public void play(){ assertEquals("Playing 哎呦不错哦 by 周杰伦",player.play()); } }
1、自动化装配
Spring从两个角度来实现自动化装配:
组件扫描(ComponentScan):自动发现应用上下文中所创建的bean
自动装配(Autowired):自动满足bean之间的依赖
CompactDisc接口类和实现类:
public interface CompactDisc { String play(); }
@Component public class SgtPeppers implements CompactDisc { private String title="哎呦不错哦"; private String artist="周杰伦"; @Override public String play() { return "Playing "+title+" by "+artist; } }
MediaPalyer接口类和实现类
public interface MediaPlayer { String play(); } @Component public class CDPlayer implements MediaPlayer { private CompactDisc cd ; @Autowired public CDPlayer(CompactDisc cd){ this.cd=cd; } @Override public String play(){ return cd.play(); } }
配置类CDPlayerConfig:
@Configuration @ComponentScan public class CDPlayerConfig { }
@ComponentScan作用是扫描带有@Component注解的类,并为其创建bean。默认是扫描同一包下的类,当然也可以加入参数,指定包名。
如@ComponentScan(basePackages={"com.zcs"})或者@ComponentScan(basePackageClasses={CDPlayer.class,DVDPlayer.class})(即这些类所在的包)
@Autowired满足自动依赖装配,可以作用在任何方法上面。
XML形式的自动装配则修改测试类@ContextConfiguration,改成加载spring-config.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"> <bean id="compactDisc" class="com.zcs.impl.SgtPeppers"></bean> <bean id="cdPlayer" class="com.zcs.impl.CDPlayer"> <constructor-arg ref="compactDisc"></constructor-arg> </bean> <context:component-scan base-package="com.zcs"> </beans>
2、显示bean装配
当想要将第三方库中的组件装配到你的应用中,在这种情况下是没有办法在它的类上面添加@Component和@Autowired注解的,因此就不能使用自动化装配的方案。
只能选择java代码或者xml显示配置
首先可以把两个实现类上的@Component,还有配置类中的@ComponentScan,CDPlayer类中方法上的@Autowired注解去掉。
这个时候测试应该都通不过。
Java代码方式:修改CDPlayerConfig配置类为:
@Configuration public class CDPlayerConfig { @Bean public CompactDisc sgtPeppers(){ return new SgtPeppers(); } @Bean public MediaPlayer getCDPlayer(){ return new CDPlayer(sgtPeppers()); }
//@Bean
//public MediaPlayer getCDPlayer(CompactDisc compactDisc){
// return new CDPlayer(compactDisc);
//}
}
再运行测试应该会通过了,解释下 @Bean注解告诉Spring这个方法将会返回一个对象,该对象要注册为Spring应用上下文中的bean。方法体中包含了最终生成bean实列的逻辑。
第二个方法故意写的奇怪一点,看起来CompactDisc是通过调用sgtPeppers()方法得到的,但并非如此,Spring会拦截对sgtPeppers方法的调用,转而从上下文中直接获取该方法所创建的bean,而不是每次都对其进行实际的调用。
比如:再添加一个方法(测试会通不过)
@Bean public MediaPlayer anotherCDPlayer() { return new CDPlayer(sgtPeppers()); }
这两个方法依赖注入的CompactDisc将会是同一个bean。
当然通过情况下用注释的方法更容易理解一点。
XML方式:
首先CDPlayerTest为XML方式,同时修改spring-config.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"> <bean id="compactDisc" class="com.zcs.impl.SgtPeppers"></bean> <bean id="cdPlayer" class="com.zcs.impl.CDPlayer"> <constructor-arg ref="compactDisc"></constructor-arg>
</bean> <context:component-scan base-package="com.zcs"/> </beans>
组件扫描方式的配置不去掉也是没影响的。
补充:
复杂一点的构造方法
修改SgtPeppers的构造方法
public class SgtPeppers implements CompactDisc { private String title="哎呦不错哦"; private String artist="周杰伦"; private List<String> tracks=new ArrayList<>(); public SgtPeppers(String title, String artist, List<String> tracks) { this.title = title; this.artist = artist; this.tracks = tracks; } @Override public String play() { return "Playing "+title+" by "+artist; } }
同时修改spring-config.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"> <bean id="compactDisc" class="com.zcs.impl.SgtPeppers"> <constructor-arg index="0" value="雪狼湖"/> <constructor-arg index="1" value="张学友"/> <constructor-arg index="2"> <list> <value>不老的传说</value> <value>爱是永恒</value> </list> </constructor-arg> </bean> <bean id="cdPlayer" class="com.zcs.impl.CDPlayer"> <constructor-arg ref="compactDisc"></constructor-arg> </bean> <context:component-scan base-package="com.zcs"/> </beans>
还有属性注入<property>,c和p命名空间使用就不细说了。对于xml的配置一般在java代码中也会有对应的配置。但显示配置用java代码会类型安全一点
参考:spring实战第4版