Spring MVC源码分析

Spring MVC源码分析之请求分发 在使用SpringMvc时,通过Controller跟,就能实现网络请求的处理。那么,这是怎么实现的呢?请求是如何从Tomcat进入到controller里的方法的呢? 阅读详情

 

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

  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();
    }

}

 

Spring MVC源码分析——初始化过程 1.      概述 对于Web开发者,MVC模型是大家再熟悉不过的了,SpringMVC中,满足条件的请求进入到负责请求分发的DispatcherServlet,DispatcherServlet根据请求url到控制器的映射(HandlerMapping中保存),HandlerMapping最终返回HandlerExecutionChain,其中包含了具体的处理对象handler(也即我们编程 阅读详情

相关推荐

Spring 源码分析--Spring源码面试系列---pring MVC实现原理过程解析

第一章Spring MVC实现原理 初探SpringMVC请求处理流程 第二章Spring MVC十大组件 MultipartResolver LocaleResolver ThemeResolver HandlerMappings HandlerAdapters HandlerExceptionResolvers RequestToViewNameTranslator ViewResolvers FlashMapManager 第三章Spring MVC源码分析 1、初始化阶段 2

java技术专家全栈成长之路 652

Spring 和 Spring MVC经典面试题及答案

关于Spring 和 Spring MVC的43个问题【问题汇总】   1.为什么使用Spring ? 1). 方便解耦,简化开发          通过Spring提供的IoC容器,可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。     2). AOP编程的支持          通过Spring提供的AOP功能,方便进行面向切面的编程,如性能监测...

林逗哥 4万+

Spring MVC 初始化源码分析

前言: 相信从事 Java 开发的小伙伴都使用用过 Spring MVC,同时我们知道 Spirng MVC 有 9 大组件,Spring MVC 的初始化其实就是对这些组件的初始化,前面我们分析了 Sping 的初始化,Spring 的初始化是Spring 在启动时候调用了 AbstractApplicationContext#refresh 方法完成初始化的,那 Sping MVC 又是什么时候完成初始化的?本篇我们将剖析一下 Spring MVC 的初始化过程。 Spring MVC 9 大组件 我们

weixin_42118323的博客 869

Java 多线程

1) 什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在 进程之中,是进程中的实际运作单位。程序员可以通过它进行多 处理器编程,你可以使用多线程对运算密集型任务提速。比如, 如果一个线程完成一个任务要 100 毫秒,那么用十个线程完成改 任务只需 10 毫秒。 2) 线程和进程有什么区别? 线程是进程的子集,一个进程可以有很多线程,每条线程并 ...

java技术专家全栈成长之路 414

Spring MVC 框架源码分析

在 getHandler() 方法中,它遍历 handlerMappings 列表,根据请求的特征匹配相应的映射条目,并返回对应的处理器。同样,Spring MVC 提供了多种实现类,其中包括 AnnotationMethodHandlerAdapter,它是用于处理带有注解的方法的适配器。Spring MVC 是一个基于 Java 的 Web 框架,它提供了一种模型-视图-控制器(Model-View-Controller,MVC)的架构模式,用于构建灵活、可扩展的 Web 应用程序。

CodeShiftZ的博客 119

从RadixTree到哈希表:深入对比SGLang和vLLM的Prefix Cache实现差异

本文深入对比了SGLang与vLLM在大语言模型推理服务中实现Prefix Cache(前缀缓存)的两种核心方案。SGLang采用基于Radix Tree的RadixAttention,支持任意长度前缀的精确匹配与灵活复用;vLLM则采用基于哈希表的方案,深度集成于PagedAttention,实现简洁但要求块级精确对齐。文章剖析了二者在设计哲学、内存管理、适用场景及集群级优化(如Prefix Cache Aware Routing)上的关键差异,为高性能LLM服务的技术选型提供决策依据。

blockchain9miner的博客 956

关于Servlet线程安全性和DispatcherServlet的线程安全性的解析

