看到网上很多人在讨论spring里的几个注解@Autowired, @Resource, @Inject,有时面试也会问,其实没什么用,开发时谁关心呢,好比学习考驾照前,背题目、参加测验,可一旦拿了证就忘了很多交通规则,也好比考研前学习马列主义、毛泽东思想等,考上后就忘得差不多了,但还是有必要了解下。
请先阅读下
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注解如何使用。
注解@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; }举例:
配置文件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")
此时再次执行就好了
留个问题,让你结合反射、注解、动态代理、设计模式实现类似功能,你怎么设计,先不考虑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
Spring 注解关键字解析 @Component、@Repository、@Service、@Controller @Resource、@Autowired、@Qualifier https://blog.mimvp.com/article/16892.html