Spring框架@Autowired注解

       看到网上很多人在讨论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

 

 

参考: 

  1. How does autowiring work in Spring? https://stackoverflow.com/questions/3153546/how-does-autowiring-work-in-spring

  2. How does @Autowired work in Spring framework https://blogs.sap.com/2016/08/11/how-does-autowired-work-in-spring-framework/

  3. Understanding Spring Concept of Autowiring  https://www.youtube.com/watch?v=WgCKVYJRccI

  4. 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

  5. Autowiring in Spring  https://dzone.com/articles/autowiring-in-spring

  6. Spring Bean Autowiring – @Autowired https://howtodoinjava.com/spring-core/spring-beans-autowiring-concepts/

  7. Spring @Autowired Annotation https://www.journaldev.com/2623/spring-autowired-annotation

  8. Spring Auto-Wiring Beans with @Autowired annotation https://mkyong.com/spring/spring-auto-wiring-beans-with-autowired-annotation/

  9. Spring @Autowired Guide  https://www.amitph.com/spring-autowired-guide/

  10. Why You Should Use Constructor Injection in Spring https://reflectoring.io/constructor-injection/

  11. Spring @Autowired tutorial http://zetcode.com/spring/autowired/

  12. Spring Auto-Wiring Beans With @Autowired annotation [AutowiredAnnotationBeanPostProcessor] https://dzone.com/articles/spring-auto-wiring-beans-with-autowired-annotation-1

  13. Talking more about AutowiredAnnotationBeanPostProcessor https://huongdanjava.com/talking-more-about-autowiredannotationbeanpostprocessor.html

  14. AutowiredAnnotationBeanPostProcessor test https://www.programcreek.com/java-api-examples/?api=org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor

  15. @Autowired vs @Resource vs @Inject 的区别 https://einverne.github.io/post/2017/08/autowired-vs-resource-vs-inject.html

  16. [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

  17. Spring Framework: @Autowired , @Inject and @Resource https://springbootdev.com/2017/03/01/spring-framework-autowired-inject-and-resource/

  18. Spring 注解关键字解析 @Component、@Repository、@Service、@Controller @Resource、@Autowired、@Qualifier https://blog.mimvp.com/article/16892.html

  19. Difference Between @Resource, @Autowired and @Inject in Spring Injection  https://javabeat.net/difference-resource-autowired-inject-spring-injection/

  20. Spring Core Container 源码分析五:@Autowired https://www.shangyang.me/2017/04/05/spring-core-container-sourcecode-analysis-annotation-autowired/

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值