网站首页 > 开源技术 正文
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<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) 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<String> 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 作具体实现。
猜你喜欢
- 2024-12-23 完全零基础入门Fastjson系列漏洞 fastjson漏洞最新bug
- 2024-12-23 在老挝汽车远不如摩托车实用,上下班高峰期,老挝交通太糟糕
- 2024-12-23 十万字解析java免查杀合集 10万字java总结
- 2024-12-23 8K字详解Java安全之动态加载字节码
- 2024-12-23 白盒自动化测试工具:FindBugs的使用指南
- 2024-12-23 Java单向代码执行链配合的动态代码上下文执行
- 2024-12-23 2023级网络安全岗面试题及面试经验分享
- 2024-12-23 结合 Jimureport 的某个漏洞披露看 Aviator 表达式注入
- 2024-12-23 IDEA 的 debug 怎么实现?出于这个好奇心,我越挖越深
- 2024-12-23 虚拟机字节码操作黑科技工具简介 子虚拟机
你 发表评论:
欢迎- 05-16东契奇:DFS训练时喷了我很多垃圾话 我不懂他为什么比赛不这么干
- 05-16这两球很伤!詹姆斯空篮拉杆不中 DFS接里夫斯传球空接也没放进
- 05-16湖人自媒体调查:89%球迷希望DFS回归79%希望詹姆斯回归
- 05-16Shams:湖人得到全能球员DFS 节省了1500万奢侈税&薪金空间更灵活
- 05-16G5湖人胜率更高!詹姆斯不满判罚,DFS谈5人打满下半场:这很艰难
- 05-16DFS:当东契奇进入状态 所有防守者在他面前都像个圆锥桶
- 05-16上一场9中6!DFS:不能让纳兹-里德这样的球员那么轻松地投三分
- 05-16WIDER FACE评测结果出炉:滴滴人脸检测DFS算法获世界第一
- 最近发表
-
- 东契奇:DFS训练时喷了我很多垃圾话 我不懂他为什么比赛不这么干
- 这两球很伤!詹姆斯空篮拉杆不中 DFS接里夫斯传球空接也没放进
- 湖人自媒体调查:89%球迷希望DFS回归79%希望詹姆斯回归
- Shams:湖人得到全能球员DFS 节省了1500万奢侈税&薪金空间更灵活
- G5湖人胜率更高!詹姆斯不满判罚,DFS谈5人打满下半场:这很艰难
- DFS:当东契奇进入状态 所有防守者在他面前都像个圆锥桶
- 上一场9中6!DFS:不能让纳兹-里德这样的球员那么轻松地投三分
- WIDER FACE评测结果出炉:滴滴人脸检测DFS算法获世界第一
- 湖人自媒体调查:89%球迷希望DFS回归 79%希望詹姆斯回归
- 一觉醒来湖人苦盼的纯3D终于到位 DFS能带给紫金军多少帮助
- 标签列表
-
- jdk (81)
- putty (66)
- rufus (78)
- 内网穿透 (89)
- okhttp (70)
- powertoys (74)
- windowsterminal (81)
- netcat (65)
- ghostscript (65)
- veracrypt (65)
- asp.netcore (70)
- wrk (67)
- aspose.words (80)
- itk (80)
- ajaxfileupload.js (66)
- sqlhelper (67)
- express.js (67)
- phpmailer (67)
- xjar (70)
- redisclient (78)
- wakeonlan (66)
- tinygo (85)
- startbbs (72)
- webftp (82)
- vsvim (79)
本文暂时没有评论,来添加一个吧(●'◡'●)