Spring ⑤ @Resource @Inject 注解依赖注入详解

Spring ⑤ @Resource @Inject 注解依赖注入详解

Spring ⑤ @Resource @Inject 注解依赖注入详解

Spring 源码系列文章会遵循由浅入深,由易到难,由宏观到微观的原则,目标是尽量降低学习难度,而不是一上来就迷失在源码当中. 文章会从一个场景作为出发点,针对性的目的性极强的针对该场景对 Spring 的实现原理,源码进行探究学习。该系列文章会让你收获什么? 从对 Spring 的使用者成为 Spring 专家。该文章会同步在微信公众号 【DevXJava】, 方便在微信客户端阅读。

本章的内容是对 bean 创建过程中依赖项注入的探究,针对 @Resource 和 @Inject (JSR330 标准) 注解进行讲解分析。

@Inject 注解依赖注入

在第4章 Spring ④ Autowired 注解依赖注入详解 | 看不懂你打我 中我们详细分析了使用 @Autowired 注解如何进行依赖注入的过程(对于理解了上期内容的读者,本期内容对你来说会是非常简单的。)。核心是 AutowiredAnnotationBeanPostProcessor 的 postProcessProperties 方法实现的。AutowiredAnnotationBeanPostProcessor 不仅支持处理 @Autowired 注解同时也支持 JSR-330 标准的 @Inject 注解。所以在使用过程中 @Autowired 注解和 @Inject 注解并没有实质上的不同,理解了 @Autowired 的原理也就理解了 @Inject 。

在 AutowiredAnnotationBeanPostProcessor 构造函数中将支持的注解添加到 autowiredAnnotationTypes 中。如果项目中引入了 JSR-330 的 jar 包会将 @Inject 注解也添加进去。

