依赖注入的模式和类型:Spring提供了哪些依赖注入的模式和类型?
自动绑定(Autowiring):为什么Spring会引入Autowiring?
如果模型字段名称改变了,回会导致依赖关系的改变,导致找不到注入的Bean
自动绑定(Autowiring)模式:各种自动绑定模式的使用场景是什么?
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);
AutowireCapableBeanFactory
int AUTOWIRE_NO = 0;
int AUTOWIRE_BY_NAME = 1;
int AUTOWIRE_BY_TYPE = 2;
int AUTOWIRE_CONSTRUCTOR = 3;
/**
* Constant that indicates determining an appropriate autowire strategy
* through introspection of the bean class.
* @see #createBean
* @see #autowire
* @deprecated as of Spring 3.0: If you are using mixed autowiring strategies,
* prefer annotation-based autowiring for clearer demarcation of autowiring needs.
*/
@Deprecated
int AUTOWIRE_AUTODETECT = 4;
AUTOWIRE_AUTODETECT在spring3.0中已被淘汰
自动绑定(Autowiring)限制和不足:如何理解和挖掘官方文档中深层次的含义?
utowire-candidate 属性是设置当前 Bean 是否为 其他 Bean autowiring 候选者
Setter方法依赖注入:Setter注入的原理是什么?
手动模式
XmlDependencySetterInjectionDemo
AnnotationDependencySetterInjectionDemo
ApiDependencySetterInjectionDemo
底层实现都是通过
definitionBuilder.addPropertyReference(“user”, “superUser”);
自动模式
构造器依赖注入:官方为什么推荐使用构造器注入?
手动模式
XmlDependencyConstructorInjectionDemo
AnnotationDependencyConstructorInjectionDemo
ApiDependencyConstructorInjectionDemo
底层实现都是通过
definitionBuilder.addConstructorArgReference(“superUser”);
自动模式
setter注入的原理吗,为什么注入顺序是不确定的,不是从上往下执行代码嘛?
并不是,由于 Java 反射 API 所返回的 public 方法熟顺序并非定义顺序,所以无法控制先后情况。你可以自行尝试!
字段注入:为什么Spring官方文档没有单独列举这种注入方式?
public class AnnotationDependencyFieldInjectionDemo {
@Autowired
private
// static // @Autowired 会忽略掉静态字段
UserHolder userHolder;
@Resource
private UserHolder userHolder2;
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注册 Configuration Class(配置类) -> Spring Bean
applicationContext.register(AnnotationDependencyFieldInjectionDemo.class);
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(applicationContext);
String xmlResourcePath = "classpath:/META-INF/dependency-lookup-context.xml";
// 加载 XML 资源,解析并且生成 BeanDefinition
beanDefinitionReader.loadBeanDefinitions(xmlResourcePath);
// 启动 Spring 应用上下文
applicationContext.refresh();
// 依赖查找 AnnotationDependencyFieldInjectionDemo Bean
AnnotationDependencyFieldInjectionDemo demo = applicationContext.getBean(AnnotationDependencyFieldInjectionDemo.class);
// @Autowired 字段关联
UserHolder userHolder = demo.userHolder;
System.out.println(userHolder);
System.out.println(demo.userHolder2);
System.out.println(userHolder == demo.userHolder2); // true
// 显示地关闭 Spring 应用上下文
applicationContext.close();
}
@Bean
public UserHolder userHolder(User user) {
return new UserHolder(user);
}
}
@Autowired 会忽略掉静态字段
通常字段注入需要依赖注解,并且没有控制顺序
方法注入:方法注入是@Autowired专利吗?
public class AnnotationDependencyMethodInjectionDemo {
private UserHolder userHolder;
private UserHolder userHolder2;
@Autowired
public void init1(UserHolder userHolder) {
this.userHolder = userHolder;
}
@Resource
public void init2(UserHolder userHolder2) {
this.userHolder2 = userHolder2;
}
@Bean
public UserHolder userHolder(User user) {
return new UserHolder(user);
}
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注册 Configuration Class(配置类) -> Spring Bean
applicationContext.register(AnnotationDependencyMethodInjectionDemo.class);
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(applicationContext);
String xmlResourcePath = "classpath:/META-INF/dependency-lookup-context.xml";
// 加载 XML 资源,解析并且生成 BeanDefinition
beanDefinitionReader.loadBeanDefinitions(xmlResourcePath);
// 启动 Spring 应用上下文
applicationContext.refresh();
// 依赖查找 AnnotationDependencyFieldInjectionDemo Bean
AnnotationDependencyMethodInjectionDemo demo = applicationContext.getBean(AnnotationDependencyMethodInjectionDemo.class);
// @Autowired 字段关联
UserHolder userHolder = demo.userHolder;
System.out.println(userHolder);
System.out.println(demo.userHolder2);
System.out.println(userHolder == demo.userHolder2);
// 显示地关闭 Spring 应用上下文
applicationContext.close();
}
}
@Bean 并非依赖注入,而是它利用依赖注入,比如讲参数注入到方法体内,作为新对象形成的数据。@Bean 实际上是一种 BeanDefinition 的实现方式,不过这个实现可能在 Bean 生命周期中才产生。
Setter 注入是通过 Java Beans 来实现的,而方法注入则是直接通过 Java 反射来做的。当然底层都是 Java 反射~
接口回调注入:回调注入的使用场景和限制有哪些?
public class AwareInterfaceDependencyInjectionDemo implements BeanFactoryAware, ApplicationContextAware {
private static BeanFactory beanFactory;
private static ApplicationContext applicationContext;
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 注册 Configuration Class(配置类) -> Spring Bean
context.register(AwareInterfaceDependencyInjectionDemo.class);
// 启动 Spring 应用上下文
context.refresh();
System.out.println(beanFactory == context.getBeanFactory());
System.out.println(applicationContext == context);
// 显示地关闭 Spring 应用上下文
context.close();
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
AwareInterfaceDependencyInjectionDemo.beanFactory = beanFactory;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
AwareInterfaceDependencyInjectionDemo.applicationContext = applicationContext;
}
}
当任意 Spring Bean 实现类实现了 BeanFactoryAware 接口时,在 Bean 生命周期回调时,会被容器调用 setBeanFactory 方法~
依赖注入类型选择:各种依赖注入有什么样的使用场景?
构造器注入保证的顺序是由于构造器参数的顺序是确定,因此,它们在注入的过程中是按照顺序。而方法注入,由于 Java 反射 API 所获取的数组顺序不确定会导致某种依赖相关的错误。
“通过构造器和Setter的方法来进行第2次的手动API注入”, 这句话怎么理解?关于@Bean组合方式。
@Bean 的方式通常属于半自动方式,许多关联 Bean 需要手动关联。
官方推荐构造器注入
String和Java原生类型也能注入Bean的属性,它们算依赖注入吗?
<bean id="user" class="org.geekbang.thinking.in.spring.ioc.overview.domain.User">
<property name="id" value="1"/>
<property name="name" value="小马哥"/>
<property name="city" value="HANGZHOU"/>
<property name="configFileLocation" value="classpath:/META-INF/user-config.properties"/>
</bean>
集合类型注入:注入Collection和Map类型的依赖区别?还支持哪些集合类型?
<bean id="user" class="org.geekbang.thinking.in.spring.ioc.overview.domain.User">
<property name="id" value="1"/>
<property name="name" value="小马哥"/>
<property name="city" value="HANGZHOU"/>
<property name="workCities" value="BEIJING,HANGZHOU"/>
<property name="lifeCities">
<list>
<value>BEIJING</value>
<value>SHANGHAI</value>
</list>
</property>
<property name="configFileLocation" value="classpath:/META-INF/user-config.properties"/>
</bean>
限定注入:如何限定Bean名称注入?如何实现Bean逻辑分组注入?
@Configuration
public class QualifierAnnotationDependencyInjectionDemo {
@Autowired
private User user; // superUser -> primary =true
@Autowired
@Qualifier("user") // 指定 Bean 名称或 ID
private User namedUser;
// 整体应用上下文存在 4 个 User 类型的 Bean:
// superUser
// user
// user1 -> @Qualifier
// user2 -> @Qualifier
@Autowired
private Collection<User> allUsers; // 2 Beans = user + superUser
@Autowired
@Qualifier
private Collection<User> qualifiedUsers; // 2 Beans = user1 + user2 -> 4 Beans = user1 + user2 + user3 + user4
@Autowired
@UserGroup
private Collection<User> groupedUsers; // 2 Beans = user3 + user4
@Bean
@Qualifier // 进行逻辑分组
public User user1() {
return createUser(7L);
}
@Bean
@Qualifier // 进行逻辑分组
public User user2() {
return createUser(8L);
}
@Bean
@UserGroup
public User user3() {
return createUser(9L);
}
@Bean
@UserGroup
public User user4() {
return createUser(10L);
}
private static User createUser(Long id) {
User user = new User();
user.setId(id);
return user;
}
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注册 Configuration Class(配置类) -> Spring Bean
applicationContext.register(QualifierAnnotationDependencyInjectionDemo.class);
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(applicationContext);
String xmlResourcePath = "classpath:/META-INF/dependency-lookup-context.xml";
// 加载 XML 资源,解析并且生成 BeanDefinition
beanDefinitionReader.loadBeanDefinitions(xmlResourcePath);
// 启动 Spring 应用上下文
applicationContext.refresh();
// 依赖查找 QualifierAnnotationDependencyInjectionDemo Bean
QualifierAnnotationDependencyInjectionDemo demo = applicationContext.getBean(QualifierAnnotationDependencyInjectionDemo.class);
// 期待输出 superUser Bean
System.out.println("demo.user = " + demo.user);
// 期待输出 user Bean
System.out.println("demo.namedUser = " + demo.namedUser);
// 期待输出 superUser user
System.out.println("demo.allUsers = " + demo.allUsers);
// 期待输出 user1 user2
System.out.println("demo.qualifiedUsers = " + demo.qualifiedUsers);
// 期待输出 user3 user4
System.out.println("demo.groupedUsers = " + demo.groupedUsers);
// 显示地关闭 Spring 应用上下文
applicationContext.close();
}
}
注解配置类中使用"@Bean"添加的那几个User在配置类应用属性注入的时候还还有实例化到容器中。
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
SpringCloud中@LoadBalanced 注解就使用到了@Qualifier扩展
引用文章: https://fangshixiang.blog.csdn.net/article/details/100890879
LoadBalancerAutoConfiguration
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
它能把容器内所有RestTemplate类型并且标注有@LoadBalanced注解的Bean全注入进来。
延迟依赖注入:如何实现延迟执行依赖注入?与延迟依赖查找是类似的吗?
@Configuration
public class LazyAnnotationDependencyInjectionDemo {
@Autowired
@Qualifier("user")
private User user; // 实时注入
@Autowired
private ObjectProvider<User> userObjectProvider; // 延迟注入
@Autowired
private ObjectFactory<Set<User>> usersObjectFactory;
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注册 Configuration Class(配置类) -> Spring Bean
applicationContext.register(LazyAnnotationDependencyInjectionDemo.class);
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(applicationContext);
String xmlResourcePath = "classpath:/META-INF/dependency-lookup-context.xml";
// 加载 XML 资源,解析并且生成 BeanDefinition
beanDefinitionReader.loadBeanDefinitions(xmlResourcePath);
// 启动 Spring 应用上下文
applicationContext.refresh();
// 依赖查找 QualifierAnnotationDependencyInjectionDemo Bean
LazyAnnotationDependencyInjectionDemo demo = applicationContext.getBean(LazyAnnotationDependencyInjectionDemo.class);
// 期待输出 superUser Bean
System.out.println("demo.user = " + demo.user);
// 期待输出 superUser Bean
System.out.println("demo.userObjectProvider = " + demo.userObjectProvider.getObject()); // 继承 ObjectFactory
// 期待输出 superUser user Beans
System.out.println("demo.usersObjectFactory = " + demo.usersObjectFactory.getObject());
demo.userObjectProvider.forEach(System.out::println);
// 显示地关闭 Spring 应用上下文
applicationContext.close();
}
}
依赖处理过程:依赖处理时会发生什么?其中与依赖查找的差异在哪?
DefaultListableBeanFactory#resolveDependency–>1.判断是否懒加载–>doResolveDependency()–>2.判断是否是多类型的bean resolveMultipleBeans()–>3.根据类型查找匹配到的bean findAutowireCandidates()–>4.bean个数大于1,选择bean即@Primary修饰 determineAutowireCandidate()—>返回结果
@Autowired注入:@Autowired注入的规则和原理有哪些?
1.在doCreateBean中会先调用applyMergedBeanDefinitionPostProcessors,后执行populateBean
所以会先调用postProcessMergedBeanDefinition后执行InstantiationAwareBeanPostProcessor的postProcessProperties。
2.postProcessProperties中有两个步骤:
(1)findAutowiringMetadata查找注入元数据,没有缓存就创建,具体是上一节内容。最终会返回InjectionMetadata,里面包括待注入的InjectedElement信息(field、method)等等
(2)执行InjectionMetadata的inject方法,具体为AutowiredFieldElement和AutowiredMethodElement的Inject方法
(2.1)AutowiredFieldElement inject具体流程:
(2.1.1)DependencyDescriptor的创建
(2.1.2)调用beanFactory的resolveDependency获取带注入的bean
(2.1.2.1)resolveDependency根据具体类型返回候选bean的集合或primary 的bean
(2.1.3)利用反射设置field
@Autowired注入过程(所有方法都在AutowiredAnnotationBeanPostProcessor#类里)
(1)调用postProcessProperties()方法(spring 5.1之后才是这个方法名,5.1之前是postProcessPropertyValues)
该步骤信息点:
a. postProcessProperties方法会比bean的setXX()方法先调用
b.findAutowiringMetadata()方法会找出一个bean加了@Autowired注解的字段(包括父类的),并且该方法做了缓存
c.xml配置的bean与bean之间是可以有继承关系的,有另一个周期(不是autowired的流程)是把配置super bean的属性合并到当前bean,之后会调用后置方法postProcessMergedBeanDefinition,该方法也会调用一次findAutowiringMetadata
d.经测试,postProcessMergedBeanDefinition会比postProcessProperties先执行,因此调用postProcessProperties时都是直接拿缓存
(2)—>inject方法(获得对应的bean,然后通过反射注入到类的字段上)
(3)inject方法会调用65讲的resolveDependency方法,这方法会根据@Autowired字段信息来匹配出符合条件的bean
JSR-330 @Inject注入:@Inject与@Autowired的注入原理有怎样的联系?
引入依赖
<!-- JSR 303 API -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
@Inject
private User injectedUser;
AutowiredAnnotationBeanPostProcessor可处理 @Autowired @Value @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.
}
}
Java通用注解注入原理:Spring是如何实现@Resource和@EJB等注解注入的?
自定义依赖注入注解:如何最简化实现自定义依赖注入注解?
@Configuration
public class AnnotationDependencyInjectionResolutionDemo {
@MyAutowired
private Optional<User> userOptional; // superUser
@InjectedUser
private User myInjectedUser;
// @Bean(name = AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)
// public static AutowiredAnnotationBeanPostProcessor beanPostProcessor() {
// AutowiredAnnotationBeanPostProcessor beanPostProcessor = new AutowiredAnnotationBeanPostProcessor();
// // @Autowired + @Inject + 新注解 @InjectedUser
// Set<Class<? extends Annotation>> autowiredAnnotationTypes =
// new LinkedHashSet<>(asList(Autowired.class, Inject.class, InjectedUser.class));
// beanPostProcessor.setAutowiredAnnotationTypes(autowiredAnnotationTypes);
// return beanPostProcessor;
// }
@Bean
@Order(Ordered.LOWEST_PRECEDENCE - 3)
@Scope
public static AutowiredAnnotationBeanPostProcessor beanPostProcessor() {
AutowiredAnnotationBeanPostProcessor beanPostProcessor = new AutowiredAnnotationBeanPostProcessor();
beanPostProcessor.setAutowiredAnnotationType(InjectedUser.class);
return beanPostProcessor;
}
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注册 Configuration Class(配置类) -> Spring Bean
applicationContext.register(AnnotationDependencyInjectionResolutionDemo.class);
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(applicationContext);
String xmlResourcePath = "classpath:/META-INF/dependency-lookup-context.xml";
// 加载 XML 资源,解析并且生成 BeanDefinition
beanDefinitionReader.loadBeanDefinitions(xmlResourcePath);
// 启动 Spring 应用上下文
applicationContext.refresh();
// 依赖查找 QualifierAnnotationDependencyInjectionDemo Bean
AnnotationDependencyInjectionResolutionDemo demo = applicationContext.getBean(AnnotationDependencyInjectionResolutionDemo.class);
// 期待输出 superUser Bean
System.out.println("demo.userOptional = " + demo.userOptional);
// 期待输出 superUser Bean
System.out.println("demo.myInjectedUser = " + demo.myInjectedUser);
// 显示地关闭 Spring 应用上下文
applicationContext.close();
}
}
面试题