本文来自网易云社区
作者:舒凯凯
背景:
将项目中发送http请求的方法改为走代理的方式,项目中发送http请求一般都是工具类,方法为static方法,改为走代理时,需要在该工具类中注入service,static方法中使用该注入的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成员变量失败,httpProxy为null,Spring报错信息:
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()
方法二:
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+款云产品!
更多网易研发、产品、运营经验分享请访问网易云社区。