两层意思:
-
注入 Spring Bean 或者可依赖对象(@Autowired)
-
注入外部化配置(@Value)
6.1 依赖注入的模式
-
手动模式
-
XML 资源配置元信息
-
Java 注解配置元信息
-
API 配置元信息(普通用户使用较少,框架开发者使用较多,需掌握)
-
-
自动模式(官方不推荐使用自动绑定模式)
-
Autowiring(自动绑定)
-
依赖注入类型:
| 依赖注入类型 | 配置元数据举例 |
|---|---|
| Setter 方法 | <property name = "user" ref="userBean"/> |
| 构造器 | <constructor-arg name="user" ref="userBean"/> |
| 字段 | @Autowired User user; |
| 方法 | @Autowired public void user(User user) {...} |
| 接口回调 | class MyBean implements BeanFactoryAware {...} |
6.2 自动绑定
6.2.1 自动绑定原因
Spring 容器能够根据自动绑定在合作的 Bean(依赖 Bean 和被依赖 Bean) 之间,通过某种策略自动处理这些合作者之间的关系
自动绑定优点:
-
减少属性或者构造器参数的设置
-
自动绑定能够更新配置
6.2.2 自动绑定模式
| 模式 | 说明 |
|---|---|
| no | 默认值,未激活 Autowiring,需要手动指定依赖注入对象 |
| byName | 根据被注入属性的名称作为 Bean 名称进行依赖查找,并将对象设置到该属性 |
| byType | 根据被注入属性的类型作为依赖类型进行查找,并将对象设置到该属性 |
| constructor | 特殊 byType 类型,用于构造器参数 |
参考枚举类:org.springframework.beans.factory.annotation.Autowire
public enum Autowire {
/**
* Constant that indicates no autowiring at all.
*/
NO(AutowireCapableBeanFactory.AUTOWIRE_NO),
/**
* Constant that indicates autowiring bean properties by name.
*/
BY_NAME(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME),
/**
* Constant that indicates autowiring bean properties by type.
*/
BY_TYPE(AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE);
private final int value;
Autowire(int value) {
this.value = value;
}
public int value() {
return this.value;
}
/**
* Return whether this represents an actual autowiring value.
* @return whether actual autowiring was specified
* (either BY_NAME or BY_TYPE)
*/
public boolean isAutowire() {
return (this == BY_NAME || this == BY_TYPE);
}
}
6.2.3 自动绑定限制以及不足(XML 特有)
-
自动绑定会被属性或者构造器参数等注入覆盖
-
无法绑定 String、原生类型、Class 类型以及这些类型的数组类型
-
缺乏精确性
-
自动绑定的信息很难在一些工具类中进行呈现,比如自动生成的文档等
-
当绑定对象不止一个会有歧义,并且会报错
6.3 Setter 方法依赖注入
-
手动模式
-
XML 资源配置元信息
-
Java 注解配置元信息
-
API 配置元信息
-
-
自动模式
-
byName
-
byType
-
6.3.1 XML 方式
public class XmlDependencySetterInjectionDemo {
public static void main(String[] args) {
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions("classpath:dependency-setter-injection.xml");
System.out.println(factory.getBean(UserHolder.class));
}
}
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <import resource="beans-ioc-lookup.xml"/> <bean class="com.gcl.dependency.injection.UserHolder"> <property name="user" ref="superUser"/> </bean> </beans>
6.3.2 Java 注解
public class AnnotationDependencySetterInjectionDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(AnnotationDependencySetterInjectionDemo.class);
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml");
context.refresh();
System.out.println(context.getBean(UserHolder.class));
context.close();
}
@Bean
public UserHolder userHolder(User user) {
// 构造器方式
// return new UserHolder(user);
// setter 方式
UserHolder holder = new UserHolder();
holder.setUser(user);
return holder;
}
}
6.3.3 API 方式
public class ApiDependencySetterInjectionDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 注册 BeanDefinition
context.registerBeanDefinition("userHolder", createBeanDefinitionUserHolder());
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml");
context.refresh();
System.out.println(context.getBean(UserHolder.class));
context.close();
}
private static BeanDefinition createBeanDefinitionUserHolder() {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(UserHolder.class);
builder.addPropertyReference("user", "superUser");
return builder.getBeanDefinition();
}
}
6.3.4 自动注入方式
public class AutowiringByNameDependencySetterInjectionDemo {
public static void main(String[] args) {
DefaultListableBeanFactory context = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
reader.loadBeanDefinitions("autowiring-dependency-setter-injection.xml");
System.out.println(context.getBean(UserHolder.class));
}
}
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <import resource="beans-ioc-lookup.xml"/> <!-- byName --> <bean class="com.gcl.dependency.injection.UserHolder" autowire="byName"/> <!-- byType --> <!-- <bean class="com.gcl.dependency.injection.UserHolder" autowire="byType"/>--> </beans>
6.4 构造器依赖注入
-
手动模式
-
XML 资源配置元信息
-
Java 注解配置元信息
-
API 配置元信息
-
-
自动模式
-
constructor
-
6.4.1 XML 方式
public class XmlDependencyConstructorInjectionDemo {
public static void main(String[] args) {
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions("classpath:dependency-constructor-injection.xml");
System.out.println(factory.getBean(UserHolder.class));
}
}
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <import resource="beans-ioc-lookup.xml"/> <bean class="com.gcl.dependency.injection.UserHolder"> <constructor-arg name="user" ref="user"/> </bean> </beans>
6.4.2 Java 注解方式
public class AnnotationDependencyConstructorInjectionDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(AnnotationDependencyConstructorInjectionDemo.class);
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml");
context.refresh();
System.out.println(context.getBean(UserHolder.class));
context.close();
}
@Bean
public UserHolder userHolder(User user) {
// 构造器方式
return new UserHolder(user);
}
}
6.4.3 API 方式
public class ApiDependencyConstructorInjectionDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.registerBeanDefinition("userHolder", createBeanDefinitionUserHolder());
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml");
context.refresh();
System.out.println(context.getBean(UserHolder.class));
context.close();
}
private static BeanDefinition createBeanDefinitionUserHolder() {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(UserHolder.class);
// 按照构造器参数顺序进行设置
builder.addConstructorArgReference("superUser");
return builder.getBeanDefinition();
}
}
6.4.4 自动注入方式
public class AutowiringByConstructorDependencySetterInjectionDemo {
public static void main(String[] args) {
DefaultListableBeanFactory context = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
reader.loadBeanDefinitions("autowiring-dependency-constructor-injection.xml");
System.out.println(context.getBean(UserHolder.class));
}
}
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <import resource="beans-ioc-lookup.xml"/> <bean class="com.gcl.dependency.injection.UserHolder" autowire="constructor"/> </beans>
6.5 字段注入
只有手动注入方式,以注解为主:
-
@Autowired(忽略静态字段)
-
@Resource
-
@Inject
public class AnnotationDependencyFieldInjectionDemo {
// Autowire 会忽略静态字段
@Autowired
private UserHolder userHolder;
@Resource
private UserHolder userHolder2;
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(AnnotationDependencyFieldInjectionDemo.class);
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml");
context.refresh();
AnnotationDependencyFieldInjectionDemo demo = context.getBean(AnnotationDependencyFieldInjectionDemo.class);
System.out.println(demo.userHolder);
System.out.println(demo.userHolder == demo.userHolder2);
context.close();
}
@Bean
public UserHolder userHolder(User user) {
// 构造器方式
return new UserHolder(user);
}
}
6.6 方法注入
手动注入:
-
@Autowired
-
@Resource
-
@Inject(可选)、
-
@Bean
public class AnnotationDependencyMethodInjectionDemo {
private UserHolder userHolder;
private UserHolder userHolder2;
@Autowired
public void initUserHolder(UserHolder userHolder) {
this.userHolder = userHolder;
}
@Resource
public void initUserHolder2(UserHolder userHolder2) {
this.userHolder2 = userHolder2;
}
@Bean
public UserHolder userHolder(User user) {
// 构造器方式
return new UserHolder(user);
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(AnnotationDependencyMethodInjectionDemo.class);
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml");
context.refresh();
AnnotationDependencyMethodInjectionDemo demo = context.getBean(AnnotationDependencyMethodInjectionDemo.class);
System.out.println(demo.userHolder);
System.out.println(demo.userHolder == demo.userHolder2);
context.close();
}
}
6.7 回调注入
Aware 系列接口回调注入(内建依赖)
| 内建接口 | 说明 |
|---|---|
| BeanFactoryAware | 获取 IoC 容器——BeanFactory |
| ApplicationContextAware | 获取 Spring 应用上下文——ApplicationContext 对象 |
| EnvironmentAware | 获取 Environment 对象 |
| ResourceLoaderAware | 获取资源加载器对象——ResourceLoader |
| BeanClassLoaderAware | 获取加载当前 Bean Class 的 ClassLoader |
| BeanNameAware | 获取当前 Bean的名称 |
| MessageSourceAware | 获取 MessageSource 对象,用于 Spring 国际化 |
| ApplicationEventPublisherAware | 获取 ApplicationEventPublisher 对象,用于 Spring 事件 |
| EmbeddedValueResolverAware | 获取 StringValueResolver 对象,用于占位符处理 |
6.8 依赖注入类型选择
-
低依赖:构造器注入(Spring 官方推荐注入方式)
-
多依赖:Setter 方法注入
-
便利性:字段注入(Spring 和 Spring 都不推荐使用字段注入)
-
声明类:方法注入
6.9 基础类型注入
-
原生类型(Primitive):boolean、byte、char、short、int、long、float、double
-
标量类型(Scalar):Number、Character、Boolean、Enum、Locale、Charset、Currency、Properties、UUID
-
常规类型(General):Object、String、TimeZone、Calendar、Optional等
-
Spring 类型:Resource、InputSource、Formatter
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- bean definitions here --> <bean id="user" class="com.gcl.bean.User"> <property name="name" value="foo"/> <property name="age" value="18"/> <!-- 枚举类型注入 --> <property name="city" value="BEIJING"/> <!-- Resource 对象注入--> <property name="configFileLocation" value="classpath:user-config.properties"/> </bean> <bean id="superUser" class="com.gcl.bean.SuperUser" parent="user" primary="true"> <property name="level" value="1"/> </bean> <bean id="userRepository" class="com.gcl.bean.UserRepository" autowire="byType"/> <!-- <property name="users">--> <!-- <util:list>--> <!-- <ref bean="user"/>--> <!-- <ref bean="superUser"/>--> <!-- </util:list>--> <!-- </property>--> <!-- </bean>--> <bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean"> <property name="targetBeanName" value="user"/> </bean> </beans>
6.10 集合类型注入
-
数组类型(Array):原生类型、标量类型、常规类型、Spring 类型
-
集合类型(Collection)
-
Collection:List、Set(SortedSet、NavigableSet、EnumSet)
-
Map:Properties
-
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- bean definitions here --> <bean id="user" class="com.gcl.bean.User"> <property name="name" value="foo"/> <property name="age" value="18"/> <property name="city" value="BEIJING"/> <property name="cities" value="BEIJING,HANGZHOU"/> <property name="cityList"> <list> <value>BEIJING</value> <value>HANGZHOU</value> <value>SHANGHAI</value> </list> </property> <property name="citySet"> <set> <value>HANGZHOU</value> <value>SHANGHAI</value> </set> </property> <property name="cityMap"> <map> <entry key="beijing" value="BEIJING"/> <entry key="shanghai" value="SHANGHAI"/> </map> </property> <property name="configFileLocation" value="classpath:user-config.properties"/> </bean> <bean id="superUser" class="com.gcl.bean.SuperUser" parent="user" primary="true"> <property name="level" value="1"/> </bean> <bean id="userRepository" class="com.gcl.bean.UserRepository" autowire="byType"/> <!-- <property name="users">--> <!-- <util:list>--> <!-- <ref bean="user"/>--> <!-- <ref bean="superUser"/>--> <!-- </util:list>--> <!-- </property>--> <!-- </bean>--> <bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean"> <property name="targetBeanName" value="user"/> </bean> </beans>
6.11 限定注入
-
@Qualifier 限定注入
-
通过 Bean 名称限定
-
通过分组限定
-
-
基于 @Qualifier 扩展限定
-
自定义注解——如 Spring Cloud @LoadBalance
-
public class AnnotationQualifierDependencyInjectionDemo {
@Autowired
@Qualifier("user")
private User user;
@Autowired
private Collection<User> allUsers;
@Autowired
@Qualifier
private Collection<User> qualifiedUsers;
// 通过使用 @Qualifier 进行逻辑分组
@Bean
@Qualifier
public User user1() {
User user = new User();
user.setName("user1");
return user;
}
// 通过使用 @Qualifier 进行逻辑分组
@Bean
@Qualifier
public User user2() {
User user = new User();
user.setName("user2");
return user;
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(AnnotationQualifierDependencyInjectionDemo.class);
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml");
context.refresh();
AnnotationQualifierDependencyInjectionDemo bean = context.getBean(AnnotationQualifierDependencyInjectionDemo.class);
System.out.println(bean.user);
System.out.println(bean.allUsers);
System.out.println(bean.qualifiedUsers);
context.close();
}
}
6.12 延迟注入
-
使用 API ObjectFactory 实现延迟注入
-
单一类型
-
集合类型
-
-
使用 API ObjectProvider 实现延迟注入(推荐使用,安全)
-
单一类型
-
集合类型
-
6.13 依赖处理过程
-
入口——DefaultListableBeanFactory#resolveDependency
-
依赖描述符——DependencyDescriptor
-
自动绑定候选对象处理器——AutowireCandidateResolver
6.14 @Autowire 注入原理
-
元信息解析(MergedBeanDefinitionPostProcessor#postProcessorMergedBeanDefinition 方法中进行)
-
依赖查找(注入过程中包含依赖查找)
-
依赖注入(字段、方法)(AutowiredAnnotationBeanPostProcessor#postProcessorProperties 方法中进行)
6.15 JSR-330 @Inject 注入原理
public AutowiredAnnotationBeanPostProcessor() {
this.autowiredAnnotationTypes.add(Autowired.class);
this.autowiredAnnotationTypes.add(Value.class);
try {
this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
@Inject 注解跟 @Autowired 注解注入流程是相似的,如果引入了 JSR-330 jar 包,就可以使用该注解进行注入
6.16 Java 通用注解注入原理(@Resource等)
CommonAnnotationBeanPostProcessor(比 AutowiredAnnotationBeanPostProcessor 支持更多的注解,除了注入注解,还支持生命周期注解)
-
注入注解
-
javax.xml.ws.WebServiceRef
-
javax.ejb.EJB
-
javax.annotation.Resource
-
-
生命周期注解(构建 LifecycleElement)
-
javax.annotation.PostConstructor
-
javax.annotation.PreDestroy
-
6.17 自定义依赖注入注解
-
基于 AutowiredAnnotationBeanPostProcessor 实现
-
自定义实现
-
生命周期处理
-
InstantiationAwareBeanPostProcessor
-
MergedBeanDefinitionPostProcessor
-
-
元数据
-
InjectElement
-
InjectMetadata
-
-
6.18 面试题
6.18.1 有多少种依赖注入方式
-
构造器注入(少依赖,强制依赖)
-
Setter 注入(多依赖,非强制依赖)
-
字段注入
-
方法注入
-
接口回调注入
6.18.2 你偏好构造器注入还是 Setter 注入?
两种依赖注入均可使用,如果是必须依赖,推荐使用构造器注入,Setter注入可选依赖
依赖注入少用外部注解,包括 Java 注解和 Spring 注解,sh奥用注解就少依赖,使用原始 API、原始构造器参数来进行操作,可以解决依赖问题,同时还能解决线程安全问题(构造器可以确保线程安全问题)
6.18.3 Spring 依赖注入来源有哪些?
依赖查找通过 getBean,依赖注入通过 resolveDependency
-
自定义 Bean(XML、注解、API)
-
内建 Bean
-
内建依赖










