spring 自定义注解实现静态注入(@StaticAutowired,@StaticValue)

简述

我们都知道在使用 spring 框架时,我们无法通过 注解(@Autowired) 的方式自动注入静态属性,如果想注入静态属性只能通过其他方式实现,这里不讨论其他原生曲线实现方式,我就要用自动注入的注解,这样用起来方便,由此诞生了这篇文章

不过首先可以先说下,本篇文章的静态注入功能核心并非我从零开始实现,而是主要spring由实现的,没错,spring 的 @Autowired 和 @Value 其实是实现了静态注入的功能的,只是,spring 将他 "封印" 了,我这里只是将其"解封",并没有去编写一套复杂的机制,还有明确另一个问题,即 spring 不提供静态注入说明其本身是并不希望开发者使用spring管理静态对象的,所以希望使用注解静态注入的自己把握是否需要

使用方式及效果

为了简单性,易用性,最好和 @Autowired 和 @Value 使用方式一致,我自定义的注解是 @StaticAutowired,@StaticValue 分别对标上面两个注解,实现了 @Autowired 和 @Value 的所有功能,外加支持自动注入静态属性

使用上这里给个小示例吧,和使用 @Autowired 和 @Value没有任何区别

在 UserService 中静态注入 UserStatic

@Service
public class UserService {
    @StaticAutowired
    public static User user;

    public static void test(){
        System.out.println("UserService 里面的静态方法 name:"+user.getName()+"      age:"+user.getAge());
    }
}

当然使用前提和 @Autowired 是一样的 ,即被注入的类必须在容器中存在,下面是被注入的 User 类

@Data
public class User {
    @StaticValue("${user.name}")
    private  static String name;
    @Value("${user.age}")
    private Integer age;

    public User() {}
    public String getName() {return name;}

  

在 User 类中,使用了 @StaticValue 注入了 静态属性 name,使用 @Value 注入了 普通属性 age ps:@StaticValue 也是可以注入普通属性的,即这里两个属性都可以用 @StaticValue

配置类

这里用 @Bean 的方式将 User 加入到容器中,在 User 类上 加类似的 @Component 注解也是可以的,我这里主要还需要引入 user.properties ,就写了一个 @Configuration 配置类,放一起了

@Configuration
@PropertySource(value = {"classpath:user.properties"})
public class SelfConfig {
    @Bean
    public User user(){
        return new User();
    }
}

user.properties 文件内容

user.name="properties config"
user.age=10

@Autowired 实现原理与被封印的静态注入

我既然想通过注解实现静态注入,肯定会参考 @Autowired 的实现,不参考不知道,一参考发现 其实

@Autowired 和 @Value 其实是实现了静态注入的功能,下面大略细说

spring 实现 @Autowired 是基于大名鼎鼎的 Bean 后置处理器实现的 ,该Bean 后置处理器是 AutowiredAnnotationBeanPostProcessor ,他的继承结构如下:

image-20200605160305333

spring 中 Bean 的后置处理器的调用时机有很多, @Autowired 的Bean 后置处理器自动注入Bean的处理在这里

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean

image-20200605161339546

点进去看具体方法

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues

image-20200608101301898

通过 debug 调试会发现,InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);是获取不到静态属性的,接着跟进:

image-20200608101555094

会先从缓存中获取注解的解析结果,这里会直接从缓存获取,因为解析在之前进行过了,不过从代码中,我们也能看出来,实际的解析就是红线标出部分 metadata = buildAutowiringMetadata(clazz); 实际解析的时机是

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors

AutowiredAnnotationBeanPostProcessor 类实现了 MergedBeanDefinitionPostProcessor 接口,会在上面的时机调用 postProcessMergedBeanDefinition 方法缓存要解析的注解结果,这里贴下调用链

image-20200608102524534

因此我们重点关注 metadata = buildAutowiringMetadata(clazz); 方法

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata

image-20200608102829238

通过 debug 调试,会发现静态属性上的注解 spring 也做了解析,但是上面框住部分做了拦截,静态属性不会加入到解析缓存中,通过debug 动态调整数据,跳过静态拦截检查,会发现 @Autowired 正常注入了静态属性,因此我上面会说 spring 其实已经做了静态注入的所有准备,但是将该功能 "封印" 了,原因估计是 spring 一些设计理念的问题,因此实现 @StaticAutowired 无需我们做什么,只需 "解封" 即可

解封 @StaticValue 问题

跳过“封印”后,@StaticAutowired 没什么问题,但是自定义注解 @StaticValue 静态注入仍然有问题,这里就不赘述详细分析过程了,原因是在获取注解的spel表达式的值时,spring 框架用的是 ContextAnnotationAutowireCandidateResolver ,该类的继承结构如下图:

image-20200608105351458

获取 注解值的方法是其父类 QualifierAnnotationAutowireCandidateResolver 的 findValue 方法

org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#findValue

/**
 * Determine a suggested value from any of the given candidate annotations.
 */
