Spring框架@Autowired注解

    科技2022-07-16  131

           看到网上很多人在讨论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-애노테이션-정리-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/

    Processed: 0.009, SQL: 8