深度分析!深刻淺出總結一下手撕springMVC的要點,建議收藏起來慢慢看!

一、建立過程與文件目錄

1.一、建立Maven工程

二、pom依賴與配置文件

2.一、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.二、springmvc配置類

<?xml version="1.0" encoding="UTF-8" ?>
<beans>
    <!--配置建立容器時要掃描的包-->
    <component-scan base-package="com.zhz.controller,com.zhz.service"></component-scan>
</beans>複製代碼

2.三、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>複製代碼
三、實體類(User)

實體類:User.javahtml

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;
}複製代碼
四、控制器(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();
    }
}複製代碼
五、業務處理類與實現類(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");
    }
}複製代碼
六、手撕mvc具體代碼

6.一、核心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.二、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() {
        //一、進行解析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(",");
        //二、進行包掃描
        for (String pack : packs) {
            executeScanPackage(pack);
        }
        //三、實例化容器中的bean
        executeInstance();

        //四、進行自動注入操做
        executeAutowired();
    }

    /**
     * 進行包掃描
     *
     * @param pack
     */
    private void executeScanPackage(String pack) {
        //一、把com.zhz.controller====>com/zhz/controller   com.zhz.service====>com/zhz/controller
        URL url = this.getClass().getClassLoader().getResource("/" + pack.replaceAll("\\.", "/"));
        String path = url.getFile();
        //二、/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.三、映射處理(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.四、前端處理器

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 {
        //一、從web.xml中得到加載初始化參數contextConfigLocation的值classpath:springmvc.xml
        String configLocation = this.getServletConfig().getInitParameter("contextConfigLocation");
        //二、建立SpringMVC容器
        webApplicationContext = new WebApplicationContext(configLocation);

        //三、進行初始化操做
        webApplicationContext.onRefresh();

        //四、初始化請求映射關係   /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.五、解析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.一、index.jsp

<html>
<body>
<h2>Hello World!</h2>
</body>
</html>複製代碼

7.二、succes.jsp

<html>
<body>
<h2>Hello World!</h2>
   跳轉至success.jsp頁面
</body>
</html>複製代碼

總結

這篇關於手撕springMVC的文章就先更到這裏了,感謝你看到這裏,文章有什麼不足還請指正,以爲文章對你有幫助的話記得給我點個贊,天天都會分享java相關技術文章或行業資訊,歡迎你們關注和轉發文章!前端

相關文章
相關標籤/搜索