今天在做一个需求的时候,需要在Servlet接口中调用一个Spring的Bean对象,因为Servlet和Spring生命周期不同,所被管理的容器不同,因此不能直接通过@Autowire注入
以下三个方案,思路分别是:
这里做演示用的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 {
.....
}
@Override
public void init(ServletConfig config) throws ServletException {
// 父类不初始化会导致getServletContext()空指针异常
super.init(config);
// 将当前this 的 Servlet交给Spring托管,因此该Servlet可以用@Autowired
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, config.getServletContext());
}
通过这个类的名字,结合注释可以知道,这个类主要的作用是对@Autowired进行增强
看注释中的如下部分,通过@Autowired将Spring的Bean对象解析到当前的Web上下文(Servlet上下文)
Servlet注入Spring的Bean对象,最终的执行方法是processInjection()
@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
在SpringBeanAutowiringSupport类的注释中有提到
WebApplicationContextUtils类允许基于ServletContext轻松访问Spring根web应用程序上下文。
bean对象本质上也是一个java的对象,只要能获取其地址值,就可以正常使用
这样配置就不需要写inti()了
Filter也是Servlet的产物,他的上下文环境也不同于Spring上下文,同样还是这个需求里面用的是Filter对其进行校验,也需要注入Srping的Bean对象,当然不能直接@Autowired
@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 getBean(Class requiredType){
return getApplicationContext().getBean(requiredType);
}
//通过id名获取bean
public static T getBean(String name){
return (T) getApplicationContext().getBean(name);
}
}
把Servlet或者Filter当普通类,直接用工具类调用即可
因为项目启动时装载在前,很多修改“装载过程”的地方不能通过热部署来reload,需要重启项目
在使用Servlet对象之前,将其需要注入的SpringBean作为形参,在配置类中(即启动时装载)指定SpringBean对象的地址值给Servlet或Filter的形参即可