今天在做一个需求的时候,需要在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上下文)

JavaBean怎样在servlet中使用_初始化

1.2processInjectionBasedOnServletContext

Servlet注入Spring的Bean对象,最终的执行方法是processInjection()

JavaBean怎样在servlet中使用_ide_02

1.2.1processInjection()

JavaBean怎样在servlet中使用_初始化_03

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应用程序上下文。

JavaBean怎样在servlet中使用_初始化_04

3.配置类传递bean地址

bean对象本质上也是一个java的对象,只要能获取其地址值,就可以正常使用

JavaBean怎样在servlet中使用_servlet_05

JavaBean怎样在servlet中使用_java_06


这样配置就不需要写inti()了

4.同样的,对于Filter

Filter也是Servlet的产物,他的上下文环境也不同于Spring上下文,同样还是这个需求里面用的是Filter对其进行校验,也需要注入Srping的Bean对象,当然不能直接@Autowired

JavaBean怎样在servlet中使用_初始化_07


JavaBean怎样在servlet中使用_java_08

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的形参即可