/**

* Create a new {@code AutowiredAnnotationBeanPostProcessor} for Spring's

* standard {@link Autowired @Autowired} and {@link Value @Value} annotations.

*

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)

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.

}

}

AutowiredAnnotationBeanPostProcessor#findAutowiredAnnotation

将需要注入的元素上的注解和 autowiredAnnotationTypes 中的注解进行比对。

@Nullable

private MergedAnnotation findAutowiredAnnotation(AccessibleObject ao) {

MergedAnnotations annotations = MergedAnnotations.from(ao);

for (Class type : this.autowiredAnnotationTypes) {

MergedAnnotation annotation = annotations.get(type);

if (annotation.isPresent()) {

return annotation;

}

}

return null;

}

JSR-330

Java 依赖注入标准(JSR-330,Dependency Injection for Java)1.0 规范 2009 年 10 月 13 日发布。该规范主要是面向依赖注入使用者,而对注入器实现、配置并未作详细要求。目前 Spring 、Guice 已经开始兼容该规范,JSR-299(Contexts and Dependency Injection for Java EE platform,参考实现 Weld )在依赖注入上也使用该规范。JSR-330 规范并未按 JSR 惯例发布规范文 档,只发布了规范 API 源码,本文翻译了该规范 API 文档(Javadoc )以作为对 Java 依赖注入标准规 范的简介。

spring 从 3.0 版本开始,实现了 JSR-330 标准。

@Resource 注解依赖注入

场景

该场景中包含 属性注入 、方法注入。与 @Autowired 依赖注入场景不同的是,不再使用 AutowiredAnnotationBeanPostProcessor 而是换成了 CommonAnnotationBeanPostProcessor.

public class ResourceInjectionExp {

public static void main(String[] args) {

// @Resource 注解依赖注入

DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

CommonAnnotationBeanPostProcessor processor = new CommonAnnotationBeanPostProcessor();

processor.setBeanFactory(factory);

factory.addBeanPostProcessor(processor);

factory.registerBeanDefinition("bean1" , BeanDefinitionBuilder

.genericBeanDefinition(Bean1.class)

.getBeanDefinition());

factory.registerBeanDefinition("bean2" , BeanDefinitionBuilder

.genericBeanDefinition(Bean2.class)

.getBeanDefinition());

factory.registerBeanDefinition("bean3" , BeanDefinitionBuilder

.genericBeanDefinition(Bean3.class)

.getBeanDefinition());

Bean1 bean1 = factory.getBean(Bean1.class);

System.out.println("bean1 -> " + bean1);

}

@ToString

static class Bean1 {

@Resource

Bean2 bean2;

Bean3 bean3;

public Bean1() {

System.out.println("======================== bean1 实例化");

}

@Resource

public void setBean3(Bean3 bean3) {

this.bean3 = bean3;

}

}

static class Bean2 {

public Bean2() {

System.out.println("++++++++++++++++++++++++++ bean2 实例化");

}

}

static class Bean3 {

public Bean3() {

System.out.println("++++++++++++++++++++++++++ Bean3 实例化");

}

}

}

CommonAnnotationBeanPostProcessor

CommonAnnotationBeanPostProcessor 静态代码块中将 @Resource 注解加到了 resourceAnnotationTypes 中.

static {

resourceAnnotationTypes.add(Resource.class);

webServiceRefClass = loadAnnotationType("javax.xml.ws.WebServiceRef");

if (webServiceRefClass != null) {

resourceAnnotationTypes.add(webServiceRefClass);

}

ejbClass = loadAnnotationType("javax.ejb.EJB");

if (ejbClass != null) {

resourceAnnotationTypes.add(ejbClass);

}

}

postProcessProperties

和 AutowiredAnnotationBeanPostProcessor 的 postProcessProperties 几乎是一模一样.

@Override

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;

}

findResourceMetadata

private InjectionMetadata findResourceMetadata(String beanName, 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 : clazz.getName());

// Quick check on the concurrent map first, with minimal locking.

InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);

if (InjectionMetadata.needsRefresh(metadata, clazz)) {

synchronized (this.injectionMetadataCache) {

metadata = this.injectionMetadataCache.get(cacheKey);

if (InjectionMetadata.needsRefresh(metadata, clazz)) {

if (metadata != null) {

metadata.clear(pvs);

}

metadata = buildResourceMetadata(clazz);

this.injectionMetadataCache.put(cacheKey, metadata);

}

}

}

return metadata;

}

buildResourceMetadata

private InjectionMetadata buildResourceMetadata(Class clazz) {

if (!AnnotationUtils.isCandidateClass(clazz, resourceAnnotationTypes)) {

return InjectionMetadata.EMPTY;

}

List elements = new ArrayList<>();

Class targetClass = clazz;

do {

final List currElements = new ArrayList<>();

ReflectionUtils.doWithLocalFields(targetClass, field -> {

if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {

if (Modifier.isStatic(field.getModifiers())) {

throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");

}

currElements.add(new WebServiceRefElement(field, field, null));

}

else if (ejbClass != null && field.isAnnotationPresent(ejbClass)) {

if (Modifier.isStatic(field.getModifiers())) {

throw new IllegalStateException("@EJB annotation is not supported on static fields");

}

currElements.add(new EjbRefElement(field, field, null));

}

else if (field.isAnnotationPresent(Resource.class)) {

if (Modifier.isStatic(field.getModifiers())) {

throw new IllegalStateException("@Resource annotation is not supported on static fields");

}

if (!this.ignoredResourceTypes.contains(field.getType().getName())) {

currElements.add(new ResourceElement(field, field, null));

}

}

});

ReflectionUtils.doWithLocalMethods(targetClass, method -> {

Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);

if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {

return;

}

if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {

if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {

if (Modifier.isStatic(method.getModifiers())) {

throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");

}

if (method.getParameterCount() != 1) {

throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);

}

PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);

currElements.add(new WebServiceRefElement(method, bridgedMethod, pd));

}

else if (ejbClass != null && bridgedMethod.isAnnotationPresent(ejbClass)) {

if (Modifier.isStatic(method.getModifiers())) {

throw new IllegalStateException("@EJB annotation is not supported on static methods");

}

if (method.getParameterCount() != 1) {

throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);

}

PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);

currElements.add(new EjbRefElement(method, bridgedMethod, pd));

}

else if (bridgedMethod.isAnnotationPresent(Resource.class)) {

if (Modifier.isStatic(method.getModifiers())) {

throw new IllegalStateException("@Resource annotation is not supported on static methods");

}

Class[] paramTypes = method.getParameterTypes();

if (paramTypes.length != 1) {

throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);

}

if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {

PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);

currElements.add(new ResourceElement(method, bridgedMethod, pd));

}

}

}

});

elements.addAll(0, currElements);

targetClass = targetClass.getSuperclass();

}

while (targetClass != null && targetClass != Object.class);

return InjectionMetadata.forElements(elements, clazz);

}

ResourceElement

private class ResourceElement extends LookupElement {

private final boolean lazyLookup;

public ResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {

super(member, pd);

Resource resource = ae.getAnnotation(Resource.class);

String resourceName = resource.name();

Class resourceType = resource.type();

this.isDefaultName = !StringUtils.hasLength(resourceName);

if (this.isDefaultName) {

resourceName = this.member.getName();

if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) {

resourceName = Introspector.decapitalize(resourceName.substring(3));

}

}

else if (embeddedValueResolver != null) {

resourceName = embeddedValueResolver.resolveStringValue(resourceName);

}

if (Object.class != resourceType) {

checkResourceType(resourceType);

}

else {

// No resource type specified... check field/method.

resourceType = getResourceType();

}

this.name = (resourceName != null ? resourceName : "");

this.lookupType = resourceType;

String lookupValue = resource.lookup();

this.mappedName = (StringUtils.hasLength(lookupValue) ? lookupValue : resource.mappedName());

Lazy lazy = ae.getAnnotation(Lazy.class);

this.lazyLookup = (lazy != null && lazy.value());

}

@Override

protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {

return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :

getResource(this, requestingBeanName));

}

}

CommonAnnotationBeanPostProcessor#getResource

/**

* Obtain the resource object for the given name and type.

* @param element the descriptor for the annotated field/method

* @param requestingBeanName the name of the requesting bean

* @return the resource object (never {@code null})

* @throws NoSuchBeanDefinitionException if no corresponding target resource found

*/

