1. 基于XML的DI
1.1 注入分类
bean实例在调用无参构造器创建了空值对象后,就要对bean对象的属性进行初始化,属性初始化是由容器自动完成的,称为注入。根据注入方式的不同,常用的有两类:设值注入、构造注入。
- 设值注入:
设值注入是指,通过Java类中的setter方法给属性赋值,优点是简单、直观,因而在Spring的依赖注入中大量使用。 - 构造注入:
构造注入是指,通过调用Java类中的有参构造方法,在创建对象的同时,给属性赋值。
现在有School类——只包含简单类型属性,Student类——包含简单类型属性和引用类型属性,来看看如何在XML中进行DI。
School.java
public class School {
public String address;
private String name;
public School(){}
public School(String address, String name){
this.address = address;
this.name = name;
}
public void setAddress(String address) {
this.address = address;
}
public void setName(String name) {
this.name = name;
}
}
Student.java
public class Student {
private int age;
public School school;
public Student(){}
public Student(int age, School school) {
this.age = age;
this.school = school;
}
public void setAge(int age) {
this.age = age;
}
public void setSchool(School school) {
this.school = school;
}
}
1.1.1 设值注入
在XML配置文件中,使用<property />标签,完成属性的赋值。
- 简单类型(String和Java基本数据类型)的设值注入:
<bean id="school" class="com.lyj.main.beans.School">
<property name="address" value="China"/>
<property name="name" value="Hnu"/>
</bean>
- 引用类型的设值注入:
<bean id="student" class="com.lyj.main.beans.Student">
<property name="age" value="20"/>
<!-- 语法一:ref作为标签属性 -->
<property name="school" ref="school"/>
<!-- 语法二:ref作为子标签 -->
<!--<property name="school">-->
<!--<ref bean="school"/>-->
<!--</property>-->
</bean>
其中,"name”是属性名称,“value”是赋的属性值,<ref />的“bean”是引用对象的<bean>的“id”。
1.1.2 构造注入
在XML配置文件中,使用<constructor-arg />标签,完成属性的赋值,一个<constructor-arg />标签对应一个构造函数中的形参。
- 使用“name”属性:
<bean id="school_" class="com.lyj.main.beans.School">
<constructor-arg name="address" value="China"/>
<constructor-arg name="name" value="Hnu"/>
</bean>
<bean id="student_" class="com.lyj.main.beans.Student">
<constructor-arg name="age" value="20" />
<constructor-arg name="school" ref="school_"/>
</bean>
<constructor-arg />标签中,“name”是构造函数的形参名,“value”是简单类型的值,“ref”是引用类型的值。
- 使用“index”属性:
<bean id="student__" class="com.lyj.main.beans.Student">
<constructor-arg index="0" value="20" />
<constructor-arg index="1" ref="school_"/>
</bean>
<!-- 省略index属性 -->
<bean id="student___" class="com.lyj.main.beans.Student">
<constructor-arg value="20" />
<constructor-arg ref="school_"/>
</bean>
<constructor-arg />标签中,"index"是构造方法中参数的位置,从0开始。
1.2 具有集合性质的属性注入
bean类——MyCollection.java
public class MyCollection {
private String[] strs;
private List<Student> students;
private Set<String> set;
private Map<String, Student> map;
private Properties properties;
private List<Map<String, String>> mapList;
public void setStrs(String[] strs) {
this.strs = strs;
}
public void setStudents(List<Student> students) {
this.students = students;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setMap(Map<String, Student> map) {
this.map = map;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
public void setMapList(List<Map<String, String>> mapList) {
this.mapList = mapList;
}
}
- 为数组(array)注入值:
<!-- Array<String> -->
<property name="strs">
<array>
<value>张三</value>
<value>李四</value>
</array>
</property>
- 为List注入值:
<property name="students">
<list>
<ref bean="student"/>
<ref bean="student_"/>
<ref bean="student__"/>
</list>
</property>
- 为Set注入值:
<!-- Set<String> -->
<property name="set">
<set>
<value>北京</value>
<value>上海</value>
</set>
</property>
- 为Map注入值:
<!-- Map<String, Student> -->
<property name="map">
<map>
<entry key="张三" value-ref="student"/>
<entry key="李四" value-ref="student_"/>
</map>
</property>
- 为Properties注入值:
<!-- Properties key-value均为String -->
<property name="properties">
<props>
<prop key="phone">123456</prop>
<prop key="address">北京朝阳区</prop>
</props>
</property>
- 复杂(嵌套)集合属性的注入:
<!-- List<Map<String, String>> -->
<property name="mapList">
<list>
<map>
<entry key="name" value="张三"/>
<entry key="weight" value="80kg"/>
</map>
<map>
<entry key="name" value="李四"/>
<entry key="weight" value="100kg"/>
</map>
</list>
</property>
1.3 引用类型属性的自动注入
由框架给引用类型完成赋值,使用<bean/>标签的"autowire"属性,赋值的方式主要有"byName"、“byType”。
- byName:按名称注入,Java类中引用类型的属性名和spring容器(xml配置文件)中<bean>的"id"一致,且数据类型一致。
<bean id="studentA" class="com.lyj.main.beans.Student" autowire="byName">
<property name="age" value="20"/>
</bean>
- byType:按类型注入,Java类中引用类型的数据类型和spring容器(xml配置文件)中/的"class"属性值是同源关系——相同类、父类和子类关系、接口和实现类关系。(注意:符合条件的对象只能有一个,否则会报错)
<bean id="studentB" class="com.lyj.main.beans.Student" autowire="byType">
<property name="age" value="20"/>
</bean>
1.4 为应用指定多个spring配置文件
- 平等关系的配置文件(相互独立)
applicationContext.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.xsd">
<bean id="school" class="com.lyj.main.beans.School">
<property name="address" value="China"/>
<property name="name" value="hnu"/>
</bean>
<bean id="student" class="com.lyj.main.beans.Student">
<property name="age" value="20"/>
</bean>
</beans>
假设我们有配置文件applicationContext.xml,可以将其拆分为两个独立的配置文件——spring-student.xml、spring-school.xml:
spring-student.xml(引用其他bean对象时,使用自动注入方式)
<?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.xsd">
<bean id="student" class="com.lyj.main.beans.Student" autowire="byType">
<property name="age" value="20"/>
</bean>
</beans>
spring-school.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.xsd">
<bean id="school" class="com.lyj.main.beans.School">
<property name="address" value="China"/>
<property name="name" value="hnu"/>
</bean>
</beans>
创建容器对象时,将另个配置文件的路径一同传入即可:
ApplicationContext context = new ClassPathXmlApplicationContext("spring-school.xml", "spring-student.xml");
- 包含关系的配置文件
各配置文件中有一个总文件,总配置文件将其他子文件通过<import/>引入,在Java代码中只需要使用总配置文件对容器进行初始化即可。(注意:总配置文件一般不定义bean对象)
total.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.xsd">
<import resource="spring-school.xml"/>
<import resource="spring-student.xml"/>
</beans>
也可以使用通配符“*”,注意不能将总配置文件包含进去。
<import resource="spring-*.xml"/>
2. 基于注解的DI
spring容器对于Bean的创建和对象属性的依赖注入提供了注解的支持,Spring 中使用注解,需要在原有 Spring 运行环境基础上再做一些改变,完成以下三个步骤:
- 导入 AOP 的 Jar 包。因为注解的后台实现用到了 AOP 编程。
- 需要更换配置文件头,即添加相应的约束。
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
</beans>
- 需要在 Spring 配置文件中配置组件扫描器,用于在指定的基本包中扫描注解。
<context:component-scan base-package="com.lyj.main2" />
如果需要包含多个包,可以定义多个<component-scan>,也可以在“base-package”属性中使用 ‘,’ 或 ‘;’ 进行分隔,或者直接指定到父包名。
2.1 定义Bean的注解——@Component
需要在类上使用注解@Component,表示创建该类的对象(默认为单例对象),该注解的“value”属性用于指定该 bean 的 id 值。
@Component(value="school") // 显式指定id,等同于 <bean id="school" class="..."/>
//@Component("school") //只有一个value属性时,可省略
//@Component() //如果未定义id,则默认为首字母小写的类名名称——school
public class School {
private String address;
private String name;
}
另外,Spring 还提供了 3 个功能基本和@Component 等效的注解:
- @Repository 用于对 DAO 实现类进行注解;
- @Service 用于对 Service 实现类进行注解;
- @Controller 用于对 Controller 实现类进行注解;
之所以创建这三个功能与@Component 等效的注解,是为了以后对其进行功能上的扩展。
2.2 简单类型属性注入——@Value
需要在属性上使用注解@Value,该注解的 value 属性用于指定要注入的值。使用该注解完成属性注入时,类中无需 setter。当然,若属性有 setter,则也可将其加到 setter 上。
@Component("school")
public class School {
@Value("北京")
private String address;
private String name;
@Value("北京大学")
public void setName(String name) {
this.name = name;
}
}
2.3 引用类型属性注入——@Autowired
需要在属性上使用注解@Autowired,同样的,也可将其加到 setter 上。
- byType方式(默认)
@Component()
public class Student {
@Value("20")
private int age;
@Autowired
private School school;
}
- byName方式
@Component()
public class Student {
@Value("20")
private int age;
@Autowired
@Qualifier(value = "mySchool") // 定义id名称
private School school;
}
@Autowired 还有一个属性 required,默认值为 true,表示当匹配失败后,程序会终止运行。若将其值设置为 false,则匹配失败后程序不会终止运行,未匹配的属性值为 null。
@Autowired(required = false)
private School school;
2.4 引用类型属性注入——@Resource
Spring提供了对 jdk中@Resource注解的支持,@Resource 注解既可以按名称匹配Bean,也可以按类型匹配 Bean,默认是按名称注入。注意,使用该注解,要求 JDK 必须是 6 及以上版本。类似地,@Resource 可在属性上,也可在 set 方法上。
- 按类型匹配Bean
@Resource 注解若不带任何参数,采用默认按属性名称的方式注入,按名称不能注入 bean,则会按照类型进行 Bean 的匹配注入。
@Component()
public class Student {
@Value("20")
private int age;
@Resource
private School school;
}
- 按名称匹配Bean
@Resource 注解指定其“name”属性,则“name”的值即为按照名称进行匹配的 Bean 的 id。
2.5 Bean的生命始末——@PostConstruct 、@PreDestroy
在初始化方法上使用@PostConstruct ,与XML配置方式的<bean init-method=""> 等效;在销毁方法上使用@PreDestroy,与XML配置方式的<bean destroy-method=""> 等效。
@PostConstruct
public void init(){
System.out.println("执行初始化操作...");
}
@PreDestroy
public void destroy(){
System.out.println("执行销毁前的处理操作...");
}
3. 基于XML与基于注解的对比
-
XML配置方式
优点:对其所做修改,无需编译代码,只需重启服务器即可将新的配置加载;在没有源代码的情况下,使用不了@Compoent 等注解,此时使用 XML 进行配置 bean。
缺点:代码多,使用麻烦,开发效率低。 -
注解配置方式
优点:配置方便、简单,开发效率高,可读性高,在阅读源码时就可以知道属性的信息。
缺点:以硬编码的方式写入到了Java代码中,其修改是需要重新编译代码的。 -
总结
所以,在实际工程中,一般是两者结合使用,大部分Bean类使用注解配置方式,而对需要经常修改的Bean类使用XML配置方式。