【1】注解用法java
根据@Autowired注解的源码,能够看到该注解能够做用在构造器、参数、方法、属性,都是从容器中获取参数组件的值数组
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
复制代码
相关注解:markdown
- @Autowired:默认按类型装配,若是咱们想使用按名称装配,能够结合@Qualifier注解一块儿使用(Spring提供)
- @Qualifier():指定装配的bean,多个实例时能够结合@Autowired一块儿使用
- @Primary:自动装配时当出现多个bean候选者时,被注解为
@Primary
的bean将做为首选者- @Resource:默认按名称装配,当找不到与名称匹配的bean才会按类型装配(不支持
@Primary
和@Autowired(required = false)
功能,JDK提供)- @Inject:须要导入javax.inject的包,和Autowired功能同样,可是没有
required=false
功能(JDK提供)
【2】自动装配app
Spring利用依赖注入(DI)完成对IOC容器中各个组件的依赖关系赋值ide
@Autowired
自动注入(Spring提供的):oop
@Autowired(required = false)
指定非必须就不会报错@Primary
的bean将做为首选者,不然将抛出异常,若是使用了@Qualifier()
指定装配的bean,则仍是使用明确指定装配的bean@Resource(JSR250)和@Inject(JSR330)(JDK提供的)源码分析
@Resource:post
@Primary
和@Autowired(required = false)
功能@Inject:ui
required=false
功能【3】@Autowired和@Resource注解的区别this
这里只对@Autowired注解标注在属性位置进行实例分析
【1】@Autowired注解
// 启动类
@Test
public void TestMain() {
// 建立IOC容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = applicationContext.getBean(UserService.class);
System.out.println("userService:" + userService);
}
// Service
@Service
public class UserService {
@Autowired(required = false) // 指定非必须
@Qualifier("userDao2") // 指定装配bean
private UserDao userDao;
@Override
public String toString() {
return "UserService{" +
"userDao=" + userDao +
'}';
}
}
// Dao
@Repository
public class UserDao {
private String label = "1";
public void setLabel(String label) {
this.label = label;
}
@Override
public String toString() {
return "UserDao{" +
"label='" + label + '\'' +
'}';
}
}
// 配置类
@Configuration
@ComponentScan({"dao","service","controller"})
public class AppConfig {
@Primary // 首选装配bean
@Bean("userDao2")
public UserDao userDao(){
UserDao userDao = new UserDao();
userDao.setLabel("2");
return userDao;
}
}
复制代码
输出结果以下,因为上面使用@Qualifier("userDao2")
指定了要装配的bean,因此这里输出的是label=’2‘:
- 若是将
@Qualifier("userDao2")
改成@Qualifier("userDao")
,则装配的是label=’1‘
【2】@Resource注解
@Service
public class UserService {
@Resource(name = "userDao2",type = UserDao.class)
private UserDao userDao;
@Override
public String toString() {
return "UserService{" +
"userDao=" + userDao +
'}';
}
}
复制代码
- 默认按照组件名称进行装配,也能够指定名称进行装配
- 当找不到与名称匹配的bean会按类型装配
- 不支持
@Primary
和@Autowired(required = false)
功能- 若是同时指定了name和type,则从Spring上下文中找到惟一匹配的bean进行装配,找不到则抛出异常。
- 若是指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
- 若是指定了type,则从上下文中找到相似匹配的惟一bean进行装配,找不到或是找到多个,都会抛出异常。
- 若是既没有指定name,又没有指定type,则自动按照byName方式进行装配;若是没有匹配,则回退为一个原始类型进行匹配,若是匹配则自动装配。
这里对@Autowired注解底层进行源码分析
@Autowired是用来装配bean的,确定和bean的实例化有关,先通过了refresh方法,在finishBeanFactoryInitialization方法中getBean,而后走getObject的时候触发bean的初始化。bean的初始化是一个很复杂地方,在AbstractAutowireCapableBeanFactory#doCreateBean方法中,先建立一个BeanWrapper,它的内部成员变量wrappedObject中存放的就是实例化的MyService对象,Spring Bean的生命周期源码详解 - 【Spring底层原理】,再日后进入populateBean方法进行属性注入
Spring对autowire注解的实现逻辑位于类:AutowiredAnnotationBeanPostProcessor#postProcessProperties
之中,——>findAutowiringMetadata——>buildAutowiringMetadata,核心代码就在buildAutowiringMetadata方法里面
private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
return InjectionMetadata.EMPTY;
} else {
List<InjectedElement> elements = new ArrayList();
// 须要处理的目标类
Class targetClass = clazz;
do {
List<InjectedElement> currElements = new ArrayList();
// 经过反射获取该类全部的字段,并遍历每个字段,并经过方法findAutowiredAnnotation遍历每个字段的所用注解,并若是用autowired修饰了,则返回auotowired相关属性
ReflectionUtils.doWithLocalFields(targetClass, (field) -> {
MergedAnnotation<?> ann = this.findAutowiredAnnotation(field);
if (ann != null) {
// 校验autowired注解是否用在了static方法上
if (Modifier.isStatic(field.getModifiers())) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Autowired annotation is not supported on static fields: " + field);
}
return;
}
// 判断是否指定了required
boolean required = this.determineRequiredStatus(ann);
currElements.add(new AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement(field, required));
}
});
// 和上面同样的逻辑,可是是经过反射处理类的method
ReflectionUtils.doWithLocalMethods(targetClass, (method) -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
MergedAnnotation<?> ann = this.findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Autowired annotation is not supported on static methods: " + method);
}
return;
}
if (method.getParameterCount() == 0 && this.logger.isInfoEnabled()) {
this.logger.info("Autowired annotation should only be used on methods with parameters: " + method);
}
boolean required = this.determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new AutowiredAnnotationBeanPostProcessor.AutowiredMethodElement(method, required, pd));
}
}
});
// 用@Autowired修饰的注解可能不止一个,所以都加在currElements这个容器里面,一块儿处理
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
} while(targetClass != null && targetClass != Object.class);
return InjectionMetadata.forElements(elements, clazz);
}
}
复制代码
- 获取须要处理的目标类
- 经过doWithLocalFields方法传入目标类参数,经过反射获取该类全部的字段,并遍历每个字段,并经过方法findAutowiredAnnotation遍历每个字段的所用注解,并若是用autowired修饰了,则返回auotowired相关属性
- 判断autowired注解是否用在了static方法上
- 若有多个@Autowired修饰的注解,都加在currElements这个容器里面,一块儿处理
最后返回包含全部带有autowire注解修饰的一个InjectionMetadata集合,以下
public InjectionMetadata(Class<?> targetClass, Collection<InjectionMetadata.InjectedElement> elements) {
this.targetClass = targetClass;
this.injectedElements = elements;
}
复制代码
有了目标类,与全部须要注入的元素集合以后,咱们就能够实现autowired的依赖注入逻辑了,实现的方法以下:
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) {
if (!this.validatedBeanNames.contains(beanName)) {
if (!this.shouldSkip(this.beanFactory, beanName)) {
List<String> invalidProperties = new ArrayList();
PropertyDescriptor[] var6 = pds;
int var7 = pds.length;
for(int var8 = 0; var8 < var7; ++var8) {
PropertyDescriptor pd = var6[var8];
if (this.isRequiredProperty(pd) && !pvs.contains(pd.getName())) {
invalidProperties.add(pd.getName());
}
}
if (!invalidProperties.isEmpty()) {
throw new BeanInitializationException(this.buildExceptionMessage(invalidProperties, beanName));
}
}
this.validatedBeanNames.add(beanName);
}
return pvs;
}
复制代码
调用InjectionMetadata中定义的inject方法:
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Collection<InjectionMetadata.InjectedElement> checkedElements = this.checkedElements;
Collection<InjectionMetadata.InjectedElement> elementsToIterate = checkedElements != null ? checkedElements : this.injectedElements;
if (!((Collection)elementsToIterate).isEmpty()) {
Iterator var6 = ((Collection)elementsToIterate).iterator();
while(var6.hasNext()) {
InjectionMetadata.InjectedElement element = (InjectionMetadata.InjectedElement)var6.next();
element.inject(target, beanName, pvs);
}
}
}
复制代码
进行遍历,而后调用inject方法,inject方法其实现逻辑以下:
protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs) throws Throwable {
if (this.isField) {
Field field = (Field)this.member;
// 暴力破解的方法,经过反射技术对对象进行实例化和赋值
ReflectionUtils.makeAccessible(field);
field.set(target, this.getResourceToInject(target, requestingBeanName));
} else {
if (this.checkPropertySkipping(pvs)) {
return;
}
try {
Method method = (Method)this.member;
ReflectionUtils.makeAccessible(method);
// 注入的bean的名字,这个方法的功能就是根据这个bean的名字去拿到它
method.invoke(target, this.getResourceToInject(target, requestingBeanName));
} catch (InvocationTargetException var5) {
throw var5.getTargetException();
}
}
}
复制代码
- 使用了反射技术,分红字段和方法去处理的。
- makeAccessible这样的能够称之为暴力破解的方法,经过反射技术对对象进行实例化和赋值
- getResourceToInject方法的参数就是要注入的bean的名字,这个方法的功能就是根据这个bean的名字去拿到它
@AutoWired自动注入过程图: