Spring MVC源码分析

 

从以下三个方面进行介绍:

  1.  Servlet与Spring MVC之间的关系
  2.  Servlet框架线程是否安全
  3. 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>MyServlet</servlet-name>
  <servlet-class>com.toov5.servlet01.MyServlet</servlet-class>
</servlet>
  
<servlet-mapping>
  <servlet-name>MyServlet</servlet-name>
  <url-pattern>/hello</url-pattern>
</servlet-mapping>  
 
</web-app>
  

启动并且访问:

 

 

注: Servlet线程不安全,单例才会产生共享。使用适合加锁哦

 

关于过滤器和拦截器:

拦截器与过滤器区别
  • 拦截器和过滤器都是基于Aop实现,能够对请求执行之前和之后实现拦截。
  • 过滤器是基于Servlet容器实现,对Web请求之前和之后实现拦截
  • 拦截器不需要依赖于Servlet、不仅可以实现Web请求还有其他方法拦截等。

 

SpringMVC拦截器的使用
  1. 自定义拦截拦截请求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<Class<?>> 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的环境:

 

可以看到引入包

 <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.1.RELEASE</version>
    </dependency>

 

 

 

DispatcherServlet是Spring MVC的核心类,客户端所有的请求都转发到DispatcherServlet,最终执行请求定义的方法。其实就是一个Servlet类,无非就是包装来根据URL能够映射找到我们Spring mvc中定义的方法。

源代码分析:

 

 看继承图:

 

 

继承FrameworkServlet 继承 HttpServlet

面向对象思想,重写。执行先父类再之类。 执行父类的原因:

super.service(request, response);

请求过来先走Service方法,判断类型 如果是get,执行doGet方法。

 

 

流程:HttpServlet Service 判断请求如果Get请求

  1. 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);
        Map<String, Object> attributesSnapshot = 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控制层容器初始化
  1. HttpServletBean  init ()方法
  2. FrameworkServlet initServletBean方法→  initWebApplicationContext();
  3. 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

 

 


 

应用:

 b

pom引用纯Servlet:

<dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.0</version>
        </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 方法
     * <p>
     * 访问这个请求 根据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<Class<?>> 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<Class<?>> getClasses(String pack) {

        // 第一个class类的集合
        Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
        // 是否循环迭代
        boolean recursive = true;
        // 获取包的名字 并进行替换
        String packageName = pack;
        String packageDirName = packageName.replace('.', '/');
        // 定义一个枚举的集合 并进行循环来处理这个目录下的things
        Enumeration<URL> 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<JarEntry> 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<Class<?>> 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>Title</title>
</head>
<body>
successful! go on!
</body>
</html>

 

访问:

 

 

 

关于适配器:

定义:将一个系统的接口转换成另外一种形式,从而使原来不能直接调用的接口变得可以调用。

 

应用场景:

  1. Mybatis多种日志框架的整合
  2. SpringMVC适配器模式,Spring MVC通过Handler获取对应的适配器,然后通过适配器执行我们请求的方法。
  3. 新老版本的兼容问题

 

 

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 List<HandlerAdapter> handlerAdapters;

    public DispatcherServlet() {
        handlerAdapters = new ArrayList<HandlerAdapter>();
        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();
    }

}

 

posted @ 2019-11-17 00:50  toov5  阅读(348)  评论(0编辑  收藏  举报