@Resource注解的原理、源码
⽂章⽬录
前⾔
@Resource注解和@Autowired注解是咱们使⽤Spring的两⼤利器,⽤来进⾏属性注⼊。这篇⽂章来简单分析下@Resource的原理
⼀、@Resource怎么⽤?
很简单的啦,譬如如下:
@Service
public class TestService {
@Resource
ResourceLockDAO testDao;
}
⼆、那么注⼊做了些啥
1.查⼀个类的所有注⼊点
1.1 在Spring中Bean的⽣态位置
Spring的⽣态代码过程中,在推断出类的构造⽅法,且根据构造⽅法创建实例之后,就进⾏了该bean的注⼊点查询过程。
详见AbstractAutowireCapableBeanFactory的doCreateBean⽅法中的applyMergedBeanDefinitionPostProcessors,看源码么,点进去!!
/**
* Apply MergedBeanDefinitionPostProcessors to the specified bean definition,
* invoking their {@code postProcessMergedBeanDefinition} methods.
* @param mbd the merged bean definition for the bean
* @param beanType the actual type of the managed bean instance
* @param beanName the name of the bean
* @see MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition
*/
protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName){
for(BeanPostProcessor bp :getBeanPostProcessors()){
if(bp instanceof MergedBeanDefinitionPostProcessor){
MergedBeanDefinitionPostProcessor bdp =(MergedBeanDefinitionPostProcessor) bp;
bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
}
}
}
可以看到循环调⽤了所有BeanPostProcessor,到MergedBeanDefinitionPostProcessor⼦类,调⽤其postProcessMergedBeanDefinition⽅法,此处不累赘,我直接说了,调⽤的CommonAnnotationBeanPostProcessor的对应⽅法。细⼼的你可能会发现,还有个叫AutowiredAnnotationBeanPostProcessor的类也有该⽅法,从类名,聪明的你应该猜到,这个类的该⽅法是为了处理@Autowired注解。
废话不多说,咱们来看看@Resource注解查询
1.2 detail,源码解读resource和autowired注解的区别
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName){
super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
InjectionMetadata metadata =findResourceMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
咱们主要看findResourceMetadata⽅法
private InjectionMetadata findResourceMetadata(String beanName, final Class<?> clazz, @Nullable PropertyValues pvs){
// Fall back to class name as cache key, for backwards compatibility with custom callers.
String cacheKey =(StringUtils.hasLength(beanName)? beanName : Name());
// Quick check on the concurrent map first, with minimal locking.
InjectionMetadata metadata = (cacheKey);
dsRefresh(metadata, clazz)){
synchronized (this.injectionMetadataCache){
metadata = (cacheKey);
dsRefresh(metadata, clazz)){
if(metadata != null){
metadata.clear(pvs);
}
metadata =buildResourceMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
1. 可以看到此处先从injectionMetadataCache这个map结构的cache⾥获取,如果获取不到才会进⾏下⾯的操作,⼤家是不是觉得没有
必要,因为Spring是单例的,但是Spring只是默认是单例的,万⼀你使⽤的scope是原型呢?是吧?查bean的注⼊点这个操作就不需要多次执⾏了。
2. 看到此处的synchronized原语的上下代码,熟悉单例设计模式的同学应该兴奋了。因为这是咱们的单例模式–懒汉实现⽅式的原理,
也就是双重检查锁,也就是在synchronized的前后分别进⾏判断,防⽌多线程同时处理,最后出错。
下⾯是最重要的代码逻辑,根据重要程度,此处源码不会全部贴上来,会进⾏适当删减
private InjectionMetadata buildResourceMetadata(final Class<?> clazz){
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
do{
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
// 处理属性上的@Resource注解
ReflectionUtils.doWithLocalFields(targetClass, field ->{
if(webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)){
// 如果有@WebServiceRef注解,也处理
}
else if(ejbRefClass != null && field.isAnnotationPresent(ejbRefClass)){
// 如果有@EJB注解也处理
}
else if(field.isAnnotationPresent(Resource.class)){
// 如果加了@Resource注解的field是static的,那就直接报错
if(Modifier.Modifiers())){
throw new IllegalStateException("@Resource annotation is not supported on static fields");
}
if(!Type().getName())){
// 把field包装⼀下加⼊到currElements⾥⾯
currElements.add(new ResourceElement(field, field, null));
}
}
});
//处理⽅法上的@Resource注解
ReflectionUtils.doWithLocalMethods(targetClass, method ->{
//查该⽅法的桥接⽅法
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
/
/判断桥接⽅法和method的⽅法签名是否相同
if(!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)){
return;
}
if(method.MostSpecificMethod(method, clazz))){
if(webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)){
// 处理@WebServiceRef注解
}
else if(ejbRefClass != null && bridgedMethod.isAnnotationPresent(ejbRefClass)){
// 处理@EJB注解
}
else if(bridgedMethod.isAnnotationPresent(Resource.class)){
// 静态⽅法如果加了@Resource注解,则报错
if(Modifier.Modifiers())){
throw new IllegalStateException("@Resource annotation is not supported on static methods");
}
Class<?>[] paramTypes = ParameterTypes();
// 此处还限定了加了@Resource注解的⽅法必须只有⼀个⼊参
if(paramTypes.length !=1){
throw new IllegalStateException("@Resource annotation requires a single-arg method: "+ method);
}
if(!ains(paramTypes[0].getName())){
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new ResourceElement(method, bridgedMethod, pd));
}
}
}
});
elements.addAll(0, currElements);
targetClass = Superclass();
}
while(targetClass != null && targetClass != Object.class);
// 把field和method区分开后,封装成InjectionMetaData返回findResourceMetadata⽅法
return new InjectionMetadata(clazz, elements);
}
在findResourceMetadata⽅法中,会把该InjectionMetadata放⼊到injectionMetadataCache中。
结构如下图:
2. 注⼊
2.1 在Spring中bean的⽣态的位置
回到AbstractAutowireCapableBeanFactory的doCreateBean⽅法,在设置了SingletonFactory之后,会调⽤populateBean⽅法,这个⽅法就是我们的属性注⼊⽅法。
populate⽅法执⾏分为⼏种情况:
1. 调⽤BeanPostProcessor类的postProcessAfterInstantiation,如果有⼀个返回了false,那么就会停⽌这个bean的属性注⼊。⽬前默认全是true,这是框架留给程序员的⼀个扩展点
2. 判断当前的mbd(merged bean definition)设置的⾃动注⼊mode,不过⼀般情况下,我们都不会设置,⽆⾮是byName或者byType
3. 查看当前的mbd有么有设置propertyValues,如果设置了,就拿上进⼊到最重要的实际注⼊逻辑
遍历所有的BeanPostProcessor,判断是不是InstantiationAwareBeanPostProcessor,然后再调⽤其postProcessProperties⽅法。聪明的你⼀定⼜猜到了此处我们实际调⽤的CommonAnnotationBeanPostProcessor的postProcessProperties⽅法。看源码吧
2.2 detail, show me the code
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName){
InjectionMetadata metadata =findResourceMetadata(beanName, Class(), pvs);
try {
metadata.inject(bean, beanName, pvs);
}
catch (Throwable ex){
throw new BeanCreationException(beanName,"Injection of resource dependencies failed", ex);
}
return pvs;
}
1. 可以看到第⼀步是获取InjectionMetadata,也就是咱们在上⾯查注⼊点时⽣成的InjectionMetadata,此处就是直接从
injectionMetadataCache中拿的
2. 第⼆步就是真实的注⼊过程了metadata.inject(bean, beanName, pvs);
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Collection<InjectedElement> checkedElements = this.checkedElements;
Collection<InjectedElement> elementsToIterate =
(checkedElements != null ? checkedElements : this.injectedElements);
if(!elementsToIterate.isEmpty()){
for(InjectedElement element : elementsToIterate){
// log
element.inject(target, beanName, pvs);
}
}
}
获取该mbd的所有注⼊点,for循环调⽤InjectMetadata的inject⽅法:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论