通过组件扫描和自动装配实现Spring的自动化配置是更为推荐的方式,但有时候自动化配置的方案行不通,因此需要明确配置Spring。
在进行显式配置时,通过Java代码装配bean是更好的方案,因为它更强大,类型安全并且重构。
JavaConfig是配置代码,意味着它不应包含任何业务逻辑,也不该侵入到业务逻辑代码中。通常将JavaConfig放到单独的包中,使它与其他的应用程序逻辑分离。
一、利用JavaConfig进行注解扫描(该部分只是用来替代XML中自动扫描,不是属于JavaConfig配置)
现在不是在XML文件中配置<\component-scan>。而是通过Java代码定义Spring的装配规则:
//启用组件扫描,默认会扫描与配置类相同的包及其子包(基础包),查找所有带@Component注解的类,
//但是将该配置文件单独放在一个包中,与其他应用代码区分开
@Configuration
//扫描一个包
@ComponentScan(basePackages = "org.springaction")
//扫描多个包
//@ComponentScan(basePackages = {"org.springaction","org.wdz.filter"})
//也可以指定包中包含的类或接口
//@ComponentScan(basePackageClasses = {CompactDisc.class, GuestFilter.class})
public class CDPlayConfig {
//不用写其他代码
}
测试一下:
@RunWith(SpringJUnit4ClassRunner.class)
//配置类
@ContextConfiguration(classes = CDPlayConfig.class)
public class CDPConfigTest {
@Autowired
private CompactDisc cd;
@Test
public void doP(){
cd.play();
}
}
二、显式产生bean实例
对上面的CDPlayConfig 进行修改,不再采用注解扫描,而是显式产生bean实例:
//将一个POJO标注为定义Bean的配置类
@Configuration
public class CDPlayConfig {
}
在类中编写方法,利用@Bean注解产生实例:
@Configuration
public class CDPlayConfig {
//返回一个对象,注册为Spring应用上下文中的bean。
//默认情况下,bean的ID与方法名是一样的。也可以设置一个不同的名字
@Bean(name = "lonely")
public CompactDisc sgtPeppers(){
return new SgtPeppers();
}
}
不使用默认无参的构造器构建实例,而是依赖一个bean。
//在同一个JavaConfig文件中
@Bean
public CompactDisc cdPlay(){
//构造器依赖一个CompactDisc。
//因为sgtPeppers()添加了@Bean注解,Spring将会拦截所有对它的调用(它不是普通的方法),并确保直接返回该方法所创建的bean,而不是每次都对其进行实际的调用。
return new CDPlat(sgtPeppers());
}
//不是简单的调用sgtPeppers()的方法逻辑,而是从Spring容器中返回相应Bean的单例,不管调用多少次,都返回相同的Bean
@Bean
public CompactDisc anthercdPlay(){
//默认情况下,Spring中的bean都是单例的。
//sgtPeppers()方法的调用返回的都是同一个bean。
return new CDPlat(sgtPeppers());
}
上面的Bean都是在同一个JavaConfig中定义的,如果Bean是在多个@Configuration中进行定义,也就是一个JavaConfig引用其他的JavaConfig。
//第一个JavaConfig配置
@Configuration
public class OneConfig{
@Bean
public CompactDisc sgtPeppers(){
return new sgtPeppers();
}
}
//由于@Configuration注解类本身已经标注了@Component注解,所以任何标注了@Configuration的类,本身也相当于标注了@Component,即它们可以像普通的Bean一样被注入其他的Bean中。
//第二个JavaConfig配置
@Configuration
public class TwoConfig{
//像普通Bean一样注入CompactDisc
@Autowired
private CompactDisc compactDisc;
@Bean
public CDPlay cdPlay(){
//像普通Bean一样,调用Bean相关的方法
return new CDPlay(compactDisc.sgtPeppers());
}
}
如果存在多个配置类,可以把它们组装到一个配置类中:
@Configuration
public class OneConfig{
@Bean
public CompactDisc sgtPeppers(){
return new sgtPeppers();
}
}
//组装到另一个配置中(合二为一)
@Configuration
//使用Import注解
@Import(OneConfig.class)
public class ServiceConfig{
@Bean
public LoginServer loginServer(){
return new LoginServer ();
}
}
3、对于JavaConfig的配置,也可以使用ApplicationContext方式进行启动,获取相应的Bean:
public static void main(String[] args){
//构造函数的参数直接传入标注@Configuration的Java类
ApplicationContext ctx=new AnnotationConfigApplicationContext(OneConfig.class);
CompactDisc sgtpeppees=ctx.getBean(CompactDisc.class);
}
如果需要注入多个@Configuration配置类,那么:
public static void main(String[] args){
ApplicationContext ctx=new AnnotationConfigApplicationContext();
//注册多个@Configuration配置类
ctx.register(OneConfig.class);
ctx.register(TwoConfig.class);
//刷新容器以应用这些注册的配置类
ctx.refresh();
CompactDisc sgtpeppees=ctx.getBean(CompactDisc.class);
}
可以对通过调用方法来引用bean的方式进行修改,更为常用的方式是:
//调用方法创建cdPlay bean的时候,会自动装配一个CompactDisc到配置的方法中
//CompactDisc bean并不一定是在同一个配置类中引用@Bean方法产生的,它可以通过组件扫描或XML配置获得(不用管compactDisc是采用什么方式创建出来的)
@Bean
@Autowired
public CompactDisc cdPlay(CompactDisc compactDisc){
//在方法体中,可以按照合适的方式使用compactDisc bean,比如构造器和Setter方法
CDPlat cdPlat=new CDPlat(compactDisc);
cdPlat.setCompactDisc(compactDisc);
return cdPlat;
}