美文网首页程序员
深度分析!深入浅出总结手撕SpringMVC要点,建议收藏

深度分析!深入浅出总结手撕SpringMVC要点,建议收藏

作者: 996小迁 | 来源:发表于2021-01-09 15:57 被阅读0次

    SpringMVC

    你对SpringMVC了解多少呢?SpringMVC可以说得上是当前最优秀的MVC框架,采用了松散耦合可插拔组件结构,比其他MVC框架更具扩展性和灵活性;为了提高框架的扩展性和灵活性,设计了松耦合可插拔的组件。理解SpringMVC的原理,在面试或工作中都十分的重要。

    SpringMVC的原理在网络上到处都可以找得到,但是写得都很概括、零散;对应阅读源码经验较少的小伙伴来说,自己去看源码被很多细节所干扰阻碍,不能够很好地抽离出springMVC原理的主线。

    下面就给大家分享一下LZ整理的有关SpringMVC的学习资料,学会用代码说话!

    1、创建过程与文件目录

    1.1、创建Maven工程

    深度分析!深入浅出总结手撕SpringMVC要点,建议收藏 深度分析!深入浅出总结手撕SpringMVC要点,建议收藏 深度分析!深入浅出总结手撕SpringMVC要点,建议收藏 深度分析!深入浅出总结手撕SpringMVC要点,建议收藏

    2、pom依赖与配置文件

    2.1、pom依赖

    <?xml version="1.0" encoding="UTF-8"?>
    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.zhz</groupId>
        <artifactId>simulation-springmvc</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>war</packaging>
    
        <name>simulation-springmvc Maven Webapp</name>
        <!-- FIXME change it to the project's website -->
        <url>http://www.example.com</url>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <maven.compiler.source>1.7</maven.compiler.source>
            <maven.compiler.target>1.7</maven.compiler.target>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.11</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.1.0</version>
                <scope>provided</scope>
            </dependency>
    
            <dependency>
                <groupId>dom4j</groupId>
                <artifactId>dom4j</artifactId>
                <version>1.6.1</version>
            </dependency>
    
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
                <version>3.5</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.12</version>
            </dependency>
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-databind</artifactId>
                <version>2.9.1</version>
            </dependency>
        </dependencies>
    
        <build>
            <finalName>simulation-springmvc</finalName>
            <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
                <plugins>
                    <plugin>
                        <artifactId>maven-clean-plugin</artifactId>
                        <version>3.1.0</version>
                    </plugin>
                    <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
                    <plugin>
                        <artifactId>maven-resources-plugin</artifactId>
                        <version>3.0.2</version>
                    </plugin>
                    <plugin>
                        <artifactId>maven-compiler-plugin</artifactId>
                        <version>3.8.0</version>
                    </plugin>
                    <plugin>
                        <artifactId>maven-surefire-plugin</artifactId>
                        <version>2.22.1</version>
                    </plugin>
                    <plugin>
                        <artifactId>maven-war-plugin</artifactId>
                        <version>3.2.2</version>
                    </plugin>
                    <plugin>
                        <artifactId>maven-install-plugin</artifactId>
                        <version>2.5.2</version>
                    </plugin>
                    <plugin>
                        <artifactId>maven-deploy-plugin</artifactId>
                        <version>2.8.2</version>
                    </plugin>
                </plugins>
            </pluginManagement>
        </build>
    </project>
    

    2.2、springmvc配置类

    <?xml version="1.0" encoding="UTF-8" ?>
    <beans>
        <!--配置创建容器时要扫描的包-->
        <component-scan base-package="com.zhz.controller,com.zhz.service"></component-scan>
    </beans>
    

    2.3、web.xml

    <!DOCTYPE web-app PUBLIC
            "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
            "http://java.sun.com/dtd/web-app_2_3.dtd" >
    
    <web-app>
      <display-name>Archetype Created Web Application</display-name>
    
      <!--配置前端控制器-->
      <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>com.zhz.springmvc.servlet.DispatcherServlet</servlet-class>
        <init-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <!--Web服务器一旦启动,Servlet就会实例化创建对象,然后初始化(预备创建对象)-->
        <load-on-startup>1</load-on-startup>
      </servlet>
    
      <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
      </servlet-mapping>
    </web-app>
    

    3、实体类(User)

    实体类:User.java

    package com.zhz.bean;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    /**
     * @author :zhz
     * @date :Created in 2021/01/04
     * @version: V1.0
     * @slogan: 天下风云出我辈,一入代码岁月催
     * @description: 实体类
     **/
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User {
       private Integer id;
       private String name;
       private String password;
    }
    

    4、控制器(UserController)

    package com.zhz.controller;
    
    import com.zhz.bean.User;
    import com.zhz.service.UserService;
    import com.zhz.springmvc.annotation.Autowired;
    import com.zhz.springmvc.annotation.Controller;
    import com.zhz.springmvc.annotation.RequestMapping;
    import com.zhz.springmvc.annotation.ResponseBody;
    
    /**
     * @author :zhz
     * @date :Created in 2021/01/03
     * @version: V1.0
     * @slogan: 天下风云出我辈,一入代码岁月催
     * @description: 控制器
     **/
    @Controller
    public class UserController {
    
        @Autowired(value = "userService")
        private UserService userService;
    
        @RequestMapping("/listUsers")
        public String listUsers(){
            userService.listUsers();
            return "forward:/success.jsp";
        }
    
        @RequestMapping("/getData")
        @ResponseBody  //返回json格式的数据
        public User getData(){
            //调用服务层
            return userService.getUser();
        }
    }
    

    5、业务处理类与实现类(UserService,UserServiceImpl)

    package com.zhz.service;
    
    import com.zhz.bean.User;
    
    /**
     * @author :zhz
     * @date :Created in 2021/01/03
     * @version: V1.0
     * @slogan: 天下风云出我辈,一入代码岁月催
     * @description:
     **/
    public interface UserService {
        void listUsers();
    
        User getUser();
    }
    
    package com.zhz.service.impl;
    
    import com.zhz.bean.User;
    import com.zhz.service.UserService;
    import com.zhz.springmvc.annotation.Service;
    
    /**
     * @author :zhz
     * @date :Created in 2021/01/03
     * @version: V1.0
     * @slogan: 天下风云出我辈,一入代码岁月催
     * @description:
     **/
    @Service("userService")
    public class UserServiceImpl implements UserService {
    
        @Override
        public void listUsers() {
            System.out.println("===调用===UserServiceImpl===listUser===");
        }
    
        @Override
        public User getUser() {
            return new User(1,"zhz","123456");
        }
    }
    

    6、手撕mvc具体代码

    6.1、核心annotation注解

    package com.zhz.springmvc.annotation;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * @Retention注解表示Annotation的保留策略 RetentionPolicy.Class:运行时不保留,不可以通过反射读取。
     * RetentionPolicy.RUNTIME:运行是保留,可以通过反射读取。
     * RetentionPolicy.SOURCE:丢弃。
     */
    @Target(value = ElementType.FIELD)  //作用在属性上
    @Retention(value = RetentionPolicy.RUNTIME)
    public @interface Autowired {
        String value();
    }
    
    package com.zhz.springmvc.annotation;
    
    import java.lang.annotation.*;
    
    @Target(ElementType.TYPE)  //  //作用在类上
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Controller {
    
        String value() default "";
    }
    
    package com.zhz.springmvc.annotation;
    
    import java.lang.annotation.*;
    
    @Target(ElementType.METHOD)    //作用在方法上
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface RequestMapping {
    
        String value() default "";
    }
    
    package com.zhz.springmvc.annotation;
    
    import java.lang.annotation.*;
    
    /**
     * @BelongsProject: SpringMvc
     */
    @Target(ElementType.METHOD)    //作用在方法上
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface ResponseBody {
    }
    
    package com.zhz.springmvc.annotation;
    
    import java.lang.annotation.*;
    
    /**
     * @Description: 自定义注解
     */
    @Target(ElementType.TYPE)  //作用在类上
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Service {
    
        String value();
    }
    

    6.2、context上下文(SpringMVC容器)

    package com.zhz.springmvc.context;
    
    import com.zhz.springmvc.annotation.Autowired;
    import com.zhz.springmvc.annotation.Controller;
    import com.zhz.springmvc.annotation.Service;
    import com.zhz.springmvc.xml.XmlParse;
    
    import java.io.File;
    import java.lang.reflect.Field;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    /**
     * @author :zhz
     * @date :Created in 2021/01/03
     * @version: V1.0
     * @slogan: 天下风云出我辈,一入代码岁月催
     * @description: SpringMVC容器
     **/
    public class WebApplicationContext {
    
        //classpath:springmvc.xml
        String contextConfigLocation;
    
        //定义集合  用于存放 bean 的权限名|包名.类名
        List<String> classNameList = new ArrayList<>();
    
        //创建Map集合用于扮演IOC容器:  key存放bean的名字   value存放bean实例
        public Map<String, Object> iocMap = new ConcurrentHashMap<>();
    
        public WebApplicationContext() {
        }
    
        public WebApplicationContext(String contextConfigLocation) {
            this.contextConfigLocation = contextConfigLocation;
        }
    
        /**
         * 初始化Spring容器
         */
        public void onRefresh() {
            //1、进行解析spring mvc配置文件操作  ==》 com.zhz.controller,com.zhz.service
            String basePackage = XmlParse.getBasePackage(contextConfigLocation.split(":")[1]);
            //通过","来分割com.zhz.controller,com.zhz.service 获得对应的包名
            String[] packs = basePackage.split(",");
            //2、进行包扫描
            for (String pack : packs) {
                executeScanPackage(pack);
            }
            //3、实例化容器中的bean
            executeInstance();
    
            //4、进行自动注入操作
            executeAutowired();
        }
    
        /**
         * 进行包扫描
         *
         * @param pack
         */
        private void executeScanPackage(String pack) {
            //1、把com.zhz.controller====>com/zhz/controller   com.zhz.service====>com/zhz/controller
            URL url = this.getClass().getClassLoader().getResource("/" + pack.replaceAll("\\.", "/"));
            String path = url.getFile();
            //2、/com/zhz/service
            File dir = new File(path);
            for (File file : dir.listFiles()) {
                if (file.isDirectory()) {//说明是com.zhz.service.impl层
                    executeScanPackage(pack + "." + file.getName());
                } else {
                    //文件目录下文件  获取全路径   UserController.class  ==> com.zhz.controller.UserController
                    String className = pack + "." + file.getName().replaceAll(".class", "");
                    classNameList.add(className);
                }
            }
    
        }
    
        /**
         * 实例化容器
         */
        private void executeInstance() {
            try {
                // com.zhz.controller.UserController      com.zhz.service.impl.UserServiceImpl
                for (String className : classNameList) {
                    Class<?> clazz = Class.forName(className);
                    if (clazz.isAnnotationPresent(Controller.class)) {
                        //控制层的bean,得到类的简写名称也就是UserController
                        String beanName = clazz.getSimpleName().substring(0, 1).toLowerCase()+clazz.getSimpleName().substring(1);//首位变为小写
                        iocMap.put(beanName, clazz.newInstance());
                    } else if (clazz.isAnnotationPresent(Service.class)) {
                        //Service层,主要是为了获得他的value
                        Service service = clazz.getAnnotation(Service.class);
                        String beanName = service.value();
                        iocMap.put(beanName, clazz.newInstance());
                    }
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 进行自动注入操作
         */
        private void executeAutowired() {
            try {
                //从容器中取出bean,然后判断bean中是否有属性上使用Autowired,如果使用了该注解,就需要进行自动注入操作
                for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
                    //取出容器中的bean
                    Object bean = entry.getValue();
                    //从bean中获取属性
                    Field[] fields = bean.getClass().getDeclaredFields();
                    for (Field field : fields) {
                        if (field.isAnnotationPresent(Autowired.class)) {
                            //获取注解中的value值,该值是bean的name
                            Autowired autowired = field.getAnnotation(Autowired.class);
                            String beanName = autowired.value();
                            ;
                            //取消检查机制
                            field.setAccessible(true);
                            field.set(bean, iocMap.get(beanName));
                        }
                    }
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
    

    6.3、映射处理(url与ccontroller之间的映射)

    package com.zhz.springmvc.handler;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    import java.lang.reflect.Method;
    
    /**
     * @author :zhz
     * @date :Created in 2021/01/04
     * @version: V1.0
     * @slogan: 天下风云出我辈,一入代码岁月催
     * @description:
     **/
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class HandlerMapping {
    
        //请求URL地址
        private String url;
        //控制器
        private Object controller;
        //控制器的方法
        private Method method;
    }
    

    6.4、前端处理器

    package com.zhz.springmvc.servlet;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.zhz.springmvc.annotation.Controller;
    import com.zhz.springmvc.annotation.RequestMapping;
    import com.zhz.springmvc.annotation.ResponseBody;
    import com.zhz.springmvc.context.WebApplicationContext;
    import com.zhz.springmvc.handler.HandlerMapping;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    
    /**
     * @author :zhz
     * @date :Created in 2021/01/03
     * @version: V1.0
     * @slogan: 天下风云出我辈,一入代码岁月催
     * @description: 前端控制器
     **/
    public class DispatcherServlet extends HttpServlet {
    
        //指定SpringMvc容器
        private WebApplicationContext webApplicationContext;
        //创建集合,用于存放映射关系、映射地址与控制器.方法,用于发送请求直接从该集合中进行匹配
        List<HandlerMapping> handList = new ArrayList<>();
    
        @Override
        public void init() throws ServletException {
            //1、从web.xml中获得加载初始化参数contextConfigLocation的值classpath:springmvc.xml
            String configLocation = this.getServletConfig().getInitParameter("contextConfigLocation");
            //2、创建SpringMVC容器
            webApplicationContext = new WebApplicationContext(configLocation);
    
            //3、进行初始化操作
            webApplicationContext.onRefresh();
    
            //4、初始化请求映射关系   /findUser   ===》控制器.方法
            initHandlerMapping();
        }
    
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //进行请求分发处理
            doDispatcher(request, response);
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request, response);
        }
    
        /**
         * 初始化请求映射关系(获取链接地址0
         */
        private void initHandlerMapping() {
            //遍历map, key存放bean的名字   value存放bean实例
            for (Map.Entry<String, Object> entry : webApplicationContext.iocMap.entrySet()) {
                //获得bean的class类型
                Class<?> clazz = entry.getValue().getClass();
                if (clazz.isAnnotationPresent(Controller.class)) {
                    //获取bean中所有的方法,为这些方法建立映射关系
                    Method[] methods = clazz.getDeclaredMethods();
                    for (Method method : methods) {
                        if (method.isAnnotationPresent(RequestMapping.class)) {
                            RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                            //获取注解中的值
                            String url = requestMapping.value();
                            //建立映射地址,与控制器 方法
                            HandlerMapping handlerMapping = new HandlerMapping(url, entry.getValue(), method);
                            handList.add(handlerMapping);
                        }
                    }
                }
            }
        }
    
        /**
         * 进行请求分发处理
         *
         * @param request
         * @param response
         */
        private void doDispatcher(HttpServletRequest request, HttpServletResponse response) {
            try {
                //根据用户的请求地址(/listUsers)查找Controller
                HandlerMapping handlerMapping = getHandler(request);
    
                if (handlerMapping == null) {
                    response.getWriter().print("<h1>404 NOT  FOUND!</h1>");
                } else {
                    //调用处理方法之前 进行参数的注入
    
                    //调用目标方法---》获得方法的返回值类型
                    Object result = handlerMapping.getMethod().invoke(handlerMapping.getController());
                    if (result instanceof String){
                        //跳转到JSP中
                        String viewName = (String)result;
                        //forward:/success.jsp重定向
                        if (viewName.contains(":")){
                            String viewType=viewName.split(":")[0];//也就是forward或者redirect
                            String viewPage=viewName.split(":")[1];//跳转的页面
                            if (viewType.equals("forward")){//请求转发
                                request.getRequestDispatcher(viewPage).forward(request,response);
                            }else{//重定向
                                response.sendRedirect(viewPage);
                            }
                        }else{
                            //默认请求转发
                            request.getRequestDispatcher(viewName).forward(request,response);
                        }
                    }else{
                        //返回JSON格式数据
                        Method method=handlerMapping.getMethod();
                        if (method.isAnnotationPresent(ResponseBody.class)){
                            //将返回值转换成 json格式数据
                            ObjectMapper objectMapper = new ObjectMapper();
                            String json = objectMapper.writeValueAsString(result);
                            response.setContentType("text/html;charset=utf-8");
                            PrintWriter writer= response.getWriter();
                            writer.print(json);
                            writer.flush();
                            writer.close();
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (ServletException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 根据用户请求查找对应的Handler======>获取请求对应的handler(也就是从handList中取出)
         *
         * @param request
         * @return
         */
        private HandlerMapping getHandler(HttpServletRequest request) {
            String requestURI = request.getRequestURI();
            for (HandlerMapping handlerMapping : handList) {
                //从容器的Handle取出URL  和  用户的请求地址进行匹配,找到满足条件的Handler(controller)
                if (handlerMapping.getUrl().equals(requestURI)) {
                    return handlerMapping;
                }
            }
            return null;
        }
    
    }
    

    6.5、解析XML

    package com.zhz.springmvc.xml;
    
    import lombok.val;
    import org.dom4j.Attribute;
    import org.dom4j.Document;
    import org.dom4j.DocumentException;
    import org.dom4j.Element;
    import org.dom4j.io.SAXReader;
    
    import java.io.File;
    import java.io.InputStream;
    
    /**
     * @author :zhz
     * @date :Created in 2021/01/04
     * @version: V1.0
     * @slogan: 天下风云出我辈,一入代码岁月催
     * @description: 解析spring mvc.xml
     **/
    public class XmlParse {
    
        public static String getBasePackage(String xml){
            try {
                SAXReader saxReader=new SAXReader();
                // 通过reader对象的read方法加载spring mvc.xml文件,获取docuemnt对象。
                InputStream inputStream = XmlParse.class.getClassLoader().getResourceAsStream(xml);
                Document document = saxReader.read(inputStream);
                // 通过document对象获取根节点beans
                Element rootElement = document.getRootElement();
                // 通过element对象的返回给定本地名称和任何名称空间的第一个元素
                Element componentScan = rootElement.element("component-scan");
                //返回componentScan的参数
                Attribute attribute = componentScan.attribute("base-package");
                //返回base-package的值
                String basePackage = attribute.getText();
                return basePackage;
            } catch (DocumentException e) {
                e.printStackTrace();
            }
            return "";
        }
    }
    

    7、前端页面

    7.1、index.jsp

    <html>
    <body>
    <h2>Hello World!</h2>
    </body>
    </html>
    

    7.2、succes.jsp

    <html>
    <body>
    <h2>Hello World!</h2>
       跳转至success.jsp页面
    </body>
    </html>
    

    总结

    这篇关于手撕springMVC的文章就先更到这里了,感谢你看到这里,文章有什么不足还请指正,觉得文章对你有帮助的话记得给我点个赞,SpringMVC其实并不算是一个很难的知识点,用点心多看就很简单

    每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!

    相关文章

      网友评论

        本文标题:深度分析!深入浅出总结手撕SpringMVC要点,建议收藏

        本文链接:https://www.haomeiwen.com/subject/cmvcaktx.html