我们知道在Servlet第一次被调用的时候,Servlet容器会根据web.xml中配置的信息去实例化Servlet,而且这个Servlet只会被实例化一次。当多个请求同时到来时,可能会使用同一个Servlet进行处理,这时候就会涉及到线程安全的问题。 纠结!!! Servlet的线程安全性?...

chijunmei7041的博客 881

Dify Rerank 模型配置全流程解析:从报错定位到代码实现

Dify Rerank 模型配置全流程解析:从报错定位到代码实现

engchina的专栏 4471

java mvc 源码分析_Spring MVC 源码解析

写在前面在分析 Spring AOP 源码之前,如果你对 Spring IOC、依赖注入(DI) 原理不是很清楚,建议您先了解一下:Spring IOC 源码解析、Spring 依赖注入(DI) 源码解析,这样或许会让你的思路更加清晰。1.什么是 MVCMVC 作为一种设计模式,并不是仅限于 Java Web 应用,还包括许多应用,比如前端、PHP、.NET 等语言。之所以那么做的根本原因在于解耦...

weixin_29095587的博客 249

数学建模灵敏性分析(一阶、二阶灵敏度)python代码+懒人专用版

对于勉强看懂了灵敏度分析的同学来说,本文给出一个方便移植、修改的python程序,可以很方便地进行灵敏度分析。

春江花朝秋月夜 1万+

ComfyUI工作流换脸节点报错ReActorFaceSwap

找到自己ComfyUI安装目录的custom_nodes下是否有这个comfyui-reactor-node-main。大家在借鉴别人工作流进行AI换脸的时候,如果遇到ReActorFaceSwap报错提示,莫慌,看我的,两步解决问题。这里千万注意不要嵌套文件夹,保证点击去这个文件就可以看到内置的配置文件!找到自己ComfyUI安装目录的facerestore_models。如果有这个文件,直接跳过,去下一步。如果没有,可以去Git官网下载。

qq_70810809的博客 9016

【Spring MVC】Spring MVC的执行流程与源码分析

史上最全的Spring MVC执行流程源码解析,爆肝一个月带你彻底搞懂Spring MVC的底层原理。学习Spring MVC源码看这一篇就够了。

小七的博客 2108

RT-Thread littefs控制GD32F4xx片内Flash

GD32F4xx RT-Thread下使用littefs 控制片内flash 调试记录

madao1234的博客 995

spring mvc核心源码分析

前言 自研究了spring security核心源码以来,在实践使用的基础上更为深入的学习了它的思想.我的java世界仿佛被打开了一扇大门,开始对研究源码有种渴望.打算先看一轮核心源码,满足目前工作需要,待对boot有个整体的了解之后再逐个细细研究 spring mvc核心流程图 流程说明: 用户发送请求到dispatcherServlet,它执行父类frameworkServlet的...

qq_40285302的博客 218

DeepLabv3+改进专栏

🔥【DeepLabv3+改进专栏!探索语义分割新高度】🌟 你是否在为图像分割的精度与效率发愁?📢 本专栏重磅推出:✅ 独家改进策略:融合注意力机制、轻量化设计与多尺度优化✅ 即插即用模块:ASPP+升级、解码器。

AIcurator的博客 2576

用 SNC 和 SAP Single Sign-On Secure Login 把 SAP 登录这件小事,变成一件安全又不用输密码的事

在传统的 SAP 系统里,登录这件事看起来很朴素:用户打开 SAP GUI,输入一个 SAP 用户名,再输入一个 SAP 密码,点回车,进系统,开始干活。表面上没毛病,但从安全角度看,这种老办法问题很大。默认情况下,这些用户名和密码在网络上传输时是明文,也就是不加密传输。攻击者如果在公司网络里做监听,理论上可以直接截取到口令信息,这种风险对今天任何一家合规要求高、审计严格的企业来说,基本是不可接受的。(SAP Help Portal)SAP 给出的答案叫做 ,全称是 。SNC 做的事情可以概括成两类:一类是

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 377

spring mvc 源码分析之spring mvc初始化

