Spring 注入static成员变量方法介

叁叁肆2018-09-19 13:32

本文来自网易云社区

作者:舒凯凯


背景:

将项目中发送http请求的方法改为走代理的方式,项目中发送http请求一般都是工具类,方法为static方法,改为走代理时,需要在该工具类中注入servicestatic方法中使用该注入的service发送http请求。

public class HttpUtil {

public static byte[] sendHttpRequest(){
Connection con = ...
con.sendRequest();
...
return ...;
}
}
修改为使用代理发送http请求
public class HttpUtil {

@Autowired
private static HttpProxy httpProxy;

public static byte[] sendHttpRequest(){
return httpProxy.sendRequest();
}
}

问题:

Spring @Autowired 注入static成员变量失败,httpProxynullSpring报错信息:

Autowired annotation is not supported on static fields: HttpProxy

不支持static成员变量的注入

 

解决方案:

方法一:

使用Spring对外开发的扩展接口,实现static成员变量的注入,在Bean实例化,并且初始化之后,注入static成员变量,实现Spring的接口BeanPostProcessor中的postProcessAfterInitialization方法。

public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

该方法会在bean初始化之后,并且所有的属性都被注入了之后回调用该方法,再方法中注入static的成员变量。

a.定义注解

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface StaticFieldInject {
}

b.实现static成员变量注入的bean

public class StaticFieldInjectPostProcessor implements BeanPostProcessor, ApplicationContextAware {
//实现BeanPostProcessor接口,扩展Spring功能
//实现ApplicationContextAware接口,获取applicationContext,为了获取需要注入的servier bean

private ApplicationContext applicationContext;

//默认实现,不做任何事情
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

return bean;
}

// 在Bean实例化,并且初始化之后,Spring会回调该方法
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// 获取Bean 类中每个成员变量
Field[] fields = bean.getClass().getDeclaredFields();
for (Field field : fields) {
// 类中的每个成员变量,是否含有自定义的注解 @StaticFieldInject 修饰,并且是 static 修饰的
if (hasAnnotation(field.getAnnotations(), StaticFieldInject.class.getName()) && Modifier.isStatic(field.getModifiers())) {
// 注入static成员变量
injectField(bean, field);
}
}
return bean;
}

// 注入bean
private void injectField(Object bean, Field field) {
if (!field.isAccessible()) {
field.setAccessible(true);
}
try {
// 从applicationContext中,通过类型获取需要注入的bean--要求被注入的bean是通过spring管理的,而且是singleton
Object object = applicationContext.getBean(field.getType());

if(object == null){
logger.error("Inject static field error!");
}
// 注入bean
field.set(bean, object);
} catch (Exception e) {
logger.error("Inject static field exception!", e);
}
}

// 判断是否还有特定的注解
private boolean hasAnnotation(Annotation[] annotations, String annotationName) {
if (annotations == null) {
return false;
}
if (StringUtils.isEmpty(annotationName)) {
return false;
}
for (Annotation annotation : annotations) {
if (annotation.annotationType().getName().equals(annotationName)) {
return true;
}
}
return false;
}

// 注入applicationContext,用来获取被注入的bean
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}

c.修改工具类,使用自定义注解修饰static成员变量

public class HttpUtil {

@StaticFieldInject
private static HttpProxy httpProxy;

public static byte[] sendHttpRequest(){
return httpProxy.sendRequest();
}

}

这里列一下Spring对外扩展接口

为什么选择实现BeanPostProcessor接口而不是其他接口,这里又涉及到Spring管理bean的实例化和初始化顺序问题,这里简单介绍几个比较重要的接口中方法执行顺序,因为涉及的地方比较复杂,有可能不会完全按照该顺序执行,因为根据配置的不同,有可能实例化bean会使用工厂方法返回一个实例,那么很多扩展接口就不会执行。

1.         InstantiationAwareBeanPostProcessor.applyBeanPostProcessorsBeforeInstantiation()

2.         SmartInstantiationAwareBeanPostProcessor.determineCandidateConstructors()

3.         MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition()

4.         SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference()

5.         InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()

6.         InstantiationAwareBeanPostProcessor. postProcessPropertyValues ()

7.         BeanPostProcessor.postProcessBeforeInitialization()

8.         初始化方法

9.         BeanPostProcessor.postProcessAfterInitialization()

10.     DestructionAwareBeanPostProcessor.postProcessBeforeDestruction()

实现@Autowired注入的AutowiredAnnotationBeanPostProcessor是通过InstantiationAwareBeanPostProcessor. postProcessPropertyValues()实现注入的,这里为了不影响其他注解或者注入方法以及自定义Spring的扩展,选在在Bean初始化结束之后的BeanPostProcessor.postProcessAfterInitialization()中执行static成员变量的注入,也可以选择其他时机完成注入,这里就不举例子了,效果一样。


方法二:

setter方法注入

public class HttpUtil {

private static HttpProxy httpProxy;

public static byte[] sendHttpRequest(){
return httpProxy.sendRequest();
}

@Autowired(required = true)
public void set setHttpProxy (HttpProxy httpProxy) {
HttpUtil.httpProxy = httpProxy;
}
}

虽然@Autowired不支持static成员变量,但是支持普通方法的注入

 

方法三:

构造函数注入

public class HttpUtil
{
private static HttpProxy httpProxy;

@Autowired(required = true)
public HttpUtil(@Qualifier(“httpProxy”) HttpProxy httpProxy) {
HttpUtil. httpProxy = httpProxy;
}
}

与方法二类似



网易云免费体验馆0成本体验20+款云产品!

更多网易研发、产品、运营经验分享请访问网易云社区