上一篇,用 setter 方式举例说明了 Spring 中的依赖注入支持的数据类型。
这篇,看看依赖注入的实现方式。
首先,bean 的配置文件可以通过 xml 和 properties 两种方式。其中 xml 是主流,properties 基本不用,具体实现方式:
-
setter 方法
-
构造器
-
接口回调
-
注解
-
API
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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="ConstXiong" class="constxiong.impltype.User">
<constructor-arg>
<value>1</value>
</constructor-arg>
<property name="name" value="ConstXiong"/>
</bean>
<bean id="favorites" class="java.lang.String">
<constructor-arg>
<value>写代码、睡觉</value>
</constructor-arg>
</bean>
<!-- 开启注解能力 -->
<context:component-scan base-package="constxiong"/>
</beans>
bean 的类代码
package constxiong.impltype;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class User implements ApplicationContextAware {
//构造方法注入
private Integer id;
public User(Integer id) {
this.id = id;
}
// set 方法注入
private String name;
public void setName(String name) {
this.name = name;
}
//实现接口 ApplicationContextAware 及其回调方法注入 applicationContextAware
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
// xml 开启注解能力
// 使用 @Autowired 给属性字段注入
@Autowired
private String favorites;
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + ''' +
", applicationContext=" + applicationContext +
", friend=" + favorites +
'}';
}
}
测试代码,包含 bean 通过 api 注册与注入
package constxiong.impltype;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 依赖注入的实现方式
*/
public class Test {
@SuppressWarnings("resource")
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("META-INF/spring-dependency-injection-impltype.xml");
User constxiong = (User)context.getBean("ConstXiong");
System.out.println(constxiong);
//api 构造、注入、组装、注册 bean
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory)context.getBeanFactory();
AbstractBeanDefinition userBeanDefinition = new RootBeanDefinition(User.class);
//构造方法注入
beanFactory.registerBeanDefinition("ApiUser", userBeanDefinition);
ConstructorArgumentValues argValues = new ConstructorArgumentValues();
argValues.addIndexedArgumentValue(0, 1);
//set 方法注入
userBeanDefinition.setConstructorArgumentValues(argValues);
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.addPropertyValue(new PropertyValue("name", "ApiUser"));
userBeanDefinition.setPropertyValues(propertyValues);
System.out.println(beanFactory.getBean("ApiUser"));
}
}
结果打印
User{id=1, name='ConstXiong', applicationContext=org.springframework.context.support.ClassPathXmlApplicationContext@1a6c5a9e, started on Mon Jan 11 23:10:11 CST 2021, friend=写代码、睡觉}
User{id=1, name='ApiUser', applicationContext=org.springframework.context.support.ClassPathXmlApplicationContext@1a6c5a9e, started on Mon Jan 11 23:10:11 CST 2021, friend=写代码、睡觉}
代码加了注释很容易看懂,强调几点:
-
注解的使用需要在 xml 里添加 context 的命名空间,context:component-scan 开启注解的能力,设置 bean 扫描路径。
-
setter 方法注入的缺点是可能放大了 bean 的修改权限、如果字段之间有依赖关系与校验逻辑 set 顺序不可控。
-
构造方法注入的缺点是属性较多时构造方法参数配置较多,顺序易出错、对 null 处理灵活性较差、不利于子类继承与扩展。
-
与注入的相关的注解除了示例中的 @Autowired,还包括 @Autowired + @Qualifier 指定 bean 名称注入,@Resource(name="") 指定 bean 名称注入,JSR250 规范;@Value 注入字符串、系统信息、配置文件信息、表达式等;@Inject JSR330规范。一般 @Autowired 使用最多,注解的使用都支持在属性字段上,有些也可以用在 setter 方法和构造方法上。
-
<bean> 标签还可以指定 autowire,自动绑定(Autowiring)的模式,这个配置也会影响依赖注入的结果。no:默认该值,不进行自动注入,需要手动配置要注入的 bean;byName:根据 bean 名称注入;byType:根据类型注入;constructor:根据构造方法加其参数注入。
-
网上还有资料提到了静态工厂方法注入和实例工厂方法注入,个人觉得这两个只是 bean 的创建的方式,严格意义上还未到注入的范畴。
【Java学习资源】整理推荐
- Spring 依赖注入的处理过程与 DependencyDescriptor 的说明
- Spring 各种 Aware 接口回调注入
- Spring Bean 生命周期内的 Exception 复现
- Spring 内建 Bean
- Spring Bean 的别名
- Spring Bean 未指定名称的命名规则
- Bean 何时被 GC
- Spring Bean 延迟加载
- ObjectFactory 与 BeanFactory 的区别
- Bean 销毁的方式与顺序
- Bean 初始化的方式与顺序
- Bean 的实例化方式
- Bean的注册方式
- 什么是 BeanDefinition?
- Spring IoC 容器启动时做了什么?
- BeanFactory 与 FactoryBean 的区别
- BeanFactory 与 ApplicationContext 的区别
- Spring IoC 依赖注入的实现方式
- Spring IoC 依赖注入(支持哪些数据类型?)
- Spring bean 依赖查找
- Spring-IoC
- Spring 的核心特性