spring mvc 源码分析 spring mvc初始化源码分析spring mvc 源码分析前言一、DispatcherServlet中定义的九个属性二、九⼤组件的初始化时机onRefresh(ApplicationContext context)开始第一次请求初始化处理器映射 initHandlerMappings(context);DispatcherServlet.properties 文件配置的内容 前言 我们知道spring mvc要实现web能力,主要依靠九大组件。因此spring mvc初

baidu_38652335的博客 437

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

java mvc 源码分析_Spring MVC 源码分析

Spring MVCSpringMVC中扮演关键角色的DispatcherServlet类。1DispatcherServlet1.1DispatcherServlet 类图1.2初始准备阶段1.2.1 init()HttpServletBean类中init()方法public final void init() throwsServletException {if (this.logger....

weixin_33318722的博客 126

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

spring-mvc源码分析之案例搭建

spring-mvc源码分析之案例搭建一、创建案例在源码工程中创建model在源码工程中创建model选择gradle 创建web项目添加DemoController类 和springmvc.xml以及web.xml和显示的index.jsp二、测试示例idea+maven tomcat配置idea+gradle tomcat配置下载 tomcat下载完成后 解压 配置编码**idea配置总结 一、创建案例 在源码工程中创建model 创建后的结构图 在源码工程中创建model选择gradle 创建web

baidu_38652335的博客 468

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

【Spring系列-09】Spring MVC 请求处理与源码分析

这篇文章深入解析了Spring MVC的核心处理流程,重点剖析了DispatcherServlet和HandlerMapping的工作原理。文章以用户登录请求为例,详细展示了请求从接收到响应的完整过程,包括:请求分发、处理器查找、适配器调用、视图渲染等关键步骤。通过分析doDispatch()方法源码,揭示了Spring MVC内部精巧的组件协作机制,并辅以流程图直观呈现请求流转路径。文章还介绍了HandlerMapping的作用及常见URL映射场景,为理解Spring MVC的请求处理机制提供了清晰的视角

专注后端技术架构,AI技术分享 1301

【Spring MVC】HandlerMapping初始化详解(超详细过程源码分析)

【Spring MVC】HandlerMapping初始化详解(超详细过程源码分析)

c19990515的博客 141

Spring MVC中的CharacterEncodingFilter介绍以及源码分析

一、场景 CharacterEncodingFilter是Spring MVC很简单很实用的一个过滤器,当前台JSP页面和JAVA代码中使用了不同的字符集进行编码的时候就会出现表单提交的数据或者上传/下载中文名称文件出现乱码的问题,那这个类就可以出场了。 二、配置 从名字就可以看出来它是个过滤器了,所以就要想配置普通过滤器那样配置到web.xml中去了,配置方式如下: &lt;...

HY—Chan 1700

Spring MVC 源码分析

1. DisptcherServlet介绍 在MVC项目搭建的过程中,我们通常会在webapp下的web.xml文件中添加上一个servelet配置,而且通常我们使用的是DispatcherServlet这个类,如下列代码所示: <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/200

lengyingzhang的博客 2128

Spring MVC源码分析の请求处理流程

在Spring MVC中,请求处理的关键是的doDispatch方法:其中包含了@RequestBody,@ResponseBody等注解的解析,参数的适配,方法拦截器的执行,目标方法的执行,返回值处理,以及异常处理等逻辑。Spring MVC整体请求流程,本篇只简单地进行主要流程的分析,诸如如何解析参数,解析返回值,渲染视图等不在此列。客户端请求↓↓↓HandlerExecutionChain(包含Handler和拦截器)↓调用拦截器 preHandle()↓。

西岭千秋雪的博客 1063

spring mvc源码分析之请求分发

Spring MVC原理。Spring MVC源码分析。Spring MVC源码详解

scientificCommunity的博客 426
上一篇: 关于Spring Cloud的思考和总结
下一篇: Redis总结
Art&Code
博客等级 码龄7年 77粉丝 554原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值