1.@Resource
跟@Autowired
@Resource
标签在日常的Spring开发中跟@Autowired
标签一样经常被用来注入对象用。虽然两者的作用一样,但是还是有区别的。这里简单说一下在注入时候区别
1.使用上的区别
1.@Resource
@Resource
装配顺序
- 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
- 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
- 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
- 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;
2.@Autowired
@Autowired
默认按类型装配(这个注解是属业spring的),默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required
属性为false
2.实现上的区别
关于@Autowired
的实现前面已经分析过Spring源码-----AutowiredAnnotationBeanPostProcessor解析Autowired。@Resource
的实现则是在CommonAnnotationBeanPostProcessor
类中实现的。
2.1 @Resource
的实现
与@Autowired
的实现类似 @Resource
过程也是类似的,这里在基于前面分析@Autowired
的基础上进行讲解。前面说过AnnotationConfigUtils
类的registerAnnotationConfigProcessors
方法的作用以及调用时机,在这个方法中不仅注册了与AutowiredAnnotationBeanPostProcessor
有关的Bean还注册了CommonAnnotationBeanPostProcessor
相关的Bean,后面也说了这些Bean的实例化以及初始化的时机。在CommonAnnotationBeanPostProcessor
中间接也实现了MergedBeanDefinitionPostProcessor
类的postProcessMergedBeanDefinition
方法,在这个方法中进行第一次@Resource
标签的解析以及相关数据的保存,逻辑跟@Autowired
的一样。
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class> beanType, String beanName) {
super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
真正的进行注入的逻辑是在Bean 的生命周期中的postProcessProperties
方法调用的时候完成的。关于生命周期前面用流程图的方式详细分析过Spring的Bean生命周期流程图及代码解释。这里不在描述。在这个方法中,会再次获取一次当前初始化的bean中的贴有 @Resource
标签的Bean,并保存在InjectionMetadata
对象中,然后进行注入。
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
}
return pvs;
}
2.2注入细节上的实现(两个标签注入方式不同的原因)
这两个标签最后都会将需要注入的信息保存在InjectionMetadata
对象中,但是最后注入的逻辑却不一样。
2.2.1 @Resource
注入实现
@Resource
的注入方法最后由自己实现的,先看InjectionMetadata
类的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);
//反射调用注入 --getResourceToInject--
field.set(target, getResourceToInject(target, requestingBeanName));
}
//不是字段则是方法
else {
//检查是否是需要跳过的属性
if (checkPropertySkipping(pvs)) {
return;
}
try {
//获取注入的set方法
Method method = (Method) this.member;
ReflectionUtils.makeAccessible(method);
//反射调用注入 --getResourceToInject--
method.invoke(target, getResourceToInject(target, requestingBeanName));
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
这里主要是判断注入是在字段还是在方法上面,其中在使用反射的方式注入的时候,有一个getResourceToInject
方法。这个方法的作用是从上下文获取注入的对象(如果将A注入到a字段,那么这个方法就是从上下文获取A)。这个方法在InjectionMetadata
是空实现的。在CommonAnnotationBeanPostProcessor
实现了自己的获取对象的逻辑。现在CommonAnnotationBeanPostProcessor
类的内部类ResourceElement
中查看实现的方法逻辑
protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
//检查当前字段需要注入的对象是否延迟初始化,是的创建一个延迟初始化的代理源对象,如果不是则查找需要注入的对象
return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
getResource(this, requestingBeanName));
}
这里进入其中的getResource
方法,因为在buildLazyResourceProxy
方法中也会调用这个方法来获取需要被代理的对象的。
protected Object getResource(LookupElement element, @Nullable String requestingBeanName)
throws NoSuchBeanDefinitionException {
//mappedName是否不为空,这里的mappedName,如果@Resource标签中的lookup属性不是空这为lookup属性,如果lookup是空的
//则进一步获取@Resource中的mappedName属性
//lookupType相当于type属性
if (StringUtils.hasLength(element.mappedName)) {
//使用SimpleJndiBeanFactory获取Bean
return this.jndiFactory.getBean(element.mappedName, element.lookupType);
}
//是否使用JNDI的方式查找Bean,java5的方式,默认是false
if (this.alwaysUseJndiLookup) {
//使用SimpleJndiBeanFactory获取Bean
return this.jndiFactory.getBean(element.name, element.lookupType);
}
//如果resourceFactory不是null,resourceFactory一般不会是null,除非人为设置
if (this.resourceFactory == null) {
throw new NoSuchBeanDefinitionException(element.lookupType,
"No resource factory configured - specify the 'resourceFactory' property");
}
//如果没有指定name也没有指定type则从spring的BeanFactory中按照requestingBeanName获取Bean,,也就是根据beanName获取
return autowireResource(this.resourceFactory, element, requestingBeanName);
}
从上面的分析可以看出来 @Resource
的注入原理,是现根据指定的name或者type来用JNDI的方式注入,如果都没有指定则使用spring的注入的方式来根据字段的名字来注入
2.2.2@Autowired
注入实现
与 @Resource
不一样@Autowired
的注入是在AutowiredAnnotationBeanPostProcessor
类中实现了InjectionMetadata
类的inject
方法。
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
Object value;
//是否已经存在缓存需要注入的对象和被注入对象的依赖关系,是的则获取缓存的对象
if (this.cached) {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}
else {
//创建依赖关系描述对象
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
Set autowiredBeanNames = new LinkedHashSet<>(1);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();
try {
//从beanFactory中获取需要注入的对象
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
synchronized (this) {
if (!this.cached) {
if (value != null || this.required) {
this.cachedFieldValue = desc;
//将要被注入的Bean注册要当前要注入对象的依赖Bean
registerDependentBeans(beanName, autowiredBeanNames);
if (autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
this.cachedFieldValue = new ShortcutDependencyDescriptor(
desc, autowiredBeanName, field.getType());
}
}
}
else {
this.cachedFieldValue = null;
}
this.cached = true;
}
}
}
if (value != null) {
ReflectionUtils.makeAccessible(field);
//设置值
field.set(bean, value);
}
}
}
可以看到@Autowired
的注入逻辑是先看是否注入过,如果是的则获取已经存在的依赖关系,如果还不存在则创建一个依赖关系对象,并根据beanName去spring中寻找需要注入的对象然后设置到依赖关系中并保存起来。最后将寻找到的bean设置到字段中。
2.@PostConstruct
跟@PreDestroy
2.1@PostConstruct
跟@PreDestroy
的获取
@PostConstruct
跟@PreDestroy
的实现是在CommonAnnotationBeanPostProcessor
的父类InitDestroyAnnotationBeanPostProcessor
中实现的。
两个注解的的实现跟前面两个注入对象的标签的实现也是有点类似的,在postProcessMergedBeanDefinition
方法中会先寻找一次带有@PostConstruct
或@PreDestroy
注解方法,分别保存到由LifecycleElement
组成的两个集合中,一个是初始化方法集合initMethods
一个是销毁方法集合destroyMethods
private LifecycleMetadata buildLifecycleMetadata(final Class> clazz) {
//检查是否存在初始化方法标签集合initAnnotationType或者销毁方法标签集合destroyAnnotationType中的标签,不存在则返回空的LifecycleMetadata对象
if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) {
return this.emptyLifecycleMetadata;
}
//初始化方法集合
List initMethods = new ArrayList<>();
//销毁方法集合
List destroyMethods = new ArrayList<>();
Class> targetClass = clazz;
do {
final List currInitMethods = new ArrayList<>();
final List currDestroyMethods = new ArrayList<>();
//迭代目标类中的方法
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
//查找贴有初始化方法标签集合initAnnotationType中标签的方法
if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
LifecycleElement element = new LifecycleElement(method);
currInitMethods.add(element);
if (logger.isTraceEnabled()) {
logger.trace("Found init method on class [" + clazz.getName() + "]: " + method);
}
}
//查找贴有销毁方法标签集合destroyAnnotationType中的标签的方法
if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
currDestroyMethods.add(new LifecycleElement(method));
if (logger.isTraceEnabled()) {
logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method);
}
}
});
//将找到的初始化方法保存到初始化方法集合
initMethods.addAll(0, currInitMethods);
//将找到的销毁方法保存到销毁方法集合
destroyMethods.addAll(currDestroyMethods);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
//如果初始化方法跟销毁都是空则返回空的LifecycleMetadata,否则返回包含他们的LifecycleMetadata
return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata :
new LifecycleMetadata(clazz, initMethods, destroyMethods));
}
2.2@PostConstruct
跟@PreDestroy
方法的调用
前面已经分析了贴有注解方法的获取,剩下的就是调用了。
2.2.1贴有@PostConstruct
方法的调用
@PostConstruct
解析后,调用贴有这个标签的方法的位置在Bean的生命周期中的postProcessBeforeInitialization
方法,在InitDestroyAnnotationBeanPostProcessor
中。
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//获取生命周期方法,转换为LifecycleMetadata对象
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
//调用对象中的方法
metadata.invokeInitMethods(bean, beanName);
}
catch (InvocationTargetException ex) {
throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
}
return bean;
}
2.2.1贴有@PreDestroy
方法的调用
逻辑上跟@PostConstruct
一样,只不过是调用的位置不一样,在Bean的生命周期中的postProcessBeforeDestruction
中调用
@Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
//获取生命周期方法,转换为LifecycleMetadata对象
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
//调用对象中的方法
metadata.invokeDestroyMethods(bean, beanName);
}
catch (InvocationTargetException ex) {
String msg = "Destroy method on bean with name '" + beanName + "' threw an exception";
if (logger.isDebugEnabled()) {
logger.warn(msg, ex.getTargetException());
}
else {
logger.warn(msg + ": " + ex.getTargetException());
}
}
catch (Throwable ex) {
logger.warn("Failed to invoke destroy method on bean with name '" + beanName + "'", ex);
}
}
到这里就解析完毕了