环境:springboot2.3.9.RELEASE
1 Servlet注册
方式1:
在配置类(启动类)上添加@ServletComponentScan注解
@SpringBootApplication
@ServletComponentScan
public class SpringBootComprehensiveApplication
}
Servlet类上添加@WebServlet注解接口
@WebServlet("/hello")
public class MyServlet extends HttpServlet {
}
对应的Filter, Linstener有:@WebFilter, @WebListener
方式2:
通过向IOC容器添加ServletRegistrationBean方式;该种方式可以在Servlet中注入其它Bean或者读取application.properties配置信息等。对应的filter, Listener有对应的bean;FilterRegistrationBean,
ServletListenerRegistrationBean
@Bean
public ServletRegistrationBean<MyServlet> servlet() {
ServletRegistrationBean<MyServlet> servlet = new ServletRegistrationBean<>(new MyServlet()) ;
servlet.addUrlMappings("/hello") ;
return servlet ;
}
方式3:
动态注册Servlet
@Component
public class DynamicRegServlet implements ServletContextInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
ServletRegistration initServlet = servletContext.addServlet("myServlet", MyServlet.class) ;
initServlet.addMapping("/hello") ;
}
}
该种方式是利用的Servlet3.0开始才有的功能,通过SPI技术在容器启动的时候做一些初始化工作,比如注册Servlet等。在Servle规范中通过
ServletContainerInitializer实现该功能。
该Servlet规范的开发流程如下;
1、配置
ServletContainerInitializer
在src/META-INF/services下新建
javax.servlet.ServletContainerInitializer文件,文件内容为ServletContainerInitializer接口的实现类(完整的包名+类名)如下:
com.pack.container.config.CustomServletInitializer
CustomServletInitializer类
@HandlesTypes({ScoreWebInit.class})
public class CustomServletInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
c.forEach(web -> {
try {
((ScoreWebInit)web.newInstance()).pay() ;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
});
ServletRegistration.Dynamic reg = ctx.addServlet("MyServlet", com.pack.servlet.MyServlet.class) ;
reg.setLoadOnStartup(1) ;
reg.addMapping("/hello") ;
}
}
注意:@HandlesTypes该注解会把属性value配置的值(ScoreWebInt.class)对应的所有类都收集上然后在onStartup方法中的Set<Class> 集合中应用。
在spring-web-xxxx.jar下就有通过该技术实现的相应功能。
2 扫描Servlet实现原理
在方式1中的实现原理就是扫描类路径下所有@WebServlet,@WebFilter,@WebListener。找到所有的类后再通过方式2的方式进行注册。下面将核心源码贴出
2.1 导入核心类
// 该注解如果没有配置basePackages或者basePackageClasses属性,那么会读取当前添加@ServletComponentScan注解的类的包路径进行扫描。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ServletComponentScanRegistrar.class)
public @interface ServletComponentScan {
}
// 注册BeanDefinition
class ServletComponentScanRegistrar implements ImportBeanDefinitionRegistrar{
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
if (registry.containsBeanDefinition(BEAN_NAME)) {
updatePostProcessor(registry, packagesToScan);
} else {
// 当前容器中没有对应的Bean时执行该方法
addPostProcessor(registry, packagesToScan);
}
}
}
2.2 注册BeanFactory 处理器
ServletComponentScanRegistrar最核心的功能就是注册BeanFactoryPostProcessor
class ServletComponentScanRegistrar implements ImportBeanDefinitionRegistrar{
private void addPostProcessor(BeanDefinitionRegistry registry, Set<String> packagesToScan) {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(ServletComponentRegisteringPostProcessor.class);
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(packagesToScan);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BEAN_NAME, beanDefinition);
}
}
2.3 实例化扫描组件
进入
ServletComponentRegisteringPostProcessor类中首先这个类有个static代码块
class ServletComponentRegisteringPostProcessor implements BeanFactoryPostProcessor, ApplicationContextAware {
private static final List<ServletComponentHandler> HANDLERS;
static {
List<ServletComponentHandler> servletComponentHandlers = new ArrayList<>();
servletComponentHandlers.add(new WebServletHandler());
servletComponentHandlers.add(new WebFilterHandler());
servletComponentHandlers.add(new WebListenerHandler());
HANDLERS = Collections.unmodifiableList(servletComponentHandlers);
}
}
这段代码分别是添加相应Servlet, Filter, Listener的处理句柄,分别处理@WebServlet,@WebFilter,@WebListener 注解。
查看WebServletHandler
class WebServletHandler extends ServletComponentHandler {
WebServletHandler() {
super(WebServlet.class);
}
// 看到该方法也就十分清楚了最终找到所有的Class以后,通过ServletRegistrationBean进行注册为Bean
@Override
public void doHandle(Map<String, Object> attributes, AnnotatedBeanDefinition beanDefinition,BeanDefinitionRegistry registry) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ServletRegistrationBean.class);
builder.addPropertyValue("asyncSupported", attributes.get("asyncSupported"));
builder.addPropertyValue("initParameters", extractInitParameters(attributes));
builder.addPropertyValue("loadOnStartup", attributes.get("loadOnStartup"));
String name = determineName(attributes, beanDefinition);
builder.addPropertyValue("name", name);
builder.addPropertyValue("servlet", beanDefinition);
builder.addPropertyValue("urlMappings", extractUrlPatterns(attributes));
builder.addPropertyValue("multipartConfig", determineMultipartConfig(beanDefinition));
registry.registerBeanDefinition(name, builder.getBeanDefinition());
}
// other code
}
// 父类ServletComponentHandler;在父类总添加相应的过滤器(分别查找相应注解的类,@WebServlet等。)
abstract class ServletComponentHandler {
private final Class<? extends Annotation> annotationType;
private final TypeFilter typeFilter;
protected ServletComponentHandler(Class<? extends Annotation> annotationType) {
this.typeFilter = new AnnotationTypeFilter(annotationType);
this.annotationType = annotationType;
}
}
接下来执行BeanFactoryPostProcessor对应的回调方法了
class ServletComponentRegisteringPostProcessor implements BeanFactoryPostProcessor, ApplicationContextAware {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (isRunningInEmbeddedWebServer()) {
ClassPathScanningCandidateComponentProvider componentProvider = createComponentProvider();
for (String packageToScan : this.packagesToScan) {
scanPackage(componentProvider, packageToScan);
}
}
}
}
createComponentProvider方法进行创建扫描相应符合条件的Bean扫描类
private ClassPathScanningCandidateComponentProvider createComponentProvider() {
ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(false);
componentProvider.setEnvironment(this.applicationContext.getEnvironment());
componentProvider.setResourceLoader(this.applicationContext);
for (ServletComponentHandler handler : HANDLERS) {
componentProvider.addIncludeFilter(handler.getTypeFilter());
}
return componentProvider;
}
在该方法中为当前的
ClassPathScanningCandidateComponentProvider类扫描设置过滤器;过滤器在上面的static静态代码块中已经设置了WebServletHandler,WebFilterHandler,WebListenerHandler在父类中分别创建不同注解的new AnnotationTypeFilter(annotationType)过滤类。
创建完类扫描类以后开始扫描通过该类扫描相应包(路径)下的所有类文件 检查是否有对应的注解。
2.4 查找及注册Bean
class ServletComponentRegisteringPostProcessor implements BeanFactoryPostProcessor, ApplicationContextAware {
private void scanPackage(ClassPathScanningCandidateComponentProvider componentProvider, String packageToScan) {
for (BeanDefinition candidate : componentProvider.findCandidateComponents(packageToScan)) {
if (candidate instanceof AnnotatedBeanDefinition) {
for (ServletComponentHandler handler : HANDLERS) {
handler.handle(((AnnotatedBeanDefinition) candidate), (BeanDefinitionRegistry) this.applicationContext);
}
}
}
}
}
findCandidateComponents方法查找候选的(符合条件)的类并实例化为BeanDefinition对象。
方法执行链findCandidateComponents ---》scanCandidateComponents
在scanCandidateComponents方法中查找符合条件的类,然后实例化为BeanDefinition
public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
candidates.add(sbd);
}
}
return candidates;
}
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return false;
}
}
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return isConditionMatch(metadataReader);
}
}
return false;
}
}
在第二个for开始匹配所有的类是否有相关的注解。如果匹配上就相应的创建BeanDefinition对象放入集合Set中。
查找到所有的类以后分别调用相应的Web*Handler(ServletComponentHandler)进行注册Bean。
返回到上面的scanPackage方法中执行handler.handle方法。
abstract class ServletComponentHandler {
void handle(AnnotatedBeanDefinition beanDefinition, BeanDefinitionRegistry registry) {
Map<String, Object> attributes = beanDefinition.getMetadata().getAnnotationAttributes(this.annotationType.getName());
if (attributes != null) {
doHandle(attributes, beanDefinition, registry);
}
}
}
doHandle方法分别在子类(WebServletHandler,WebFilterHandler,WebListenerHandler)中实现,如上面已经提到的WebServletHandler类的doHandler方法。
到这里我们知道了注解的方式最终也是被注册为ServletRegistrationBean 实例。那这个ServletRegistrationBean又是如何被容器(Tomcat)所感知的呢?
2.5 Tomcat注册Servlet
在Web容器下 BeanFactory使用的是
AnnotationConfigServletWebServerApplicationContext
Spring IOC容器核心方法是refresh方法
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
try {
// Initialize other special beans in specific context subclasses.
onRefresh();
} catch (BeansException ex) {
throw ex;
} finally {
resetCommonCaches();
}
}
}
}
这里只留了onRefresh方法,进入该方法:
onRefresh会进入到子类的方法
public class ServletWebServerApplicationContext extends GenericWebApplicationContext implements ConfigurableWebServerApplicationContext {
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
} catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
}
进入createWebServer方法
public class ServletWebServerApplicationContext extends GenericWebApplicationContext implements ConfigurableWebServerApplicationContext {
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = getWebServerFactory();
this.webServer = factory.getWebServer(getSelfInitializer());
getBeanFactory().registerSingleton("webServerGracefulShutdown",new WebServerGracefulShutdownLifecycle(this.webServer));
getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer));
} else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
} catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}
}
这里会进入factory.getWebServer(getSelfInitializer())方法执行
进入getSelfInitializer()方法
public class ServletWebServerApplicationContext extends GenericWebApplicationContext implements ConfigurableWebServerApplicationContext {
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
return this::selfInitialize;
}
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
}
这里的for循环是遍历当前容器中所有ServletContextInitializer类型的Bean。ServletRegistrationBean就是继承ServletContextInitializer
到这里分别调用ServletContextInitializer的onStartup方法,进入onStartup方法:
public abstract class RegistrationBean implements ServletContextInitializer, Ordered {
@Override
public final void onStartup(ServletContext servletContext) throws ServletException {
String description = getDescription();
if (!isEnabled()) {
logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
return;
}
register(description, servletContext);
}
// 在子类中实现
protected abstract void register(String description, ServletContext servletContext);
}
进入子类DynamicRegistrationBean
1 public abstract class DynamicRegistrationBean<D extends Registration.Dynamic> extends RegistrationBean {
2 @Override
3 protected final void register(String description, ServletContext servletContext) {
4 D registration = addRegistration(description, servletContext);
5 if (registration == null) {
6 logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
7 return;
8 }
9 // 配置Servlet Mapping相关的信息(在子类ServletRegistrationBean中实现的)
10 configure(registration);
11 }
12 // 子类中实现
13 protected abstract D addRegistration(String description, ServletContext servletContext);
14 }
进入子类ServletRegistrationBean
public class ServletRegistrationBean<T extends Servlet> extends DynamicRegistrationBean<ServletRegistration.Dynamic> {
@Override
protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
String name = getServletName();
return servletContext.addServlet(name, this.servlet);
}
}
到这里就是通过ServletContext来动态注册Servlet(Servilet3.0)。
这里返回了
ServletRegistration.Dynamic对象会继续执行configure方法配置urlMapping等信息。
public class ServletRegistrationBean<T extends Servlet> extends DynamicRegistrationBean<ServletRegistration.Dynamic> {
@Override
protected void configure(ServletRegistration.Dynamic registration) {
super.configure(registration);
String[] urlMapping = StringUtils.toStringArray(this.urlMappings);
if (urlMapping.length == 0 && this.alwaysMapUrl) {
urlMapping = DEFAULT_MAPPINGS;
}
if (!ObjectUtils.isEmpty(urlMapping)) {
registration.addMapping(urlMapping);
}
registration.setLoadOnStartup(this.loadOnStartup);
if (this.multipartConfig != null) {
registration.setMultipartConfig(this.multipartConfig);
}
}
}
到此在Springboot环境下Servlet如何被注册到Servlet容器中就已经清晰了。这动态注册Servlet的相关API都是在Servlet3.0规范中才有的。