servlet3.0解析
- 在Servlet3.0中可以动态注册Servlet,Filter,Listener,在ServletContext对应注册API为:
addServlet(); addFilter();addListener()
2.从servlet3.0开始,web容器启动时为提供给第三方组件机会做一些初始化的工作;
例如注册servlet或者filtes等,servlet规范中通过
ServletContainerInitializer实现此功能。每个框架要使用
ServletContainerInitializer就必须在对应的jar包的META-INF/services 目录创建一个名为
javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的
ServletContainerInitializer实现类,那么,当web容器启动时就会运行这个初始化器做一些组件内的初始化工作。
动态注册Servlet,但不要希望太高,只能在初始化时进行注册。在运行时为了安全原因,无法完成注册。在初始化情况下的注册Servlet组件有两种方法:
1.实现ServletContextListener接口,在contextInitialized方法中完成注册.
2.在jar文件中放入实现
ServletContainerInitializer接口的初始化器
tomact 遵循servlet规范 springweb使用的就是servlet规范
tomcat 7 servlet< 3.0规范
tomcat 8 servlet >3.0规范
META-INF /services/javax.servlet.ServletContainerInitializer
org.springframework.web.SpringServletContainerInitializer
@HandlesTypes的实现原理:
他的作用是将注解指定的Class对象作为参数传递到onStartup(
ServletContainerInitializer)方法中;
其中@HandlesTypes注解表示
SpringServletContainerInitializer 可以处理的类,在onStartup 方法中,可以通过Set<Class>> c 获取得到。
然而这个注解是要留给用户扩展的,他指定的Class对象并没有要继承
ServletContainerInitializer,更没有写入META-INF/services/的文件(也不可能写入)中,那么Tomcat是怎么扫描到指定的类的呢。
答案是Byte Code Engineering Library (BCEL),这是Apache Software Foundation 的Jakarta 项目的一部分,作用同ASM类似,是字节码操纵框架。
SpringServletContainerInitializer
通过源码发现,配合注解@HandlesTypes它可以将其指定的Class对象作为参数传递到onStartup方法中。进而在onStartup方法中获取Class对象的具体实现类,进而调用实现类中的具体方法。
SpringServletContainerInitializer类中@HandlesTypes指定的是Class对象是
WebApplicationInitializer.Class。利用这个机制,若实现WebApplicationInitializer这个接口,我们就可以自定义的注入Servlet,或者Filter,即可以不再依赖web.xml的配置。
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
registerContextLoaderListener(servletContext);
}
/**
* org.springframework.web.context.AbstractContextLoaderInitializer implements WebApplicationInitializer
* registerContextLoaderListener
*/
protected void registerContextLoaderListener(ServletContext servletContext) {
//WebApplicationContext web容器创建
WebApplicationContext rootAppContext = createRootApplicationContext();
if (rootAppContext != null) {
ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
listener.setContextInitializers(getRootApplicationContextInitializers());
servletContext.addListener(listener);
}
else {
logger.debug("No ContextLoaderListener registered, as " +
"createRootApplicationContext() did not return an application context");
}
}
@RequestMapping解析
RequestMappingHandlerMapping实现了InitializingBean接口,因此,在初始化并装配该Bean实例时,执行到上述代码是,便会执行他的afterPropertySet方法。我们接下来看看他的afterPropertySet方法:
/**
* Scan beans in the ApplicationContext, detect and register handler methods.
* @see #isHandler(Class)
* @see #getMappingForMethod(Method, Class)
* @see #handlerMethodsInitialized(Map)
*/
protected void initHandlerMethods() {
String[] beanNames = obtainApplicationContext().getBeanNamesForType(Object.class);
for (String beanName : beanNames) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
Class> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
if (beanType != null && isHandler(beanType)) {
//遍历这些Bean,依次判断是否是处理器,并检测其HandlerMethod
detectHandlerMethods(beanName);
}
}
}
//后置处理什么也没有做
handlerMethodsInitialized(getHandlerMethods());
}
它直接调用了initHandlerMethods()方法,并且该方法被描述为:扫描ApplicationContext中的beans,检测并注册处理器方法。we are close。
检测@RequestMapping
//RequestMappingHandlerMapping#isHandler
@Override
protected boolean isHandler(Class> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
看看有没有被@Controller或者@RequestMapping注解标记
//org.springframework.web.reactive.result.method.AbstractHandlerMethodMapping.detectHandlerMethods
protected void detectHandlerMethods(final Object handler) {
Class> handlerType = (handler instanceof String beanName ? obtainApplicationContext().getType(beanName) : handler.getClass());
if (handlerType != null) {
final Class> userType = ClassUtils.getUserClass(handlerType);
Map methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup) method -> getMappingForMethod(method, userType));
methods.forEach((method, mapping) -> {
//根据方法上的@RequestMapping来创建RequestMappingInfo实例。
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
//注册请求映射
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
先获取方法上的@RequestMapping信息,然后获取类级别上的@RequestMapping 信息,然后将两者结合,这里我们有必要再了解下怎样创建RequestMappingInfo对象的(包括他的内部结构),以及怎样将类级别的request mapping信息和方法级别的进行结合的?
/**
* 获取 RequestMappingInfo 信息
* 获取@RequestMapping注解
*提供一个自定义类型级别的请求条件。 getCustomTypeCondition
* 提供一个自定义方法级请求条件 getCustomMethodCondition
* @param element
* @return
*/
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
RequestCondition> condition = (element instanceof Class ?
getCustomTypeCondition((Class>) element) : getCustomMethodCondition((Method) element));
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
当请求到达时,去urlMap中需找匹配的url,以及获取对应mapping实例,然后去handlerMethods中获取匹配HandlerMethod实例。
//org.springframework.web.reactive.result.method.AbstractHandlerMethodMapping.MappingRegistry.register(T, java.lang.Object, java.lang.reflect.Method)
public void register(T mapping, Object handler, Method method) {
this.readWriteLock.writeLock().lock();
try {
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
validateMethodMapping(handlerMethod, mapping);
Set directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
for (String path : directPaths) {
this.pathLookup.add(path, mapping);
}
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
corsConfig.validateAllowCredentials();
this.corsLookup.put(handlerMethod, corsConfig);
}
this.registry.put(mapping,
new MappingRegistration<>(mapping, handlerMethod, directPaths, corsConfig != null));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
SpringMVC 初始化 - HttpservletBean
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
要分析 SpringMVC 的初始化过程,就必须从 HttpServletBean 的 init 方法开始。
创建 PropertyValues
至于两个空方法:
- initBeanWrapper :在 SpringMVC 中并未做任务实现,而是留给用户扩展
- initServletBean:在其子类 FrameworkServlet 作具体实现。