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地址


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM