两层意思:
-
注入 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
-
内建依赖