一,自动装配
使用自动装配,容器可以自动装配Bean,省去手工装配Bean的麻烦。当涉及自动装配Bean的依赖关系时,Spring提供了多种处理方式,如下:(参考Spring实战 - P64)
- byName
- byType
- constructor
- autodetect
1.1,byName自动装配
把与Bean的属性有相同名字的其他Bean自动装配到Bean对应的属性中,如果没有跟属性名字相匹配的Bean,则该属性不进行自动装配。
//Student.java
package com.sequence;
public class Student {
private String name;
private int age;
private Address addr;
...
}
//Address.java
package com.sequence;
public class Address {
private String city;
...
}
//Beans.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="lisi" class="com.sequence.Student" autowire="byName">
<property name="name" value="lisi" />
<property name="age" value="30" />
</bean>
<bean id="addr" class="com.sequence.Address">
<property name="city" value="xuchang" />
</bean>
</beans>
程序分析:
通过设置autowire属性为byName,Spring将特殊对待lisi Bean的所有属性,为这些属性寻找与其名字相同的Bean。在这里,Spring发现有一个与addr属性名相同的Bean,直接通过setter注入来进行自动装配。
1.2,使用注解自动装配
从Spring2.5开始,可以使用注解自动装配Bean的属性。使用注解自动装配与在XML中使用autowire属性自动装配没有太大差别。但是,使用注解可以更细粒度的自动装配,在Spring配置文件中设置autowire属性进行的自动装配将装配一个Bean的所有属性,而且,只能通过名称与类型来装配Bean。可以通过使用@Autowired或@Resource注解一个设置方法、构造函数、属性甚至任意方法自动装配特定的属性。
1.2.1,工作原理
为了使Spring自动装配具有@Autowired或@Resource注解的属性,必须在IoC容器中注册一个AutowiredAnnotationBeanPostProcessor实例。可以在Bean配置文件中包含
<context:annotation-config>
元素,这将自动注册一个AutowiredAnnotationBeanPostProcessor实例。(参考Spring攻略 - P41)
1.2.2,使用@Autowired
package com.sequence;
import org.springframework.beans.factory.annotation.Autowired;
public class Student {
private String name;
private int age;
@Autowired
private Address addr;
...
}
程序分析:
使用@Autowired注解可以直接标注属性,也可以标注setter方法,还可以标注需要自动装配Bean引用的任意方法,甚至可以标注构造函数。
1.2.3,可选的自动装配
默认情况下,@Autowired具有强契约特征,其所标注的属性必须是可装配的。如果没有Bean可以装配到@Autowired所标注的属性,自动装配就会失败,抛出NoSuchBeanDefinitionException异常。如果属性不一定非要装配,null值也是可以接受的,可以通过设置@Autowired的required属性为false来配置自动装配是可选的。
二,自动检测
<context:component-scan>
元素允许Spring自动检测与定义Bean,可以减少使用<bean>
元素。为了配置Spring自动检测,需要使用<context:component-scan>
元素。(参考Spring实战 - P77)
2.1,为自动检测标注Bean
<context:component-scan>
元素会扫描指定的包及其所有子包,并查找出能自动注册为Spring Bean的类,base-package
属性标示了所扫描的包。默认情况下,<context:component-scan>
查找使用构造型注解所标注的类,把这些类注册为Spring Bean。构造型注解如下:@Component、@Controller、@Repository、@Service
。
//MachineGun.java
@Component("gun")
public class MachineGun implements Gun{
public void shoot() {
System.out.println("MachineGun Shoot!");
}
}
//Character.java
package com.game;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component("badMen")
public class Character {
@Autowired
private Gun gun;
public void setGun(Gun gun) {
this.gun = gun;
}
public void kill() {
gun.shoot();
}
}
//Beans.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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config />
<context:component-scan base-package="com.game">
</context:component-scan>
</beans>
//Main.java
package com.game;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("Beans.xml");
Character badMen = (Character) context.getBean("badMen");
badMen.kill();
}
}
输出结果:
五月 15, 2018 8:35:57 上午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7dc5e7b4: startup date [Tue May 15 08:35:57 CST 2018]; root of context hierarchy
五月 15, 2018 8:35:57 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [Beans.xml]
MachineGun Shoot!
程序分析:
使用了自动装配与自动检测技术,配置文件中没有出现
<bean>、<property>
等标签,就可以实现相关的功能,配置文件非常的简洁。
2.2,过滤组件扫描
在如何通过扫描获取候选Bean方面,
<context:component-scan>
非常灵活。通过为<context:component-scan>
配置<context:include-filter>
或<context:exclude-filter>
子元素,可以随意调整扫描行为。
三,使用Spring基于java的配置
3.1,定义一个配置类
基于java的配置,使用@Configuration注解的java类,就等价于XML配置中的
<beans>
元素。
package com.game;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MachineGun implements Gun{
public void shoot() {
System.out.println("MachineGun Shoot!");
}
}
程序分析:
@Configuration注解会通知Spring,这个类将包含一个或多个Spring Bean定义。这些Bean定义是使用@Bean注解所标注的方法。
3.2,声明一个简单的Bean
使用@Bean注解定义一个方法来定义gun Bean
package com.game;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MachineGun implements Gun{
public void shoot() {
System.out.println("MachineGun Shoot!");
}
@Bean
public MachineGun gun() {
return new MachineGun();
}
}
@Bean通知Spring,这个被标注的方法将返回一个对象,该对象被注册为Spring应用上下文中的一个Bean,方法名作为该Bean的ID,在该方法中所实现的所有逻辑本质上都是为了创建Bean。
使用java配置的优点:
在XML配置中,Bean的类型与ID都是由String属性来标示的,String标识符的缺点就是无法进行编译期检查。如果重命名了MachineGun类,可能会忘记修改相应的XML配置。在基于java的配置中,Bean的ID与类型都被视为方法签名的一部分,Bean的创建都是在方法中定义的。因为它们全都是java代码,所以可以进行编译期检查来确保Bean的类型是否合法,并且Bean的ID是唯一的。
3.3,使用Spring的基于java的配置进行注入
package com.sequence;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class Student {
private String name;
private int age;
private Address addr;
@Bean
public Address address() {
Address obj = new Address();
obj.setCity("xuchang");
return obj;
}
@Bean
public Student zhangsan() {
String name = "zhangsan";
int age = 30;
Student obj = new Student();
obj.setName(name);
obj.setAge(age);
obj.setAddr(address());
return obj;
}
public Student() {
}
...
}
程序分析:
Spring基于java的配置感觉很自然,定义Bean就像使用java编写类的实例化代码一样,setter注入也是自然的java代码。
注意:
在Spring的java配置中,通过声明方法引用一个Bean并不等同于调用该方法,例如:
@Bean
public Student zhangsan() {
String name = "zhangsan";
int age = 30;
Student obj = new Student();
obj.setName(name);
obj.setAge(age);
obj.setAddr(address());
return obj;
}
通过调用@Bean注解标注的address()方法,会通知Spring该方法定义的Bean要被注册进Spring的上下文中。因此,在其他Bean声明方法中引用这个方法时,Spring都会拦截该方法调用,并尝试在应用上下文中查找该Bean,而不是让方法创建一个新的实例。