@Nullable
protected Object findValue(Annotation[] annotationsToSearch) {
// qualifier annotations have to be local
if (annotationsToSearch.length > 0) {
    AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes(
	// this.valueAnnotationType 是写死在类上的成员变量 Value.class
	// 即 private Class<? extends Annotation> valueAnnotationType = Value.class;
	AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType);
		if (attr != null) {
			return extractValue(attr);
		}
	}
	return null;
}

经过上面的代码,是解析不出来 @StaticValue 的注解值的,只能解析出来 @Value 的

@StaticAutowired,@StaticValue 实现

上面分析的差不多了,下面开始代码修改,首先是两个自定义注解的定义

/**
 * @Description: 注入静态属性
 * @Author: ysx
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
@Documented
public @interface StaticAutowired {
    boolean required() default true;
}
/**
 * @Description: 注入静态属性值
 * @Author: ysx
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
@Documented
public @interface StaticValue {
    String value();
}

通过上面分析,为了能取到 @StaticValue 注解值,我们需要自己定义一个类来代替 ContextAnnotationAutowireCandidateResolver ,我们继承该类,重写方法即可,这样没有风险

/**
 * @Description: spring 容器中默认的 ContextAnnotationAutowireCandidateResolver 写死只解析 @Value 注解 ,想解析自定义的 @StaticValue 注解 只能出此继承下策
 * @Author: ysx
 */
public class ExContextAnnotationAutowireCandidateResolver extends ContextAnnotationAutowireCandidateResolver {
    private Class<? extends Annotation> staticValueAnnotationType = StaticValue.class;
    
    @Override
    protected Object findValue(Annotation[] annotationsToSearch) {
        // 父类 对 @Value 的 value 值解析出来
        Object value = super.findValue(annotationsToSearch);
        if(value!=null){
            return value;
        }
        // 如果 无值 解析 @staticValue
        if (annotationsToSearch.length > 0) {
            AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes(
                    AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.staticValueAnnotationType);
            if (attr != null) {
                return extractValue(attr);
            }
        }
        return null;
    }
}

下面就是 解析 StaticAutowired,StaticValue 的 bean 后置处理器了,该处理器直接参考 AutowiredAnnotationBeanPostProcessor 做一些修改即可,代码如下:

/**
 * @Description: 解析 StaticAutowired,StaticValue 的 bean 后置处理器
 * @Author: ysx
 */
