今天在做一个需求的时候,需要在Servlet接口中调用一个Spring的Bean对象,因为Servlet和Spring生命周期不同,所被管理的容器不同,因此不能直接通过@Autowire注入
以下三个方案,思路分别是:
- 在初始化这个Servlet类接口的时候,重写init()方法,允许Servlet扫描Bean对象
- 在初始化这个Servlet类接口的时候,重写init()方法,指定获取bean:getBean()
- 在利用配置类在装载Servlet的时候,将bean对象地址作为形参传给Servlet
0.Servlet结构演示
这里做演示用的StringRedisTemplate 本身不是一个SpringBean对象,但是由于项目中有配置将其扫描为了bean,所以这里直接用StringRedisTemplate 来做演示
public class ValidateCodeServlet extends HttpServlet {
//@Autowired
private StringRedisTemplate stringRedisTemplate;
// public ValidateCodeServlet() {
// }
// public ValidateCodeServlet(StringRedisTemplate stringRedisTemplate) {
// this.stringRedisTemplate = stringRedisTemplate;
// }
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
....}
@Override
public void init(ServletConfig config) throws ServletException {
.....
}
1.允许Servlet扫描Bean对象
@Override
public void init(ServletConfig config) throws ServletException {
// 父类不初始化会导致getServletContext()空指针异常
super.init(config);
// 将当前this 的 Servlet交给Spring托管,因此该Servlet可以用@Autowired
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, config.getServletContext());
}
1.1SpringBeanAutowiringSupport类
通过这个类的名字,结合注释可以知道,这个类主要的作用是对@Autowired进行增强
看注释中的如下部分,通过@Autowired将Spring的Bean对象解析到当前的Web上下文(Servlet上下文)
1.2processInjectionBasedOnServletContext
Servlet注入Spring的Bean对象,最终的执行方法是processInjection()
1.2.1processInjection()
2.get指定的bean
@Override
public void init(ServletConfig config) throws ServletException {
ServletContext servletContext = this.getServletContext();
WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
this.stringRedisTemplate = (StringRedisTemplate) context.getBean("StringRedisTemplate");
}
如果目标类本身没有直接注入Spring容器而是配置扫描,会报错,所以不推荐这种做法,更推荐方法1
2.1WebApplicationContextUtils工具类
在SpringBeanAutowiringSupport类的注释中有提到
WebApplicationContextUtils类允许基于ServletContext轻松访问Spring根web应用程序上下文。
3.配置类传递bean地址
bean对象本质上也是一个java的对象,只要能获取其地址值,就可以正常使用
这样配置就不需要写inti()了
4.同样的,对于Filter
Filter也是Servlet的产物,他的上下文环境也不同于Spring上下文,同样还是这个需求里面用的是Filter对其进行校验,也需要注入Srping的Bean对象,当然不能直接@Autowired
5.普通类调用bean工具类
@Component
public class SpringContexUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
applicationContext = context;
}
//静态加载applicationContext
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
//通过反射获取Bean
public static <T> T getBean(Class<T> requiredType){
return getApplicationContext().getBean(requiredType);
}
//通过id名获取bean
public static <T> T getBean(String name){
return (T) getApplicationContext().getBean(name);
}
}
把Servlet或者Filter当普通类,直接用工具类调用即可
需要注意的点
因为项目启动时装载在前,很多修改“装载过程”的地方不能通过热部署来reload,需要重启项目
6.总结
在使用Servlet对象之前,将其需要注入的SpringBean作为形参,在配置类中(即启动时装载)指定SpringBean对象的地址值给Servlet或Filter的形参即可