问题场景
Spring boot 2.7.18
使用Hutool文档中使用方法
@ComponentScan(basePackages={"cn.hutool.extra.spring"})
@Import(cn.hutool.extra.spring.SpringUtil.class)
在主入口定义,会导致路由全部消失,接口访问404。
解决方案
使用提供的@EnableSpringUtil
注解或者新建个配置类,将两个注解放到上面。
原因分析
ComponentScan
注解 与 @Import
注解
@ComponentScan
用于自动检测和注册带有特定注解的类为bean。这些注解通常包括但不限于 @Component
, @Service
, @Repository
, 和 @Controller
。当一个配置类上使用了 @ComponentScan
,Spring会扫描指定的包及其子包,并将所有带有上述注解的类作为bean注册到IoC容器中。
@ComponentScan
主要用于自动发现和注册组件@Impoert
适用于导入特定的配置类和静态bean定义,便于模块化配置
怀疑是@SpringApplication
中的@ComponentScan
同主入口的@ComponetScan
相冲突
BeanFactoryPostProcessor
允许开发者在Spring IoC容器初始化之前对BeanFactory进行修改,是最早要执行的方法。该接口只有一个方法需要实现
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
这个函数允许访问并修改BeanFactory的内容,这个方法调用发生在所有bean实例化之前,可以进行配置。
ApplicationContextAware
使一个类能够获得整个Spring应用上下文(ApplicationContext
)的引用。通常,当一个类实现了ApplicationContextAware
接口后,Spring容器会在初始化该类的实例时自动调用一个回调方法,将当前的ApplicationContext
实例注入给该类。
SpringUtil的代码
通过实现上面两个BeanFactoryPostProcessor
和ApplicationContextAware
的接口,可以实现Util方法,不用实例化,直接调用静态方法
@Component
public class SpringUtil implements BeanFactoryPostProcessor, ApplicationContextAware {
/**
* "@PostConstruct"注解标记的类中,由于ApplicationContext还未加载,导致空指针<br>
* 因此实现BeanFactoryPostProcessor注入ConfigurableListableBeanFactory实现bean的操作
*/
private static ConfigurableListableBeanFactory beanFactory;
/**
* Spring应用上下文环境
*/
private static ApplicationContext applicationContext;
@SuppressWarnings("NullableProblems")
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
SpringUtil.beanFactory = beanFactory;
}
@SuppressWarnings("NullableProblems")
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
SpringUtil.applicationContext = applicationContext;
}
/**
* 获取{@link ApplicationContext}
*
* @return {@link ApplicationContext}
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 获取{@link ListableBeanFactory},可能为{@link ConfigurableListableBeanFactory} 或 {@link ApplicationContextAware}
*
* @return {@link ListableBeanFactory}
* @since 5.7.0
*/
public static ListableBeanFactory getBeanFactory() {
final ListableBeanFactory factory = null == beanFactory ? applicationContext : beanFactory;
if (null == factory) {
throw new UtilException("No ConfigurableListableBeanFactory or ApplicationContext injected, maybe not in the Spring environment?");
}
return factory;
}
/**
* 获取{@link ConfigurableListableBeanFactory}
*
* @return {@link ConfigurableListableBeanFactory}
* @throws UtilException 当上下文非ConfigurableListableBeanFactory抛出异常
* @since 5.7.7
*/
public static ConfigurableListableBeanFactory getConfigurableBeanFactory() throws UtilException {
final ConfigurableListableBeanFactory factory;
if (null != beanFactory) {
factory = beanFactory;
} else if (applicationContext instanceof ConfigurableApplicationContext) {
factory = ((ConfigurableApplicationContext) applicationContext).getBeanFactory();
} else {
throw new UtilException("No ConfigurableListableBeanFactory from context!");
}
return factory;
}
在Bean注册时,已经完成了BeanFactory的实例化,可以直接调用getBean等方法了。