该篇博客笔者准备对SpringIoC容器的Bean配置及其依赖注入的几种方式进行详细介绍,该篇博客主要阐述XML形式配置,还有一种注解方式会在之后的博客再进行详细阐述
该篇博客主要阐述
1、Spring容器配置Bean方式
2、依赖注入的三种方式
一、Spring容器配置Bean方式
- Spring使用一个或多个XML文件作为配置文件方式
- Spring采用基于Java注解的配置方式(Spring3.0及以上版本)
该篇博客主要阐述第一种使用XML文件配置下的几种依赖注入方式
1、前往Spring容器的XML之旅咯^_^
XML格式的容器信息管理方式是Spring提供的最为强大、支持最为全面的方式。所有使用XML文件进行配置信息加载的Spring IoC容器,包括BeanFactory和ApplicationContext的所有XML相应实现,都使用统一的XML格式,从Spring2.0之后,Spring继续保持向前兼容的前提下,引入了基于XML Schema的文档声明
以下是基于XSD的Spring配置文件文档声明
<?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="foo" class="x.y.Foo">
<meta key="cacheName" value="foo"/>
<property name="name" value="Rick"/>
</bean>
</beans>
配置声明框架可在spring-framework-4.3.10.RELEASE-dist/spring-framework-4.3.10.RELEASE/docs/spring-framework-reference/html/xsd-configuration.html中找到
所有注册到容器的业务对象,在Spring中称之为Bean。所以每个对象在XML中的映射也自然而然地对应一个叫< bean>的元素。所以既然容器最终可以管理所有业务对象,那么在XML中把这些叫做< bean>的元素组织起来,就叫做< beans>
2、< beans>了解一下
< beans>是XML配置文件中最顶层的元素。它下面可以包含
- 0个或1个<description>
- 多个<bean>、<import>、<alias>
< description>、< import>、< alias>
这三个元素不是必须的,但也会用到
- < description>:指定一些描述性信息
- < import>:通常情况下,可以根据模块功能或层次关系,将配置信息分门别类地放到多个XML文件中,在想加载主要配置文件,并将主要配置文件所依赖的配置文件同时加载时,可以在主要配置文件中通过< import>元素对其所依赖的配置文件进行引入:< import resource=”b.xml”>
< alias>:通过< alias>为某些名称较长的< bean>起别名(name或alias的名称都可以使用)
<alias name="dataSourceForMasterDatabase" alias="MasterDatabase"/>
二、依赖注入的三种方式
- 构造方法注入
- setter方法注入
- 接口注入
1、构造方法注入
通过构造方法注入方式为当前业务对象注入其所依赖的对象,需要使用< constructor-arg>元素来告诉Spring额外信息。如果不配置< constructor-arg>元素,那么Spring将使用默认的构造方法。
构造方法注入——传值
Student.java
package com.linjie.a;
/**
* @author LinJie
* 构造器注入——传值
*/
public class Student {
private String name;
private int age;
private int num;
private double score;
public Student(String name, int age, int num, double score) {
super();
this.name = name;
this.age = age;
this.num = num;
this.score = score;
}
@Override
public String toString() {
return "Student [name=" + name + ", num=" + num + ", age=" + age + ",score=" + score + "]";
}
}
测试类
package com.linjie.a;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author LinJie
* 测试
*/
public class SpringTest1 {
@Test
public void SpringTest() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Student student = (Student) context.getBean("student");
System.out.println(student);
}
}
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="student" class="com.linjie.a.Student">
<constructor-arg name="name" index="0" value="LinJie"></constructor-arg>
<constructor-arg name="age">
<value>20</value>
</constructor-arg>
<constructor-arg name="num" value="001"></constructor-arg>
<constructor-arg type="double" value="99.5"></constructor-arg>
</bean>
</beans>
解析applicationContext,xml各个元素属性
- id:每个注册到容器的对象都需要一个唯一标志,除了id可以唯一标识,还可以使用name属性来指定< bean>的别名(alias)
- class:每个注册到容器的对象都需要通过< bean>元素的class属性来指定其类型,要全类名
- index:根据属性参数的索引位置定位属性(根据其构造方法参数位置)
- name:根据属性参数名称定位属性
- type:根据属性参数类型定位属性:注意非基本类型参数的话比如String,需要type=”java.lang.string”
- value:为主体对象赋值
结果
构造方法注入——为当前业务对象注入其所依赖的对象
这里还是以Service层与Dao层为例
IUserDao
package com.linjie;
/**
* @author LinJie
* Dao接口
*/
public interface IUserDao {
//查询用户年龄
public void FindUserAge();
}
UserDaoImpl
package com.linjie;
/**
* @author LinJie
* Dao层实现类
*/
public class UserDaoImpl implements IUserDao {
@Override
public void FindUserAge() {
System.out.println("UserDaoImpl_Dao层被调用");
}
}
IUserService
package com.linjie;
/**
* @author LinJie
* service接口
*/
public interface IUserService {
//登录
public void login();
}
UserServiceImpl
package com.linjie;
public class UserServiceImpl implements IUserService {
private IUserDao userDao;
//Spring框架自动创建UserDaoImpl对象后,通过构造方法给对象赋值
public UserServiceImpl(IUserDao userDao) {
super();
this.userDao = userDao;
}
public IUserDao getUserDao() {
return userDao;
}
@Override
public void login() {
System.out.println("UserServiceImpl_Service层被调用");
userDao.FindUserAge();
}
}
测试类
package com.linjie;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTestaa {
@Test
public void Test() {
//读取applicationContext.xml文件
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean节点,通过bean的id来访问
IUserService userService = (IUserService) context.getBean("userService");
userService.login();
}
}
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="userDao" class="com.linjie.UserDaoImpl"></bean>
<bean id="userService" class="com.linjie.UserServiceImpl">
<constructor-arg ref="userDao"></constructor-arg>
</bean>
</beans>
ref:引用容器中其他的对象实例
结果
2、setter方法注入
与构造方法注入可以使用注入配置相对应,Spring为setter方法注入提供了< property>元素
< property>有一个name属性,用来指定该< property>将会注入的对象所对应的实例变量名称。之后通过value或者ref属性或者内嵌的其他元素来指定具体的依赖对象引用或值
setter方法注入——传值
Student.java
package com.linjie.aaa;
/**
* @author LinJie
* Student类
*/
public class Student {
private String name;
private int age;
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @param age the age to set
*/
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
测试类
package com.linjie.aaa;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTestaaa {
@Test
public void Test() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Student student = (Student) context.getBean("student");
System.out.println(student);
}
}
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="student" class="com.linjie.aaa.Student">
<property name="name" value="LinJie"></property>
<property name="age" value="20"></property>
</bean>
</beans>
结果
setter方法注入——为当前业务对象注入其所依赖的对象
其实这个例子在之前的博客中已经提及,利用Service层与Dao来示例
IUserDao
package com.linjie;
/**
* @author LinJie
* Dao接口
*/
public interface IUserDao {
//查询用户年龄
public void FindUserAge();
}
UserServiceImpl
package com.linjie;
/**
* @author LinJie
* Dao层实现类
*/
public class UserDaoImpl implements IUserDao {
@Override
public void FindUserAge() {
System.out.println("UserDaoImpl_Dao层被调用");
}
}
IUserService
package com.linjie;
/**
* @author LinJie
* service接口
*/
public interface IUserService {
//登录
public void login();
}
UserServiceImpl
package com.linjie;
public class UserServiceImpl implements IUserService {
private IUserDao userDao;//与配置文件中property name="userDao"的name对应(其实name=""是与setUserDao的名称相同)
//Spring框架自动创建UserDaoImpl对象后,通过setUserDao方法给userDao赋值
public void setUserDao(IUserDao userDao) {
this.userDao = userDao;
}
public IUserDao getUserDao() {
return userDao;
}
@Override
public void login() {
System.out.println("UserServiceImpl_Service层被调用");
userDao.FindUserAge();
}
}
测试类
package com.linjie;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTest {
@Test
public void Test() {
//读取applicationContext.xml文件
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean节点,通过bean的id来访问
IUserService userService = (IUserService) ac.getBean("userService");
userService.login();
}
}
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">
<!-- 配置要创建的对象信息 -->
<!-- id/name:用于识别不同的bean,唯一标识一个节点,标识对象的名字 -->
<!-- bean:每个bean相当于要创建的对象(利用反射机制) -->
<!-- class:创建哪个类的对象,要全类名 -->
<bean id="userDao" class="com.linjie.UserDaoImpl"></bean>
<!-- DI:依赖注入
双方都必须是bean,在创建service的时候,主动将dao的依赖对象交给service -->
<bean id="userService" class="com.linjie.UserServiceImpl">
<!-- setter方法注入
name:要与类中的userDao一致(即创建的userDao变量),然后调用userDao的setUserDao方法
ref:Spring容器中定义的bean对象的名字 -->
<property name="userDao" ref="userDao"></property>
</bean>
</beans>
当然setter方法注入还可以和构造方法注入同时使用
MockBusinessObject.java
package com.linjie.aaa;
/**
* @author LinJie
* @description:setter方法注入与构造方法注入同时使用
*/
public class MockBusinessObject {
private String dependency1;
private String dependency2;
public MockBusinessObject(String dependency1) {
super();
this.dependency1=dependency1;
}
/**
* @param dependency2 the dependency2 to set
*/
public void setDependency2(String dependency2) {
this.dependency2 = dependency2;
}
@Override
public String toString() {
return "MockBusinessObject [dependency1=" + dependency1 + ", dependency2=" + dependency2 + "]";
}
}
测试类
package com.linjie.aaa;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTestaaa {
@Test
public void Test() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
MockBusinessObject mockBo = (MockBusinessObject) context.getBean("mockBo");
System.out.println(mockBo);
}
}
applicationContext.java
<?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="mockBo" class="com.linjie.aaa.MockBusinessObject">
<constructor-arg name="dependency1" value="11111"></constructor-arg>
<property name="dependency2" value="22222"></property>
</bean>
</beans>
3、接口注入
关于接口注入因为它强制被注入对象实现不必要的接口,带有侵入性。所以基本已经放弃,所以这里就不多阐述了。
三种注入方式优缺点
1、构造方法注入
优点:对象在构造完成之后,即已经进入就绪状态,可以马上使用
缺点:当依赖对象比较多的时候,构造方法的参数列表会比较长。而通过反射构造对象的时候,对相同类型的参数处理会比较困难。并且在Java中,构造方法无法被继承,无法设置默认值。对于非必须的依赖处理,可能需要引入多个构造方法,而参数数量的变动可能造成维护不方便
2、setter方法注入
优点:因为方法可以命名,所以setter方法注入在描述性上要比构造方法注入要好些,另外,setter方法可以被继承,允许设置默认值,而且有良好的IDE支持
缺点:对象无法构造完成后马上进入就绪状态
3、接口注入
因为它强制被注入对象实现不必要的接口,带有侵入性。所以基本已经放弃使用
建议:采用以setter注入为主,构造注入为辅的注入策略。对于依赖关系无需变化的注入,尽量采用构造注入;而其他的依赖关系的注入,则考虑采用setter注入。
参考
《Spring揭秘》
《Spring IN ACTION》