在springboot中可以注册自己的servlet,通过ServletRegistrationBean这个类即可,也就是我们创建一个ServletRegistrationBean对象并加入spring的上下文即可注册一个servlet了。
所以有必要分析下ServletRegistrationBean
有上图可见,它实现了ServletContextInitializer,所以在tomcat的StandardContext的start方法被调用时,他的onStartup方法会被调用
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
Assert.notNull(this.servlet, "Servlet must not be null");
String name = getServletName();
if (!isEnabled()) {
logger.info("Servlet " + name + " was not registered (disabled)");
return;
}
logger.info("Mapping servlet: '" + name + "' to " + this.urlMappings);
Dynamic added = servletContext.addServlet(name, this.servlet);
if (added == null) {
logger.info("Servlet " + name + " was not registered "
+ "(possibly already registered?)");
return;
}
configure(added);
}
getServletName获取通过ServletRegistrationBean的setName方法设置的name,在addServlet中会设置为一个servlet的name,如果不配就是bean name
ServletRegistration.Dynamic addServlet(String servletName, String servletClass,
Servlet servlet, Map<String,String> initParams) throws IllegalStateException {
......
Wrapper wrapper = (Wrapper) context.findChild(servletName);
// Assume a 'complete' ServletRegistration is one that has a class and
// a name
if (wrapper == null) {
wrapper = context.createWrapper();
wrapper.setName(servletName);
context.addChild(wrapper);
.....
} else {
wrapper.setServletClass(servlet.getClass().getName());
wrapper.setServlet(servlet);
}
可以看到,这里把名为servletName的servlet包装成了tomcat中的wrapper container,然后放到了standardcontext中 ,之所以要这个name就是为了区分不同的servlet,因为有可能用户使用同一个servlet类,用不同的名字来区分对应到相应的urlmapping。
protected void configure(ServletRegistration.Dynamic registration) {
super.configure(registration);
......
if (!ObjectUtils.isEmpty(urlMapping)) {
registration.addMapping(urlMapping);
}
....
}
然后在configure方法中把urlMapping也放到了wrapper中。
再来说下springboot对应springmvc的dispatcherservlet的自动配置DispatcherServletAutoConfiguration类中的
@Configuration
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
protected static class DispatcherServletConfiguration
DefaultDispatcherServletCondition这个类是判断spring上下文中是否有名叫dispatcherServlet 类型为dispatcherServlet.class的bean,如果没有就进行后续的配置。
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
public ServletRegistrationBean dispatcherServletRegistration() {
ServletRegistrationBean registration = new ServletRegistrationBean(
dispatcherServlet(), this.server.getServletMapping());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
if (this.multipartConfig != null) {
registration.setMultipartConfig(this.multipartConfig);
}
return registration;
}
这里他是创建了一个DispatcherServlet的bean,然后通过ServletRegistrationBean把这个servlet放入了standardcontext中,urlmapping可以通过application.properties中的server.servlet-path设置,默认是/。
也就是说,我们不配置servlet的话,springboot也会为我们注册一个servlet到standardcontext中。
如果我们想加入自己的servlet,可以通过ServletRegistrationBean的setServlet方法或者初始化方法加入,记得同时配置urlmapping。至于名字随便起或者不起。
但有一种情况要注意,如果你想再加一个DispatcherServlet的bean,这是要设置名字,如果不设置或者设置为dispatcherServlet,那springboot自动配置里那个dispatcherServlet就不会被注册到到standardcontext中,因为addServlet方法中会判断同名的不会再注册了。
例子:
@Configuration
public class Selfconfigure {
@Bean
public ServletRegistrationBean dispatcherRegistration(DispatcherServlet dispatcherServlet) {
ServletRegistrationBean registrationBean
= new ServletRegistrationBean(dispatcherServlet);
registrationBean.addUrlMappings("/api/*");
registrationBean.addUrlMappings("/demo1/*");
registrationBean.setName("haha");
return registrationBean;
}
}
注意dispatcherServlet这里不能自己new 一个DispatcherServlet对象,因为DispatcherServlet必须作为bean被spring来创建,里面涉及spring context的设置,也就是说DispatcherServlet需要spring context,当然如果非要自己new,那也得new个context赋值给DispatcherServlet对象,我们这里直接从方法参数获取是因为springboot为我们创建了一个DispatcherServlet的bean。