目录
一、现象
最近需要在一个比较老的系统中添加OSS进行文件上传下载处理,而原有系统中已有处理文件上传和下载处理的相关抽象类和接口,为了不影响原有代码逻辑,只能通过新增实现类的模式进行处理。原有处理逻辑中具体处理实现是通过Spring的Configuration中初始化Bean的方式进行处理(通过application的配置来决定初始化Bean),原有的处理为共享磁盘和SFTP两种方式,两种方式都没有实现的Bean里面调用Spring管理的其他Bean模式进行处理。但是OSS的处理逻辑为了兼容项目中多种模式,通过OssTemplate进行处理,并且在外部封装了对外的API,用以实现系统原有逻辑不变更和简化处理逻辑,封装的服务里面很多Bean都是Spring托管的,为了能够使用这个封装的服务,需要基于原有接口的实现类也用spring托管的Bean,故在初始化中使用了SpringContextUtils类的getBean(Class)方法获取,在WEB应用中正常启动,但是在批处理的应用中报空指针异常,沙雕了。
二、解决
此处由于需要用Spring管理的bean,故通过查看Spring的Bean加载顺序,发现通过包的远近来进行加载,针对提供的基础服务,SpringContextUtils是否在Configuration的Bean初始化之前初始化是未知的。此时想到了很久之前有个大神处理生产问题时用到的@DependsOn注解(注解含义就是组件A要依赖于另一个组件B,也就是说被依赖的组件B会比组件A先注册到Spring容器中),如果Configuration的@Bean上面增加这个@DependsOn("springContextUtils")是否也跟正常@Component上增加注解一样,先把SpringContextUtils注册到Spring容器里面?
@Bean
@DependsOn({"springContextUtils"})
public FileUploadTemplate getFileSystemMethod() {
再次启动批处理后,一切OK,不会再有空指针异常发生,并且能够正常使用了。
PS:SpringContextUtils源码如下:
@Component
public final class SpringContextUtils implements ApplicationContextAware {
/**
* Spring应用上下文环境
*/
private static ApplicationContext applicationContext;
/**
* 获取对象
*
* @return Object 一个以所给名字注册的bean的实例
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
return (T) applicationContext.getBean(name);
}
/**
* 获取类型为requiredType的对象
*/
public static <T> T getBean(Class<T> clz) {
return applicationContext.getBean(clz);
}
/**
* 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
*
* @return boolean
*/
public static boolean containsBean(String name) {
return applicationContext.containsBean(name);
}
/**
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
*
* @return boolean
*/
public static boolean isSingleton(String name) {
return applicationContext.isSingleton(name);
}
/**
* @return Class 注册对象的类型
*/
public static Class<?> getType(String name) {
return applicationContext.getType(name);
}
/**
* 如果给定的bean名字在bean定义中有别名,则返回这些别名
*/
public static String[] getAliases(String name) {
return applicationContext.getAliases(name);
}
@Override
public synchronized void setApplicationContext(ApplicationContext applicationContext) {
if (SpringContextUtils.applicationContext == null) {
SpringContextUtils.applicationContext = applicationContext;
}
}
}
三、@DependsOn详解
@DependsOn注解是Spring框架中的一个功能,用于定义bean之间的依赖关系。它主要用于控制bean的初始化顺序,确保某些bean在其他bean之前被创建和初始化。@DependsOn注解常用于解决循环依赖、确保某些bean在其他bean之前被创建等场景。
具体作用:
- 控制bean的初始化顺序:通过@DependsOn可以确保某个bean在另一个bean之前被初始化,从而避免因初始化顺序不当而导致的问题。
- 解决循环依赖:当两个或多个bean之间存在循环依赖时,可以使用@DependsOn注解来指定bean的初始化顺序,从而解决循环依赖问题。
使用方法: @DependsOn注解可以与@Bean、@Component、@Service等注解一起使用。以下是@DependsOn的一些使用示例:
在Java配置类中,结合@Bean注解使用:
@Configuration
public class AppConfig {
@Bean
@DependsOn("beanB")
public BeanA beanA() {
return new BeanA();
}
@Bean
public BeanB beanB() {
return new BeanB();
}
}
在上面的示例中,beanA依赖于beanB,所以我们使用@DependsOn("beanB")注解来确保beanB在beanA之前被创建和初始化。
在组件类中,结合@Component、@Service等注解使用:
@Service
@DependsOn("beanB")
public class BeanA {
// ...
}
@Service
public class BeanB {
// ...
}
在上面的示例中,我们将@DependsOn注解应用于BeanA类上,以确保BeanB在BeanA之前被创建和初始化。