@Component
public class StaticAutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
        implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {
    private final Log logger = LogFactory.getLog(getClass());

    private DefaultListableBeanFactory beanFactory;

    private final Map<String, InjectionMetadata> injectionMetadataCache = new ConcurrentHashMap<>(256);


    /**
     *  支持的注解
     */
    private final Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<>(4);

    private String requiredParameterName = "required";

    private boolean requiredParameterValue = true;

    private final ExContextAnnotationAutowireCandidateResolver exContextAnnotationAutowireCandidateResolver =  new ExContextAnnotationAutowireCandidateResolver();

    @SuppressWarnings("unchecked")
    public StaticAutowiredAnnotationBeanPostProcessor() {
        this.autowiredAnnotationTypes.add(StaticAutowired.class);
        this.autowiredAnnotationTypes.add(StaticValue.class);
    }


    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
        AutowireCandidateResolver autowireCandidateResolver = beanFactory.getAutowireCandidateResolver();
        // 为了 解析 @StaticValue 必须使用 自定义的 ExContextAnnotationAutowireCandidateResolver
        boolean isExContextAnnotationAutowireCandidateResolver = autowireCandidateResolver instanceof ExContextAnnotationAutowireCandidateResolver;
        try {
            if (!isExContextAnnotationAutowireCandidateResolver) {
                beanFactory.setAutowireCandidateResolver(exContextAnnotationAutowireCandidateResolver);
            }
            metadata.inject(bean, beanName, pvs);
        }
        catch (BeanCreationException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanCreationException(beanName, "Injection of static autowired dependencies failed", ex);
        }finally {
            // 设置回原来的
            if (!isExContextAnnotationAutowireCandidateResolver) {
                beanFactory.setAutowireCandidateResolver(autowireCandidateResolver);
            }
        }
        return pvs;
    }



    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        if (!(beanFactory instanceof DefaultListableBeanFactory)) {
            throw new IllegalArgumentException(
                    "StaticAutowiredAnnotationBeanPostProcessor requires a DefaultListableBeanFactory: " + beanFactory);
        }
        this.beanFactory = (DefaultListableBeanFactory) beanFactory;
    }


    @Override
    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
        InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
        metadata.checkConfigMembers(beanDefinition);
    }


    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE - 2;
    }



    private InjectionMetadata findAutowiringMetadata(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 = buildAutowiringMetadata(clazz);
                    this.injectionMetadataCache.put(cacheKey, metadata);
                }
            }
        }
        return metadata;
    }


    private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
        List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
        Class<?> targetClass = clazz;

        do {
            final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
            ReflectionUtils.doWithLocalFields(targetClass, field -> {
                AnnotationAttributes ann = findAutowiredAnnotation(field);
                if (ann != null) {
                    //  这里把不做静态检查 -- @Autowired 在这里做了静态校验,因此无法自动注入静态属性
                    boolean required = determineRequiredStatus(ann);
                    currElements.add(new StaticAutowiredAnnotationBeanPostProcessor.StaticAutowiredFieldElement(field, required));
                }
            });

            elements.addAll(0, currElements);
            targetClass = targetClass.getSuperclass();
        }
        while (targetClass != null && targetClass != Object.class);
        return new InjectionMetadata(clazz, elements);
    }


    /**
     * Determine if the annotated field or method requires its dependency.
     * <p>A 'required' dependency means that autowiring should fail when no beans
     * are found. Otherwise, the autowiring process will simply bypass the field
     * or method when no beans are found.
     * @param ann the Autowired annotation
     * @return whether the annotation indicates that a dependency is required
     */
    protected boolean determineRequiredStatus(AnnotationAttributes ann) {
        return (!ann.containsKey(this.requiredParameterName) ||
                this.requiredParameterValue == ann.getBoolean(this.requiredParameterName));
    }


    @Nullable
    private AnnotationAttributes findAutowiredAnnotation(AccessibleObject ao) {
        // autowiring annotations have to be local
        if (ao.getAnnotations().length > 0) {
            for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
                AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ao, type);
                if (attributes != null) {
                    return attributes;
                }
            }
        }
        return null;
    }



    /**
     * Class representing injection information about an annotated field.
     */
    private class StaticAutowiredFieldElement extends InjectionMetadata.InjectedElement {

        private final boolean required;

        private volatile boolean cached = false;

        @Nullable
        private volatile Object cachedFieldValue;

        public StaticAutowiredFieldElement(Field field, boolean required) {
            super(field, null);
            this.required = required;
        }

        @Override
        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<String> autowiredBeanNames = new LinkedHashSet<>(1);
                Assert.state(beanFactory != null, "No BeanFactory available");
                TypeConverter typeConverter = beanFactory.getTypeConverter();
                try {
                    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;
                            registerDependentBeans(beanName, autowiredBeanNames);
                            if (autowiredBeanNames.size() == 1) {
                                String autowiredBeanName = autowiredBeanNames.iterator().next();
                                if (beanFactory.containsBean(autowiredBeanName) &&
                                        beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
                                    this.cachedFieldValue = new StaticAutowiredAnnotationBeanPostProcessor.ShortcutDependencyDescriptor(
                                            desc, autowiredBeanName, field.getType());
                                }
                            }
                        }
                        else {
                            this.cachedFieldValue = null;
                        }
                        this.cached = true;
                    }
                }
            }
            if (value != null) {
                ReflectionUtils.makeAccessible(field);
                field.set(bean, value);
            }
        }
    }



    /**
     * Register the specified bean as dependent on the staticAutowired beans.
     */
    private void registerDependentBeans(@Nullable String beanName, Set<String> autowiredBeanNames) {
        if (beanName != null) {
            for (String autowiredBeanName : autowiredBeanNames) {
                if (this.beanFactory != null && this.beanFactory.containsBean(autowiredBeanName)) {
                    this.beanFactory.registerDependentBean(autowiredBeanName, beanName);
                }
                if (logger.isTraceEnabled()) {
                    logger.trace("StaticAutowiring by type from bean name '" + beanName +
                            "' to bean named '" + autowiredBeanName + "'");
                }
            }
        }
    }



    /**
     * Resolve the specified cached method argument or field value.
     */
    @Nullable
    private Object resolvedCachedArgument(@Nullable String beanName, @Nullable Object cachedArgument) {
        if (cachedArgument instanceof DependencyDescriptor) {
            DependencyDescriptor descriptor = (DependencyDescriptor) cachedArgument;
            Assert.state(this.beanFactory != null, "No BeanFactory available");
            return this.beanFactory.resolveDependency(descriptor, beanName, null, null);
        }
        else {
            return cachedArgument;
        }
    }


    /**
     * DependencyDescriptor variant with a pre-resolved target bean name.
     */
    @SuppressWarnings("serial")
    private static class ShortcutDependencyDescriptor extends DependencyDescriptor {

        private final String shortcut;

        private final Class<?> requiredType;

        public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcut, Class<?> requiredType) {
            super(original);
            this.shortcut = shortcut;
            this.requiredType = requiredType;
        }

        @Override
        public Object resolveShortcut(BeanFactory beanFactory) {
            return beanFactory.getBean(this.shortcut, this.requiredType);
        }
    }

}

修改点 主要是以下这些,这里也提下吧

  1. buildAutowiringMetadata() 方法去除静态校验,删除方法解析相关代码
  2. BeanFactoryAware 接口注入的容器声明为 DefaultListableBeanFactory
  3. 在 postProcessProperties() 方法中将我们自定义的的 ExContextAnnotationAutowireCandidateResolver 设置到 DefaultListableBeanFactory 中

最后点击访问 github地址

posted @ 2020-08-17 15:30  柠檬请问  阅读(2569)  评论(0编辑  收藏  举报