Spring(四)装配
- 在XML中进行显示装配
- 在Java中进行显示装配
- 隐式的bean发现机制和自动装配
Spring提供了三种可选的配置方案,Spring的配置风格式可以互相搭配的,所以你可以选择使用XML装配一些bean
使用Spring基于Java的配置来装配一些bean,将剩余的bean让Spring去自动发现
建议尽可能的使用自动配置,显示的配置越少越好,当你必须要显式装配bean的时候(比如有些元码不是由你来维护的,而当你需要为
这些代码被之bean的时候)使用基于Java的配置最后使用XML的方式
自动装配
- 组件扫描:Spring会自动发现应用上下文中所创建的bean
- 自动装配:Spring自动满足bean之间的依赖
组件扫描和自动装配组合在一起能够将显式配置降低到最少,组件扫描默认时不启动的需要显式配置Spring:
1、基于Java配置:@ComponentScan注解启动组价扫描
2、基于XML配置:<context:component-scan/>
@ComponentScan默认会扫描与配置类相同的包,也就是说如果这个注解修饰了A类那么A类所在的
包下所有的子包和类都会被扫描找查带有Componenet注解或者这个注解的子注解的类,然后根据类
生成对应的bean。
和@ComponenetScan有相同的功能,但是需要在
这个标签中添加 base-package=”包路径” 属性指定索要扫描的包
//基于Java注解配置
@ComponenetScan
class A{
}
//基于XML标签配置
<beans>
<componenet-scan base-package="com.leco"/>
</beans>
SpringTest测试
@Component
public class SgrPeppers implements CompactDisc{
public void play(){
System.out.println("SgrPeppers");
}
}
//配置类
@ComponenetScan
@Configuration
public class CDPlayerConfig{
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=CDPlayerConfig.class)
public class test{
@Autowired
private CompactDisc cd;
@Test
public void test1(){
assertNotNUll(cd);
}
}
@SpringJUnit4ClassRunner 用于在测试开始的时候自动创建Spring上下文
@ContextConfiguration 会告诉他需要在CDPlayerConfig中加载配置
也可以使用AnnotationConfigApplicationContext容器进行测试
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(componenetConfig.class); //其中的参数是配置类
A a=context.getBean(A.class);
a.toA();
为组件命名
Spring应用上下文中所有的bean都会给定一个ID,ID可以通过两种方式设置:
- Spring应用上下文自动设置 //会将类名的首字母小写然后生成ID
- 自己手动设置 //随意设置
@Component("aaa") //ID="aaa"
public class A{}
@Component //ID="a"
public class A{}
设置组件扫描的基础包
因为我们需要将配置类单独放在独立的包中,让其和其他应用代码区分开,这样的话默认的基础包就不能满足要求了
之前我们没有为ComponenetScan设置属性,这意味着按照默认规则,他会配置类所在的包作为基础包来扫描组件
如果当需要扫描不同的包就需要为ComponenetScan设置value属性 如:ComponenetScan(“dao”)
1、通过basePackages属性进行更加清晰的配置
@Configuration
@ComponenetScan(basePackages="dao")
2、basePackages属性也可以使用复数形式,也就是说可以设置多个基础包,将basePackages属性设置为要扫
描包的一个数组就可以了
@Configuration
@ComponenetScan(basePackages={"com.leco.A","com.leco.B"})
public class ContextConfiguration{}
3、也可以使用basePackageClasses传入类数组
@Configuration
@ComponentScan(basePackageClasses={A.class,B.class})
public class ContextConfiguration{}
尽管我们以上都是使用类作为组件,但是最好使用通过标记接口的方式,这样能在你重构时也
非常友好的进行接口引用
以上讲的是自动扫描将bean
在一个应用中,如果对象都是独立的,彼此之间没有依赖就可以通过@Component进行标记bean
通过@ComponentScan自动扫描带有Component或他的子注解的类然后生成bean,但是如果当你的
应用中有些对象需要依赖其他对象才能完成任务这样的话我们需要有一种方法将扫描的bean和他们的
依赖装配到一起,这样的话我们就需要自动装配了。
通过为bean添加注解实现自动装配
自动装配就是让Spring自动满足bean依赖的一种方法,在满足依赖的过程中,会在Spring应用上下文中
寻找匹配某个bean需求的其他bean,为了声明要进行自动装配,我们可以借助Spring的@Autowired注解
在构造器上:
@Component //生成bean
public class A impllements BaseA{
private B b;
@Autowired //实现注入
public A(B b){
this.b=b;
}
public void toA(){
System.out.print("A")
b.toB();
}
}
在setter上:
@Autowired
public void setB(B b){
this.b=b;
}
其实setter方法没有什么特殊之处,@Autowired注解可以用在类的任何方法上,假设A有一个innsertB方法
那么@Autowired能够像在setter方法上那样,发挥相同的作用
不管是构造器、setter方法或者其他方法,Spring都会去尝试满足其中方法参数上所声明的依赖,加入有且只有一个bean匹配依赖需求
的话,那么这个bean将会被装配进来。
如果没有匹配的bean,把么在应用上下文创建的时候,Spring会抛出一个异常,为了避免异常的出现,你可以将@Autowired的required属性
设置为false。
@Autowired(required=false)
public A(A a){
this.a=a;
}
将required设置为false时,Spring在尝试执行自动装配时如果没有匹配的bean,Spring将会让这个bean处于
未装配状态,但是,把required属性设置为false时,如果在你的代码中没有进行null检查的话,这个未装配的
属性有可能会出现NullPointerException。
通过Java代码装配bean
在大部分场景下通过组件扫描和自动装配实现Spring的自动化装配时更为推荐的方法,但有时候自动化配置的方案行不通
因此需要明确配置Spring,比如想要将第三方库中的组件装配到你的应用中,在这种情况下时没有办法在他的类上添加@Component
和@Autowired注解的,影刺就不能使用自动化装配的方案了。
在这种情况下,必须使用显式装配的方式
在使用显式配置时JavaConfig(基于Java配置的类叫javaConfig类)进行配置时更好的选择,JavaConfig与其他的Java代码是有区别的
JavaConfig时配置代码,这意味着他不包含任何业务逻辑,JavaConfig也不应该侵入业务逻辑代码中
所以会将JavaConfig放在单独的包中使他与其他的应用程序逻辑分离开
创建配置类
@Configuration
public class A{
}
在创建javaConfig类的关键在于为其添加@Configuration注解,@Configuration注解表明这个类时一个配置类
该类应该包含在Spring应用上下文中如何创建bean的细节
声明简单的bean
要在JavaConfig中声明bean,我们需要编写一个方法,这个方法会创建所需类型的实例然后添加@Bean注解。
@Bean
public A newsA(){
return new A();
}
@Bean注解会告诉Spring这个方法将会返回一个对象,该对象要注册为Spring应用上下文中的bean
方法体中包含了最终产生bean实例的逻辑
默认情况下,bean的ID与带有@Bean注解的方法名是一样的,在上面的代码中bean的名字将会时newsA
如果想为他设置其他的名字可以使用@Bean注解的name属性
@Bean(name="getA")
public A newsA(){
return new A();
}
注:采用@Bean注解来声明bean有一个小技巧,就是让这个方法的返回值是一个接口,return 根须不同需求返回不同实现类的实例
借助JavaConfig实现注入
我们前面所声明的A bean时非常简单的,他自身没有其他的依赖,在声明一个B bean他依赖A
@Bean
public B newsB(){
return new B(newsA()); //这里使用的是带参构造注入
}
通过上面的代码看起来A时通过调用newsA得到的,但情况并不是,因为newsA上添加了@Bean注解,Spring将会拦截所有对他的调用
确保直接返回该方法所创建的bean而不是每次都对其进行实际的调用
这样的方式实现注入看起来比较难以理解,当然既然是使用方法生成bean,那么肯定就能使用传参的方式
@Bean
public B newsB(A a){
return new B(a);
}
这样一来就看着比较舒服了,在上面的代码中,newsB方法请求一个A做参数,当Spring调用newsB时创建B的bean的时候,他会自动装配
一个A到配置方法中。
通过这种方式时最好的选择,应为他不会要求将A声明在一个配置类中(在前面的注入中他是调用创建A bean的newsA实现注入,但是如果
这个配置类中没有newsA这个方法或者没有产生A bean的方法)在这里甚至没必要将A声明在JavaConfig中,可以通过组件扫描的方式
自动发现或者通过XML来配置
前面使用的时构造进行注入,那么也可以使用setter方法进行注入
@Bean
public B newsB(A a){
B b = new B(a);
b.setA(a);
return b;
}
配置类代码:
/**
* JavaConfig 基于Java的配置类
*/
@Configurable
//@ComponentScan(basePackageClasses = {A.class,B.class})
public class componenetConfig {
// @Bean
// public C newsC(){
// return new C(newsD());
// }
@Bean
public D newsD(){
return new D();
}
@Bean
public C initC(D d){
return new C(d);
}
@Test
public void test(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(componenetConfig.class);
C c=context.getBean(C.class);
c.toC();
}
}
我们写的是JavaConfig进行配置没有说基于XML,意思就是以后写代码推荐使用JavaConfig进行配置不推荐使用XML
(当然有些情况还是使用XML比较好)在新的项目中推荐使用自动化配置和JavaConfig,所以这里就不写了。
有些时候在一个配置类太过笨重的时候就需要拆分形成多个配置类,有可能拆成多个JavaConfig配置类
也可能会拆成JavaConfig和XML配置都有的
连接多个JavaConfig配置类
@Configuration
@Import(oneConfig.class,twoConfig.class)
public class mainConfig{
}
JavaConfig连接XML配置
@Configuration
@Import(oneConfig.class)
@ImportResource("classpath:two-config.xml")
public class mainConfig{
}