完整代码请见:https://github.com/codercuixi...
建立应用对象之间协做关系的行为一般称为装配(wiring),这也是依赖注入(DI)的本质。java
在xml文件中显式配置(基本上公司中都不用了)
在Java中进行显式配置
隐式的bean发现机制和自动装配
建议:尽量使用自动配置的机制,显式配置越少越好。
当你必需要显式配置bean的时候(好比源码不是由你来维护的,而你须要这些代码配置bean的时候),使用类型安全而且比xml更为强大的JavaConfig。
也就是本身写的尽可能使用自动配置,他人的(第三方)使用JavaConfiggit
主要经过两个角度来实现自动化配置
组件扫描(component scanning):Spring会自动发现应用上下文所建立的Bean
自动装配(autowiring):Spring会自动知足bean之间的依赖github
总共分为三步:
第一步,将要使用的Bean添加上@Component注解spring
package stereo_autoconfig.soundsystem; import org.springframework.stereotype.Component; /** * Create by cuixin on 2018/8/26 **/ @Component public class SgtPeppers implements CompactDisc { private String title = "Sgt.Peppers's Lonely Hearts Club Band"; private String artist = "The Beatles"; @Override public void play() { System.out.println("Play "+title+" by "+ artist); } }
第二步,经过@ComponentScan注解来扫描指定包及其子包中带有@Component注解的类数组
package stereo_autoconfig.soundsystem; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * Create by cuixin on 2018/8/26 * 经过@ComponentScan注解启动组件扫描, * 默认扫描所在包及其子包中带有@Component注解的类 **/ @Configuration @ComponentScan public class CDPlayerConfig { }
第三步,经过@ContextConfiguration配置上下文信息,也就是声明第二步中使用配置类安全
package stereo_autoconfig.soundsystem; 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 org.junit.Assert.assertNotNull; /** * Create by cuixin on 2018/8/26 **/ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = CDPlayerConfig.class) public class CDPlayerTest { @Autowired private CompactDisc cd; @Test public void cdShouldNotBeNull() { assertNotNull(cd); } }
默认bean的id是将类名的第一个字母小写,好比上面的bean的ID就是sgtPeppers.
若是想要指定不一样的ID,只需将值传给@Component注解。session
@Component("lonelyHeartClub") public class SgtPeppers implements CompactDisc { ... }
Spring实战中说这种方式是类型不安全的,我使用IDEA重命名包时,只有在Refactor时选择Rename Package才会更改@ComponentScan中的String值,因此才说这是不安全的。
@ComponentScan默认的扫描的是所在包及其子包,若是你扫描其余包,或者想扫描多个包,应该怎么设置呢?指定不一样的基础包ide
@Configuration //方式一,指定一个,经过设置value属性 @ComponentScan("stereo_autoconfig.soundsystem") public class CDPlayerConfig { } @Configuration //方式二,指定一个,经过设置basePackages属性 @ComponentScan(basePackages = "stereo_autoconfig.soundsystem") public class CDPlayerConfig { } @Configuration//方式三,指定多个,经过设置basePackages为数组 @ComponentScan(basePackages = {"stereo_autoconfig.soundsystem", "stereo_autoconfig.video"}) public class CDPlayerConfig { }
@Configuration @ComponentScan(basePackageClasses = {CompactDisc.class, Video.class}) public class CDPlayerConfig { }
固然为了重构时删除接口及相关类,你能够在包下面建立一个空标记接口(Marker Interface),专门用来指定基础包。ui
public Interface SoundSystemMarkerInface{ } @Configuration @ComponentScan(basePackageClasses = {SoundSystemMarkerInface.class, Video.class}) public class CDPlayerConfig { }
经过@ComponentScan,咱们已经能够扫描获得Bean,可是如何装配这个Bean依赖的另外一个bean呢?这就是自动装配解决的问题了。
简单来讲,自动装配就是让Spring自动知足bean依赖的一种方法。this
@Component public class CDPlayer implements MediaPlayer { private CompactDisc cd; @Autowired public CDPlayer(CompactDisc cd){ this.cd = cd; System.out.println("构造器 自动装配"); } @Override public void play() { cd.play(); } @Autowired public void setCompactDisc(CompactDisc cd){ this.cd = cd; System.out.println("Setter方法 自动装配"); } @Autowired(required = false) public void insertDisc(CompactDisc cd){ this.cd = cd; System.out.println("其余方法 自动装配"); } }
package stereo_autoconfig.soundsystem; 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 org.junit.Assert.assertNotNull; /** * Create by cuixin on 2018/8/26 **/ @RunWith(SpringJUnit4ClassRunner.class) @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(){ player.play(); } }
不论是构造器,Setter方法仍是其余的方法,Spring都会去尝试知足方法参数上所声明的依赖。上面依次会先执行构造器方法,
而后按照出现代码顺序执行其余自动注入方法。因此上面的输出是
构造器 自动装配
Setter方法 自动装配
其余方法 自动装配
第一点,假若有且只有一个bean匹配依赖需求的话,那么这个bean就会被装配进来。
第二点,若是没有匹配的bean,那么在应用上下文建立的时候spring会抛出一个异常。
为了不异常的出现,能够将@AutoWired的required属性设置为false。但这可能致使这个bean处于未转配状态,使用的使用须要判断是否为空。
第三点,若是出现多个bean知足依赖关系的话,spring的@AutoWired自动装配也会抛出异常,由于他们不知道选哪个
当使用第三方库中的组件装配到你的应用中,在这种状况下,是没有办法在它的类上添加@ComponentScan和@AutoWired注解的,所以就不能使用自动化装配方案了。
JavaConfig比xml的优点:更为强大,类型安全,而且对重构友好。
尽管不是必须的,但一般会将JavaConfig放到单独的包中,使他与其余的应用程序逻辑分离开来。
步骤:使用@Configuration建立配置类,并使用带有@Bean的方法声明bean,最后经过@ContextConfiguration来建立上下文
package stereo_javaconfig.soundsystem; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * Create by cuixin on 2018/8/26 **/ @Configuration //建立配置类 public class CDPlayerConfig { @Bean //经过bean注解来声明简单的bean public CompactDisc compactDisc(){ return new SgtPeppers(); } @Bean(name = "cdPlayer") //bean id默认使用的方法名,使用方法参数实现注入,最优方法,能够将CompactDisc放在其余配置类中 public CDPlayer cdPlayer(CompactDisc compactDisc){ return new CDPlayer(compactDisc); } // @Bean(name = "cdPlayer2") // //bean id默认使用的方法名,引用建立bean的方法实现注入,spring会拦截全部compactDisc()调用, // // 确保按照规定(single,request,session)使用正确的bean,而不是简单地每次调用都生成一个 // public CDPlayer cdPlayer2(){ // return new CDPlayer(compactDisc()); // } }
package stereo_javaconfig.soundsystem; 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 stereo_javaconfig.soundsystem.*; import static org.junit.Assert.*; /** * Create by cuixin on 2018/8/26 **/ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = CDPlayerConfig.class) public class CDPlayeTest { @Autowired private MediaPlayer player; @Test public void player(){ player.play(); } }
只是维护早先代码的时候你才会用到这个。