protected Object getResource(LookupElement element, @Nullable String requestingBeanName)

throws NoSuchBeanDefinitionException {

// JNDI lookup to perform?

String jndiName = null;

if (StringUtils.hasLength(element.mappedName)) {

jndiName = element.mappedName;

}

else if (this.alwaysUseJndiLookup) {

jndiName = element.name;

}

if (jndiName != null) {

if (this.jndiFactory == null) {

throw new NoSuchBeanDefinitionException(element.lookupType,

"No JNDI factory configured - specify the 'jndiFactory' property");

}

return this.jndiFactory.getBean(jndiName, element.lookupType);

}

// Regular resource autowiring

if (this.resourceFactory == null) {

throw new NoSuchBeanDefinitionException(element.lookupType,

"No resource factory configured - specify the 'resourceFactory' property");

}

return autowireResource(this.resourceFactory, element, requestingBeanName);

}

CommonAnnotationBeanPostProcessor#autowireResource

autowireResource 中会根据 @Resource 注解 name 属性的值作为 bean 的名称去寻找或创建 bean. @Resource 注解根据名称注入的优先级更高,如果 name 属性的值和实际的 bean 类型不一致会抛出 BeanNotOfRequiredTypeException. 而 @Autowired 和 @Inject 注解则会根据属性的变量名和类型进行 bean 实例的匹配。

/**

* Obtain a resource object for the given name and type through autowiring

* based on the given factory.

* @param factory the factory to autowire against

* @param element the descriptor for the annotated field/method

* @param requestingBeanName the name of the requesting bean

* @return the resource object (never {@code null})

* @throws NoSuchBeanDefinitionException if no corresponding target resource found

*/

protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)

throws NoSuchBeanDefinitionException {

Object resource;

Set autowiredBeanNames;

String name = element.name;

if (factory instanceof AutowireCapableBeanFactory) {

AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;

DependencyDescriptor descriptor = element.getDependencyDescriptor();

if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {

autowiredBeanNames = new LinkedHashSet<>();

resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);

if (resource == null) {

throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");

}

}

else {

resource = beanFactory.resolveBeanByName(name, descriptor);

autowiredBeanNames = Collections.singleton(name);

}

}

else {

resource = factory.getBean(name, element.lookupType);

autowiredBeanNames = Collections.singleton(name);

}

if (factory instanceof ConfigurableBeanFactory) {

ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory;

for (String autowiredBeanName : autowiredBeanNames) {

if (requestingBeanName != null && beanFactory.containsBean(autowiredBeanName)) {

beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);

}

}

}

return resource;

}

@Resource 、@Inject 注解实现依赖注入的原理和过程基本介绍完毕,更多的技术原理会在系列文章的后续内容中提供。该文章会同步在微信公众号 【DevXJava】, 方便在微信客户端阅读。

DevXJava 不止于技术

相关文章

妯娌关系是什么意思
365bet注册送35元

妯娌关系是什么意思

📅 07-08 👀 2248