从以下三个方面进行介绍:
- Servlet与Spring MVC之间的关系
- Servlet框架线程是否安全
- Spring MVC是如何完全无web.xml启动的
Spring MVC是基于Servlet实现的封装。
首先回顾下Servlet:
Servlet是sun公司提供的一门用于开发动态web资源的技术。
Sun公司在其API中提供了一个servlet接口,用户若想用发一个动态web资源(即开发一个Java程序向浏览器输出数据),需要完成以下2个步骤:
1、编写一个Java类,实现servlet接口。
2、把开发好的Java类部署到web服务器中。
按照一种约定俗成的称呼习惯,通常我们也把实现了servlet接口的java程序,称之为Servlet
创建工程:
IDEA添加如下参数可以防止长时间Build
需要Servlet环境,则进入Servlet的Jar包,两种方式:
1.Tomcat自带的
2.mavne 引入的
在JavaEE项目必须有web.xml,那么为啥在SpringBoot不需要web.xml?
1.xml版本:
public class MyServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().write("hello world"); } }
xml配置:
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <servlet> <servlet-name>MyServletservlet-name> <servlet-class>com.toov5.servlet01.MyServletservlet-class> servlet> <servlet-mapping> <servlet-name>MyServletservlet-name> <url-pattern>/hellourl-pattern> servlet-mapping> web-app>
启动并且访问:
注: Servlet线程不安全,单例才会产生共享。使用适合加锁哦
关于过滤器和拦截器:
拦截器与过滤器区别
- 拦截器和过滤器都是基于Aop实现,能够对请求执行之前和之后实现拦截。
- 过滤器是基于Servlet容器实现,对Web请求之前和之后实现拦截
- 拦截器不需要依赖于Servlet、不仅可以实现Web请求还有其他方法拦截等。
SpringMVC拦截器的使用
- 自定义拦截拦截请求Token
preHandle在业务处理器处理请求之前被调用;
postHandle在业务处理器处理请求执行完成后,生成视图之前执行;
afterCompletion在DispatcherServlet完全处理完请求后被调用,可用于清理资源等 。afterCompletion()执行完成后开始渲染页面
拦截器:
/** * 拦截传递参数中是否有token */ public class TokenInterceptor implements HandlerInterceptor { /** * 请求方法前置拦截,如果返回True表示会执行目标方法(请求方法) 如果返回false的情况下,则不会执行目标方法 * @param request * @param response * @param handler * @return * @throws Exception */ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println(">>>preHandle<<<"); //回去token String token = request.getParameter("token"); if (StringUtils.isEmpty(token)) { //响应下 response.getWriter().print("not find token"); return false; } return true; } /** * * @param request * @param response * @param handler * @param modelAndView * @throws Exception */ public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println(">>>>>postHandle<<<<<<<<<"); }
//渲染页面之后执行 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println(">>>afterCompletion<<<"); } }
配置:
@Configuration @ComponentScan( basePackages = {"com.toov5.controller","com.toov5.service"}) @EnableWebMvc //等于开启Spring MVC注解方式 @EnableAsync public class SpringMVCConfig implements WebMvcConfigurer { /** * 视图解析器 * @return */ @Bean public InternalResourceViewResolver internalResourceViewResolver(){ InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver(); internalResourceViewResolver.setPrefix("/WEB-INF/view/"); internalResourceViewResolver.setSuffix(".jsp"); return internalResourceViewResolver; } //1. 手动注入拦截器到Spring中 @Bean public TokenInterceptor tokenInterceptor(){ return new TokenInterceptor(); } //2. 添加拦截器 public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(tokenInterceptor()).addPathPatterns("/**"); // /**表示拦截所有请求 也可以排除某个请求 } DispatcherServlet }
注意:使用拦截器一定要关闭EnableWebMvc 否则拦截器不会生效。
ServletContainerInitializer
关于Servlet的接口:ServletContainerInitializer. 涉及到Spring Boot Spring MVC如何实现没有web.xml启动的。
在web容器启动时为提供给第三方组件机会做一些初始化的工作,例如注册servlet或者filtes等,servlet规范中通过ServletContainerInitializer实现此功能。
每个框架要使用ServletContainerInitializer就必须在对应的jar包的META-INF/services 目录创建一个名为javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的ServletContainerInitializer实现类。
Servlet容器在初始化时候可以加载一些第三方相关依赖初始化工作,比如
实现接口:
@HandlesTypes(value = MyHandlesType.class) public class MyServletContainerInitializer implements ServletContainerInitializer { /** * 作用是servlet容器初始化加载一些操作 比如第三方依赖信息 手动加载Servlet、监听器、过滤器 * @param set 获取继承该类MyHandersType类所有子类class信息 (官方称之为感兴趣的类) * @param servletContext * @throws ServletException */ public void onStartup(Set> set, ServletContext servletContext) throws ServletException { // 1.打印所有感兴趣的类型 for (Class> c : set) { System.out.println(c); } // 2.servletContext 手动注册过滤器、servlet、监听器 ServletRegistration.Dynamic payServlet = servletContext.addServlet("payServlet", new PayServlet()); payServlet.addMapping("/pay"); } }
定义类:
public class MyHandlesType { }
继承类:
public class BookServlet extends MyHandlesType { }
定义servlet:
public class PayServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().print("this is pay"); } }
启动并访问
注: tomcat回去META-INF下面读取配置:
tomcat启动时候:
下面搭建Spring mvc的环境:
可以看到引入包
<dependency> <groupId>org.springframeworkgroupId> <artifactId>spring-webmvcartifactId> <version>5.2.1.RELEASEversion> dependency>
后
DispatcherServlet是Spring MVC的核心类,客户端所有的请求都转发到DispatcherServlet,最终执行请求定义的方法。其实就是一个Servlet类,无非就是包装来根据URL能够映射找到我们Spring mvc中定义的方法。
源代码分析:
看继承图:
继承FrameworkServlet 继承 HttpServlet
面向对象思想,重写。执行先父类再之类。 执行父类的原因:
super.service(request, response);
请求过来先走Service方法,判断类型 如果是get,执行doGet方法。
流程:HttpServlet Service 判断请求如果Get请求
- getHandler(),通过url路径地址查询到具体的控制请求的方法,如果没有找到情况下直接返回404。
Handler 对应的其实就是我们的控制层类方法
SpringMVC容器 存放当前jvm中所有url映射的路径对应的请求的方法存放到Map集合。
Key:url value:请求方法
SpringMVC映射路径的容器在什么时候被创建呢?
Servlet在调用我们的init的方法被创建的。
Handler请求控制层 HandlerMethod 指的请求具体方法。
Spring mvc中核心类:
dispatcherServlet 前端控制器,就是个Servlet类,包装了而已,根据UTL能够映射找到我们的Spring mvc定义的请求方法。
在上面的config 也配合了这个类。
关系:DispatcherServlet继承FrameworkServlet继承HttpServlet
流程执行关系:
HttpServlet service方法 判断请求方法的类型
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); long lastModified; if (method.equals("GET")) { lastModified = this.getLastModified(req); if (lastModified == -1L) { this.doGet(req, resp); } else { long ifModifiedSince; try { ifModifiedSince = req.getDateHeader("If-Modified-Since"); } catch (IllegalArgumentException var9) { ifModifiedSince = -1L; } if (ifModifiedSince < lastModified / 1000L * 1000L) { this.maybeSetLastModified(resp, lastModified); this.doGet(req, resp); } else { resp.setStatus(304); } } } else if (method.equals("HEAD")) { lastModified = this.getLastModified(req); this.maybeSetLastModified(resp, lastModified); this.doHead(req, resp); } else if (method.equals("POST")) { this.doPost(req, resp); } else if (method.equals("PUT")) { this.doPut(req, resp); } else if (method.equals("DELETE")) { this.doDelete(req, resp); } else if (method.equals("OPTIONS")) { this.doOptions(req, resp); } else if (method.equals("TRACE")) { this.doTrace(req, resp); } else { String errMsg = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[]{method}; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(501, errMsg); } }
FrameworkServlet doGet
DispatcherServlet doService 可以直接打断点到这里
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { this.logRequest(request); MapattributesSnapshot = null; if (WebUtils.isIncludeRequest(request)) { attributesSnapshot = new HashMap(); Enumeration attrNames = request.getAttributeNames(); label95: while(true) { String attrName; do { if (!attrNames.hasMoreElements()) { break label95; } attrName = (String)attrNames.nextElement(); } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet")); attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext()); request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource()); if (this.flashMapManager != null) { FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); if (inputFlashMap != null) { request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); } request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); } try { this.doDispatch(request, response); //核心方法! } finally { if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) { this.restoreAttributesAfterInclude(request, attributesSnapshot); } } }
看下
doDispatch:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { try { ModelAndView mv = null; Object dispatchException = null; try { processedRequest = this.checkMultipart(request); multipartRequestParsed = processedRequest != request;
//获取到Handler mappedHandler = this.getHandler(processedRequest); if (mappedHandler == null) { this.noHandlerFound(processedRequest, response); return; } HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler()); String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) { return; } } // 执行拦截器的前置方法 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // 执行请求的目标方法,获取到 modelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } this.applyDefaultViewName(processedRequest, mv);
//执行拦截器的后置方法 mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception var20) { dispatchException = var20; } catch (Throwable var21) { dispatchException = new NestedServletException("Handler dispatch failed", var21); } this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException); } catch (Exception var22) { this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22); } catch (Throwable var23) { this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23)); } } finally { if (asyncManager.isConcurrentHandlingStarted()) { if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else if (multipartRequestParsed) { this.cleanupMultipart(processedRequest); } } }
关于 getHandler
@Nullable protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { Iterator var2 = this.handlerMappings.iterator(); while(var2.hasNext()) { HandlerMapping mapping = (HandlerMapping)var2.next(); HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null; }
重点:
Iterator var2 = this.handlerMappings.iterator();
可以打个断点看看:
handler请求控制层
handlerMethod 请求具体方法
handlerMapping 当前所有url路径映射的集合,包括拦截器
有多个handler,表示来源,有xml方式的,有注解方式的,也有类中配置的。
然后获取适配器: (可以自定义)
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { if (this.handlerAdapters != null) { Iterator var2 = this.handlerAdapters.iterator(); while(var2.hasNext()) { HandlerAdapter adapter = (HandlerAdapter)var2.next(); if (adapter.supports(handler)) {
//返回这个适配器 return adapter; } } } throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"); }
通过适配器,最终去执行方法。
DispatcherServlet源码流程分析总结:
1.执行doDispatch
2.调用getHandler方法获取请求目标的方法 也就是 请求url映射路径对应的控制层具体的方法
handlerMappings的作用查找控制器位置,比如xml和注解方式。
3.调用getHandlerAdapter获取控制层适配器 RequestMappingHandlerAdapter
4.执行拦截器前置方法 preHandle() 如果返回为true的话
5.执行实际请求目标方法 返回modeAndView对象
6.执行拦截器PostHandle()方法
7.设置渲染视图层内容
8.执行拦截器afterCompletion方法
(29-05)
SpringMVC控制层容器初始化
- HttpServletBean init ()方法
- FrameworkServlet initServletBean方法→ initWebApplicationContext();
- DispatcherServlet onRefresh方法→ initStrategies()方法
DispatcherServlet:
protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); //初始化上传文件解析器(或者是多部分请求解析器) initLocaleResolver(context);//初始化本地化解析器 initThemeResolver(context);//初始化主题解析器 initHandlerMappings(context);//初始化处理器映射器 initHandlerAdapters(context);//初始化处理器适配器 initHandlerExceptionResolvers(context);//初始化处理器异常解析器 initRequestToViewNameTranslator(context);//初始化请求到视图名翻译器 initViewResolvers(context);//初始化视图解析器 initFlashMapManager(context);//初始化重定向数据管理器 }
经过此处的调用:
protected void onRefresh(ApplicationContext context) { this.initStrategies(context); }
在父类
FrameworkServlet 进行调用
if (!this.refreshEventReceived) { Object var6 = this.onRefreshMonitor; synchronized(this.onRefreshMonitor) { this.onRefresh(wac); } }
一直往上找:
HttpServletBeand 的
init()方法
当servlet容器初始化时候进行初始化。
https://www.cnblogs.com/yanghongfei/p/8507632.html
应用:
pom引用纯Servlet:
<dependencies> <dependency> <groupId>javax.servletgroupId> <artifactId>javax.servlet-apiartifactId> <version>3.1.0version> dependency> <dependency> <groupId>org.apache.commonsgroupId> <artifactId>commons-lang3artifactId> <version>3.0version> dependency> dependencies>
注解的封装:
扫包:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented public @interface ComponentScan { String value() default ""; }
@Controller注解:配合Config类
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Controller { String value() default ""; }
@Requesting注解:
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RequestMapping { String value() default ""; }
config类:
@ComponentScan("com.test.controller") public class SpringMvcConfig { }
controller类:
@Controller public class PayController { @RequestMapping("/pay") public String pay() { return "pay"; } }
核心部分:
public class HttpServletBean extends HttpServlet { //HttpServletBean 作用 /** * SpringMVC思路: * 控制层和url映射关联定义好存放到Map集合中 肯定在项目启动时候 * 1.扫包获取class中的方法有加上RequestMapping 如果有有该注解的话 存放到map集合 * 2.key:url:value 方法 ** 访问这个请求 根据url查找对应的执行的方法在通过java反射执行该方法。
*/ @Override public void init() throws ServletException { // 初始化我们的springmvcbean的对象 和url与方法关联 initServletBean(); } protected void initServletBean() { } @Override protected void service(HttpServletRequest req, HttpServletResponse resp) { doService(req,resp); } protected void doService(HttpServletRequest req, HttpServletResponse resp) { } }
public class FrameworkServlet extends HttpServletBean { @Override protected void initServletBean() { onRefresh(); } protected void onRefresh() { } @Override protected void doService(HttpServletRequest req, HttpServletResponse resp) { } }
public class DispatcherServlet extends FrameworkServlet { private RequestMappingHandlerMapping requestMappingHandlerMapping; public DispatcherServlet() { requestMappingHandlerMapping = new RequestMappingHandlerMapping(); } @Override protected void onRefresh() { initStrategies(); } private void initStrategies() { requestMappingHandlerMapping.initHandlerMappings(); } @Override protected void doService(HttpServletRequest req, HttpServletResponse resp) { doDispatch(req, resp); } private void doDispatch(HttpServletRequest req, HttpServletResponse resp) { try { // 1.处理请求url String requestURI = req.getRequestURI(); // 2.根据url查找对应的Handler HandlerExecutionChain handler = getHandler(requestURI); if (handler == null) { noHandlerFound(req, resp); return; } // 3.使用java的反射机制执行请求方法 返回对应的modelAndView ModelAndView modelAndView = handler.handler(); // 4.开始渲染视图层 render(modelAndView, req, resp); } catch (Exception e) { } } public void render(ModelAndView modelAndView, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String viewName = modelAndView.getViewName(); req.getRequestDispatcher("/WEB-INF/view/" + viewName + ".jsp").forward(req, resp); } private HandlerExecutionChain getHandler(String url) { HandlerMethod handlerMethod = requestMappingHandlerMapping.getHandlerMethod(url); if (handlerMethod == null) { return null; } HandlerExecutionChain handlerExecutionChain = new HandlerExecutionChain(handlerMethod); return handlerExecutionChain; } protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception { throw new Exception("没有查找到对应的请求"); } }
接口
public interface WebApplicationInitializer { void onStartup(ServletContext servletContext) throws ServletException; }
实现:
public class AbstractDispatcherServletInitializer implements WebApplicationInitializer { public void onStartup(ServletContext servletContext) throws ServletException { // 1.开始注册我们的DispatcherServlet ServletRegistration.Dynamic dispatcherServlet = servletContext.addServlet("dispatcherServlet", new DispatcherServlet()); dispatcherServlet.addMapping("/");// 拦截所有的请求 // dispatcherServlet.setLoadOnStartup(1); } }
装配类:
@HandlesTypes(WebApplicationInitializer.class) public class SpringServletContainerInitializer implements ServletContainerInitializer { /** * onStartup servlet容器初始化的时候就会调用该方法 * * @param classInfos 获取 WebApplicationInitializer 所有的子类 * @param ctx * @throws ServletException */ public void onStartup(Set> classInfos, ServletContext ctx) throws ServletException { for (Class> classInfo : classInfos) { //classInfo 都是WebApplicationInitializer类的子类 try { Method method = classInfo.getMethod("onStartup", ServletContext.class); Object object = classInfo.newInstance(); method.invoke(object, ctx); } catch (Exception e) { e.printStackTrace(); } } } }
反射工具类:
public class ReflexUtils { /** * 从包package中获取所有的Class * * @param pack * @return */ public static Set> getClasses(String pack) { // 第一个class类的集合 Set > classes = new LinkedHashSet >(); // 是否循环迭代 boolean recursive = true; // 获取包的名字 并进行替换 String packageName = pack; String packageDirName = packageName.replace('.', '/'); // 定义一个枚举的集合 并进行循环来处理这个目录下的things Enumeration dirs; try { dirs = Thread.currentThread().getContextClassLoader().getResources( packageDirName); // 循环迭代下去 while (dirs.hasMoreElements()) { // 获取下一个元素 URL url = dirs.nextElement(); // 得到协议的名称 String protocol = url.getProtocol(); // 如果是以文件的形式保存在服务器上 if ("file".equals(protocol)) { System.err.println("file类型的扫描"); // 获取包的物理路径 String filePath = URLDecoder.decode(url.getFile(), "UTF-8"); // 以文件的方式扫描整个包下的文件 并添加到集合中 findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes); } else if ("jar".equals(protocol)) { // 如果是jar包文件 // 定义一个JarFile System.err.println("jar类型的扫描"); JarFile jar; try { // 获取jar jar = ((JarURLConnection) url.openConnection()) .getJarFile(); // 从此jar包 得到一个枚举类 Enumeration entries = jar.entries(); // 同样的进行循环迭代 while (entries.hasMoreElements()) { // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件 JarEntry entry = entries.nextElement(); String name = entry.getName(); // 如果是以/开头的 if (name.charAt(0) == '/') { // 获取后面的字符串 name = name.substring(1); } // 如果前半部分和定义的包名相同 if (name.startsWith(packageDirName)) { int idx = name.lastIndexOf('/'); // 如果以"/"结尾 是一个包 if (idx != -1) { // 获取包名 把"/"替换成"." packageName = name.substring(0, idx) .replace('/', '.'); } // 如果可以迭代下去 并且是一个包 if ((idx != -1) || recursive) { // 如果是一个.class文件 而且不是目录 if (name.endsWith(".class") && !entry.isDirectory()) { // 去掉后面的".class" 获取真正的类名 String className = name.substring( packageName.length() + 1, name .length() - 6); try { // 添加到classes classes.add(Class .forName(packageName + '.' + className)); } catch (ClassNotFoundException e) { // log // .error("添加用户自定义视图类错误 找不到此类的.class文件"); e.printStackTrace(); } } } } } } catch (IOException e) { // log.error("在扫描用户定义视图时从jar包获取文件出错"); e.printStackTrace(); } } } } catch (IOException e) { e.printStackTrace(); } return classes; } /** * 以文件的形式来获取包下的所有Class * * @param packageName * @param packagePath * @param recursive * @param classes */ public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, Set > classes) { // 获取此包的目录 建立一个File File dir = new File(packagePath); // 如果不存在或者 也不是目录就直接返回 if (!dir.exists() || !dir.isDirectory()) { // log.warn("用户定义包名 " + packageName + " 下没有任何文件"); return; } // 如果存在 就获取包下的所有文件 包括目录 File[] dirfiles = dir.listFiles(new FileFilter() { // 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件) public boolean accept(File file) { return (recursive && file.isDirectory()) || (file.getName().endsWith(".class")); } }); // 循环所有文件 for (File file : dirfiles) { // 如果是目录 则继续扫描 if (file.isDirectory()) { findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, classes); } else { // 如果是java类文件 去掉后面的.class 只留下类名 String className = file.getName().substring(0, file.getName().length() - 6); try { // 添加到集合中去 //classes.add(Class.forName(packageName + '.' + className)); //这里用forName有一些不好,会触发static方法,没有使用classLoader的load干净 classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className)); } catch (ClassNotFoundException e) { // log.error("添加用户自定义视图类错误 找不到此类的.class文件"); e.printStackTrace(); } } } } }
ModelAndView:
public class ModelAndView { // 跳转页面名称 private String viewName; public ModelAndView(String viewName) { this.viewName = viewName; } public String getViewName() { return viewName; } }
Handler: (handler有拦截器进行包装,每个请求的方法有拦截器)
public class HandlerExecutionChain { HandlerMethod handlerMethod; // 拦截器 public HandlerExecutionChain(HandlerMethod handlerMethod) { this.handlerMethod = handlerMethod; } public ModelAndView handler() throws InvocationTargetException, IllegalAccessException { // 1. 使用java的反射机制执行我们请求方法 Method method = handlerMethod.getMethod(); Object bean = handlerMethod.getBean(); // 2.执行我们的请求的方法 Object viewName = method.invoke(bean, null); ModelAndView modelAndView = new ModelAndView((String) viewName); return modelAndView; } }
public class HandlerMethod { // 请求方法对应的bean对象 private Object bean; private Method method; public HandlerMethod(Object bean, Method method) { this.bean = bean; this.method = method; } public Object getBean() { return bean; } public Method getMethod() { return method; } /** * 1.参数的问题 */ }
com.mayikt.servlet.web.SpringServletContainerInitializer
JSP:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Titletitle> head> <body> successful! go on! body> html>
访问:
关于适配器:
定义:将一个系统的接口转换成另外一种形式,从而使原来不能直接调用的接口变得可以调用。
应用场景:
- Mybatis多种日志框架的整合
- SpringMVC适配器模式,Spring MVC通过Handler获取对应的适配器,然后通过适配器执行我们请求的方法。
- 新老版本的兼容问题
Spring MVC中有三种适配器:
流程:
1.使用getHandlerAdapter获取对应的hanlder的具体HandlerAdapter
2.HandlerAdapter接口有如下的子 c处理请求适配器(三种)
2.1继承Controller方式所使用的适配器:SimpleControllerHandlerAdapter
2.2 HTTP请求处理器适配器:HttpRequestHandlerAdapter
3.3注解方式(@Controller)的处理器适配器:RequestMappingHandlerAdapter
使用适配器针对不同的handelr类型中安多不同的适配器实现执行。可以方便扩展。
适配器接口:
public interface HandlerAdapter { /** * 根据hanlder判断是那个HandlerAdapter类型 如果找到对应的类型话返回true * * @param handler * @return */ boolean supports(Object handler); /** * 执行我们的请求方法 */ void handle(Object handler); }
适配器实现类:
public class AnnotationHandlerAdapter implements HandlerAdapter { /** * 注解形式的适配器 * * @param handler * @return */ public boolean supports(Object handler) { return (handler instanceof AnnotationController); } public void handle(Object handler) { ((AnnotationController) handler).hanlder(); } }
public class HttpRequestHandlerAdapter implements HandlerAdapter { /** * Http类型 适配器 * * @param handler * @return */ public boolean supports(Object handler) { return (handler instanceof HttpController); } public void handle(Object handler) { ((HttpController) handler).hanlder(); } }
接口:
public interface Controller { void hanlder(); }
不同的实现类:
public class HttpController implements Controller { public void hanlder() { System.out.println("HttpController"); } }
public class AnnotationController implements Controller { public void hanlder() { System.out.println("AnnotationController"); } }
Servlet 测试:
public class DispatcherServlet { private ListhandlerAdapters; public DispatcherServlet() { handlerAdapters = new ArrayList (); handlerAdapters.add(new HttpRequestHandlerAdapter()); handlerAdapters.add(new AnnotationHandlerAdapter()); } public void dispatcher() { // 1. 已经获取到hanlder AnnotationController hanlder = new AnnotationController(); // 2.获取具体适配器 HandlerAdapter handlerAdapter = getHandlerAdapter(hanlder); // 3.执行我们的请求方案 handlerAdapter.handle(hanlder); } public HandlerAdapter getHandlerAdapter(Controller controller) { if (this.handlerAdapters != null) { for (HandlerAdapter ha : this.handlerAdapters) { if (ha.supports(controller)) { return ha; } } } return null; } public static void main(String[] args) { new DispatcherServlet().dispatcher(); } }