zoukankan      html  css  js  c++  java
  • 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();
        }
    
    }
  • 相关阅读:
    一步步学习微软InfoPath2010和SP2010--第六章节--发布并提交表单数据(6)--关键点
    一步步学习微软InfoPath2010和SP2010--第六章节--发布并提交表单数据(5)--管理员批准模板
    一步步学习微软InfoPath2010和SP2010--第六章节--发布并提交表单数据(4)--作为内容类型发布
    一步步学习微软InfoPath2010和SP2010--第六章节--发布并提交表单数据(3)--提交到SharePoint
    一步步学习微软InfoPath2010和SP2010--第六章节--发布并提交表单数据(2)--提升栏目
    一步步学习微软InfoPath2010和SP2010--第六章节--发布并提交表单数据(1)--发布方法
    release
    CMake 入门实战,从实例入手,讲解 CMake 的常见用法,例如aux_source_directory的用法
    为 CmakeLists.txt 添加 boost 组件
    Linux下使用CMake进行编译的时候寻找Boost库
  • 原文地址:https://www.cnblogs.com/toov5/p/11874905.html
Copyright © 2011-2022 走看看