看到网上很多人在讨论spring里的几个注解@Autowired, @Resource, @Inject,有时面试也会问,其实没什么用,开发时谁关心呢,好比学习考驾照前,背题目、参加测验,可一旦拿了证就忘了很多交通规则,也好比考研前学习马列主义、毛泽东思想等,考上后就忘得差不多了,但还是有必要了解下。
请先阅读下
Java反射 https://blog.csdn.net/dong19891210/article/details/106053065,
Java 注解 https://blog.csdn.net/dong19891210/article/details/106309665,
一个Spring Bean从无到有的过程 https://blog.csdn.net/dong19891210/article/details/105697175
0. 前言
spring是一款很时尚的java第三方框架,集成了许多技术,像反射、注解、动静代理、设计模式等。
说到spring,不得不提一下bean,可认为它是一种spring池(也就工厂)里的对象,由bean定义,然后按需求配置生成,类比java 自定义类生成具体的实例对象,可以把bean定义当做一种复杂的数据类型,结构大致如下:
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
implements BeanDefinition, Cloneable {
......
@Nullable
private volatile Object beanClass;
@Nullable
private String scope = SCOPE_DEFAULT;
@Nullable
private Boolean lazyInit;
private int autowireMode = AUTOWIRE_NO;
private int dependencyCheck = DEPENDENCY_CHECK_NONE;
@Nullable
private String[] dependsOn;
private boolean autowireCandidate = true;
private boolean primary = false;
private final Map<String, AutowireCandidateQualifier> qualifiers = new LinkedHashMap<>();
@Nullable
private Supplier<?> instanceSupplier;
private boolean nonPublicAccessAllowed = true;
private boolean lenientConstructorResolution = true;
@Nullable
private String factoryBeanName;
@Nullable
private String factoryMethodName;
@Nullable
private ConstructorArgumentValues constructorArgumentValues;
@Nullable
private MutablePropertyValues propertyValues;
.......
}
定义了一个bean的诸多属性,有一项:beanClass,如图
然后定义个简单的java类Student
public class Student {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student[ name=" + name + ", age=" + age + "]";
}
此时就可以把图片里bean的定义A的属性beanClass设置为Student.class.,,如图所演示
正常情况下可以生成bean对象了,比如A1,实际上bean对象的加工处理生成过程很繁琐,逻辑判断多,四个大阶段:
bean定义,实例化,属性注入,初始化。
bean定义阶段略了,解析工作;
实例化阶段: org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException》》》》》》很可能返回代理,看bean如何定义了》》》》》》org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationExceptio》》》》》》 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)》》》org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName)
属性注入: org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw),
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
.......处理阶段
}}
比如bean A有实例A1有依赖 Bean B,要具体指明依赖哪个具体的bean对象B1还是B2。
初始化阶段:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
。。。
((InitializingBean) bean).afterPropertiesSet();
invokeCustomInitMethod(beanName, bean, mbd);
。。。
}
}
下面我以用的spring 5 简单介绍下@Autowired注解如何使用。
1. @Autowired
注解@Auowired是spring框架自带的,在包org.springframework.beans.factory.annotation定义,代码很简洁
package org.springframework.beans.factory.annotation;
...
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
/**
* Declares whether the annotated dependency is required.
* <p>Defaults to {@code true}.
*/
boolean required() default true;
}
它由AutowiredAnnotationBeanPostProcessor处理,
/**
* Create a new {@code AutowiredAnnotationBeanPostProcessor} for Spring's
* standard {@link Autowired @Autowired} and {@link Value @Value} annotations.
* <p>Also supports JSR-330's {@link javax.inject.Inject @Inject} annotation,
* if available.
*/
@SuppressWarnings("unchecked")
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.
}
}
举例:
配置文件spring-annotation-bean.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-4.0.xsd">
<context:annotation-config />
<bean id="student" class="com.spring.model.Student">
<property name="age" value="31" />
<property name="name" value="dongguangming" />
</bean>
<bean id="nanjing" class="com.spring.model.City">
<property name="cityName" value="nanjing" />
</bean>
</beans>
java类
/**
*
* @author dgm
* @describe ""
* @date 2020年10月5日
*/
public class City {
private String cityName;
public String getCityName() {
return cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
}
----不分开写
public class Student {
private Integer age;
private String name;
//@Resource(name="city")
@Autowired
//@Qualifier("cityB")
private City city;
// @Autowired
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public City getCity() {
return city;
}
// @Autowired
public void setCity(City city) {
this.city = city;
}
}
注意属性City city上标有注解@Autowired
测试类:
public class AnnotationConfigurationBeanApp {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
"conf/spring-annotation-bean.xml");
int count = applicationContext.getBeanDefinitionCount();
String[] beanNames = applicationContext.getBeanDefinitionNames();
System.err.println("BeanDefinitionRegistryPostProcessor postProcessBeanFactory阶段总共:"
+ count + " 个Beans\n");
System.err.println(Arrays.asList(beanNames));
Student student = (Student) applicationContext.getBean("student");
System.out.println("Name : " + student.getName());
System.out.println("Age : " + student.getAge());
String city = student.getCity().getCityName();
System.err.println("学生所在地:"+city);
}
}
输出结果
假设城市的bean对象有多个,这是可配合@Qualifier一起使用。
<?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-4.0.xsd">
<context:annotation-config />
<bean id="student" class="com.spring.model.Student">
<property name="age" value="31" />
<property name="name" value="dongguangming" />
</bean>
<bean id="nanjing" class="com.spring.model.City">
<property name="cityName" value="nanjing" />
</bean>
<bean id="shanghai" class="com.spring.model.City">
<property name="cityName" value="shanghai" />
</bean>
</beans>
注意又加了个上海,此时在运行上述测试代码就会报错,
因为有两个城市bean对象,无法识别到底要注入哪个bean对象,此时可以修改java代码
public class Student {
...
@Autowired
@Qualifier(value="shanghai")
private City city;
...
}
特别注意:追加了注解 @Qualifier(value="shanghai")
此时再次执行就好了
有兴趣可以继续研究bean是怎么处理标有@autowired的逻辑,其实不影响开发。
留个问题,让你结合反射、注解、动态代理、设计模式实现类似功能,你怎么设计,先不考虑spring那么多属性注解的情况。
附件:
日志
04:18:27.026 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'student'
04:22:24.086 [main] WARN org.springframework.context.support.ClassPathXmlApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'student': Unsatisfied dependency expressed through field 'city'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.spring.model.City' available: expected single matching bean but found 4: puyang,nanjing,shanghai,beijing
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'student': Unsatisfied dependency expressed through field 'city'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.spring.model.City' available: expected single matching bean but found 4: puyang,nanjing,shanghai,beijing
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:643)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:130)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1422)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:594)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
at org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$8/1826334428.getObject(Unknown Source)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:895)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:144)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:85)
at com.spring.test.AnnotationConfigurationBeanApp.main(AnnotationConfigurationBeanApp.java:24)
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.spring.model.City' available: expected single matching bean but found 4: puyang,nanjing,shanghai,beijing
at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:220)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1284)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1226)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640)
... 16 more
ERROR: JDWP Unable to get JNI 1.2 environment, jvm->GetEnv() return code = -2
JDWP exit error AGENT_ERROR_NO_JNI_ENV(183): [util.c:838]
一些******PostProcessor
参考:
-
How does autowiring work in Spring? https://stackoverflow.com/questions/3153546/how-does-autowiring-work-in-spring
-
How does @Autowired work in Spring framework https://blogs.sap.com/2016/08/11/how-does-autowired-work-in-spring-framework/
-
Understanding Spring Concept of Autowiring https://www.youtube.com/watch?v=WgCKVYJRccI
-
Spring Autowiring - It's a kind of magic - Part 1 https://blog.scottlogic.com/2020/02/25/spring-autowiring-its-a-kind-of-magic.html
-
Autowiring in Spring https://dzone.com/articles/autowiring-in-spring
-
Spring Bean Autowiring – @Autowired https://howtodoinjava.com/spring-core/spring-beans-autowiring-concepts/
-
Spring @Autowired Annotation https://www.journaldev.com/2623/spring-autowired-annotation
-
Spring Auto-Wiring Beans with @Autowired annotation https://mkyong.com/spring/spring-auto-wiring-beans-with-autowired-annotation/
-
Spring @Autowired Guide https://www.amitph.com/spring-autowired-guide/
-
Why You Should Use Constructor Injection in Spring https://reflectoring.io/constructor-injection/
-
Spring @Autowired tutorial http://zetcode.com/spring/autowired/
-
Spring Auto-Wiring Beans With @Autowired annotation [AutowiredAnnotationBeanPostProcessor] https://dzone.com/articles/spring-auto-wiring-beans-with-autowired-annotation-1
-
Talking more about AutowiredAnnotationBeanPostProcessor https://huongdanjava.com/talking-more-about-autowiredannotationbeanpostprocessor.html
-
AutowiredAnnotationBeanPostProcessor test https://www.programcreek.com/java-api-examples/?api=org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
-
@Autowired vs @Resource vs @Inject 的区别 https://einverne.github.io/post/2017/08/autowired-vs-resource-vs-inject.html
-
[Spring] 의존성 주입 애노테이션 정리 - @Autowired, @Resource, @Inject https://atoz-develop.tistory.com/entry/Spring-DI-%EC%95%A0%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EC%A0%95%EB%A6%AC-Autowired-Resource-Inject
-
Spring Framework: @Autowired , @Inject and @Resource https://springbootdev.com/2017/03/01/spring-framework-autowired-inject-and-resource/
-
Spring 注解关键字解析 @Component、@Repository、@Service、@Controller @Resource、@Autowired、@Qualifier https://blog.mimvp.com/article/16892.html
-
Difference Between @Resource, @Autowired and @Inject in Spring Injection https://javabeat.net/difference-resource-autowired-inject-spring-injection/
-
Spring Core Container 源码分析五:@Autowired https://www.shangyang.me/2017/04/05/spring-core-container-sourcecode-analysis-annotation-autowired/