第12章 手写Spring MVC
第十二章 手写Spring MVC
12.1 基本结构搭建
12.1.1 创建Maven模块
12.1.2 引入Servlet依赖
<?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>org.springmvc</groupId><artifactId>myspringmvc</artifactId><version>1.0-SNAPSHOT</version><packaging>war</packaging><dependencies><!--servlet api--><dependency><groupId>jakarta.servlet</groupId><artifactId>jakarta.servlet-api</artifactId><version>6.0.0</version><scope>provided</scope></dependency></dependencies><properties><maven.compiler.source>21</maven.compiler.source><maven.compiler.target>21</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties></project>
12.1.3 配置Tomcat服务器
12.1.4 添加web支持
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"version="6.0">
</web-app>
12.1.5 创建基本类和接口
根据Spring MVC执行流程,目前先创建出以下的类和接口,后期如果需要其他的再添加:
12.2 部分类和接口的代码完善
12.2.1 @Controller注解
package org.myspringmvc.stereotype;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** ClassName: Controller* Description: 用来标注处理器,被标注的处理器,纳入IoC容器的管理。该注解只允许出现在类上,另外可以被反射机制读取。* Datetime: 2024/4/2 9:01* Author: sqnugy* Version: 1.0*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}
12.2.2 RequestMethod枚举(新建)
package org.myspringmvc.web.bind.annotation;/*** ClassName: RequestMethod* Description: 请求方式枚举* Datetime: 2024/4/2 10:35* Author: sqnugy* Version: 1.0*/
public enum RequestMethod {GET, POST
}
12.2.3 @RequestMapping注解
package org.myspringmvc.web.bind.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** ClassName: RequestMapping* Description: 用来标注处理器方法,允许标注方法和类,可以被反射机制读取。* Datetime: 2024/4/2 8:59* Author: sqnugy* Version: 1.0*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {/*** 用来指定请求路径* @return*/String[] value();/*** 用来指定请求方式* @return*/RequestMethod method();
}
12.2.4 HandlerMethod
package org.myspringmvc.web.method;import java.lang.reflect.Method;/*** ClassName: HandlerMethod* Description: 处理器方法* Datetime: 2024/4/2 8:53* Author: sqnugy* Version: 1.0*/
public class HandlerMethod {/*** 处理器对象*/private Object handler;/*** 要执行的方法*/private Method method;public HandlerMethod() {}public HandlerMethod(Object handler, Method method) {this.handler = handler;this.method = method;}public Object getHandler() {return handler;}public void setHandler(Object handler) {this.handler = handler;}public Method getMethod() {return method;}public void setMethod(Method method) {this.method = method;}
}
12.2.5 HandlerMapping接口
package org.myspringmvc.web.servlet;import jakarta.servlet.http.HttpServletRequest;/*** ClassName: HandlerMapping* Description: 主要是通过请求获取对应的处理器执行链。* Datetime: 2024/4/2 8:50* Author: sqnugy* Version: 1.0*/
public interface HandlerMapping {/*** 根据请求获取处理器执行链。* @param request* @return* @throws Exception*/HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
12.2.6 RequestMappingHandlerMapping
package org.myspringmvc.web.servlet.mvc.method.annotation;import jakarta.servlet.http.HttpServletRequest;
import org.myspringmvc.web.servlet.HandlerExecutionChain;
import org.myspringmvc.web.servlet.HandlerMapping;/*** ClassName: RequestMappingHandlerMapping* Description:* Datetime: 2024/4/2 9:44* Author: sqnugy* Version: 1.0*/
public class RequestMappingHandlerMapping implements HandlerMapping {@Overridepublic HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {return null;}
}
12.2.7 HandlerAdapter接口
package org.myspringmvc.web.servlet;import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;/*** ClassName: HandlerAdapter* Description: 通过处理器适配器调用处理器方法* Datetime: 2024/4/2 8:51* Author: sqnugy* Version: 1.0*/
public interface HandlerAdapter {/*** 执行处理器方法* @param request* @param response* @param handler* @return* @throws Exception*/ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}
12.2.8 RequestMappingHandlerAdapter
package org.myspringmvc.web.servlet.mvc.method.annotation;import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.myspringmvc.web.servlet.HandlerAdapter;
import org.myspringmvc.web.servlet.ModelAndView;/*** ClassName: RequestMappingHandlerAdapter* Description:* Datetime: 2024/4/2 9:44* Author: sqnugy* Version: 1.0*/
public class RequestMappingHandlerAdapter implements HandlerAdapter {@Overridepublic ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {return null;}
}
12.2.9 View接口
package org.myspringmvc.web.servlet;import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;import java.util.Map;/*** ClassName: View* Description:* Datetime: 2024/4/2 8:58* Author: sqnugy* Version: 1.0*/
public interface View {/*** 获取响应的内容类型* @return*/String getContentType();/*** 渲染* @param model* @param request* @param response* @throws Exception*/void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)throws Exception;
}
)
12.2.10 InternalResourceView
package org.myspringmvc.web.servlet.view;import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.myspringmvc.web.servlet.View;import java.util.Map;/*** ClassName: InternalResourceView* Description:* Datetime: 2024/4/2 10:17* Author: sqnugy* Version: 1.0*/
public class InternalResourceView implements View {@Overridepublic String getContentType() {return null;}@Overridepublic void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {}
}
12.2.11 ViewResolver接口
package org.myspringmvc.web.servlet;import java.util.Locale;/*** ClassName: ViewResolver* Description:解析逻辑视图名称,返回视图对象* Datetime: 2024/4/2 8:58* Author: sqnugy* Version: 1.0*/
public interface ViewResolver {/*** 解析逻辑视图名称,返回视图对象* @param viewName* @param locale* @return* @throws Exception*/View resolveViewName(String viewName, Locale locale) throws Exception;
}
12.2.12 InternalResourceViewResolver
package org.myspringmvc.web.servlet.view;import org.myspringmvc.web.servlet.View;
import org.myspringmvc.web.servlet.ViewResolver;import java.util.Locale;/*** ClassName: InternalResourceViewResolver* Description:* Datetime: 2024/4/2 9:45* Author: sqnugy* Version: 1.0*/
public class InternalResourceViewResolver implements ViewResolver {@Overridepublic View resolveViewName(String viewName, Locale locale) throws Exception {return null;}
}
12.2.13 DispatcherServlet
package org.myspringmvc.web.servlet;import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;/*** ClassName: DispatcherServlet* Description:* Datetime: 2024/4/2 8:50* Author: sqnugy* Version: 1.0*/
public class DispatcherServlet extends HttpServlet {@Overridepublic void init() throws ServletException {}@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doDispatch(req, resp);}/*** 处理请求的核心方法* @param request* @param response* @throws ServletException* @throws IOException*/private void doDispatch(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}
}
12.2.14 HandlerExecutionChain
package org.myspringmvc.web.servlet;import java.util.List;/*** ClassName: HandlerExecutionChain* Description:* Datetime: 2024/4/2 8:55* Author: sqnugy* Version: 1.0*/
public class HandlerExecutionChain {private Object handler;private List<HandlerInterceptor> interceptorList;private int interceptorIndex = -1;public HandlerExecutionChain() {}public HandlerExecutionChain(Object handler, List<HandlerInterceptor> interceptorList) {this.handler = handler;this.interceptorList = interceptorList;}public Object getHandler() {return handler;}public void setHandler(Object handler) {this.handler = handler;}public List<HandlerInterceptor> getInterceptorList() {return interceptorList;}public void setInterceptorList(List<HandlerInterceptor> interceptorList) {this.interceptorList = interceptorList;}public int getInterceptorIndex() {return interceptorIndex;}public void setInterceptorIndex(int interceptorIndex) {this.interceptorIndex = interceptorIndex;}
}
12.2.15 HandlerInterceptor拦截器接口
package org.myspringmvc.web.servlet;import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;/*** ClassName: HandlerInterceptor* Description: 拦截器接口* Datetime: 2024/4/2 8:54* Author: sqnugy* Version: 1.0*/
public interface HandlerInterceptor {default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {return true;}default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
}
12.2.16 ModelMap类(新建)
package org.myspringmvc.ui;import java.util.LinkedHashMap;/*** ClassName: ModelMap* Description: 将数据存储到域中。* Datetime: 2024/4/2 11:07* Author: sqnugy* Version: 1.0*/
public class ModelMap extends LinkedHashMap<String, Object> {public ModelMap() {}public ModelMap addAttribute(String name, String value){this.put(name, value);return this;}
}
12.2.17 ModelAndView
package org.myspringmvc.web.servlet;import org.myspringmvc.ui.ModelMap;/*** ClassName: ModelAndView* Description:* Datetime: 2024/4/2 8:57* Author: sqnugy* Version: 1.0*/
public class ModelAndView {private Object view;private ModelMap model;public ModelAndView() {}public ModelAndView(Object view, ModelMap model) {this.view = view;this.model = model;}public Object getView() {return view;}public void setView(Object view) {this.view = view;}/*** 该方法待实现* @param viewName*/public void setViewName(String viewName){// TODO}public ModelMap getModel() {return model;}public void setModel(ModelMap model) {this.model = model;}
}
12.3 webapp开发者写应用
12.3.1 web.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"version="6.0"><!--配置前端控制器--><servlet><servlet-name>springmvc</servlet-name><servlet-class>org.myspringmvc.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:springmvc.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>springmvc</servlet-name><url-pattern>/</url-pattern></servlet-mapping></web-app>
DispatcherServlet
的 <init-param>
的 contextConfigLocation
可以编写代码了:
@Override
public void init() throws ServletException {ServletConfig servletConfig = this.getServletConfig();String contextConfigLocation = servletConfig.getInitParameter(Constant.CONTEXT_CONFIG_LOCATION);String springMvcXmlPath = getSpringMvcXmlPath(contextConfigLocation);System.out.println("Spring MVC配置文件路径解析完成:" + springMvcXmlPath);
}private String getSpringMvcXmlPath(String contextConfigLocation) throws UnsupportedEncodingException {if(contextConfigLocation.startsWith(Constant.CLASSPATH)){String path = contextConfigLocation.substring(Constant.CLASSPATH.length()).trim();String springMvcXmlPath = Thread.currentThread().getContextClassLoader().getResource(path).getPath();// 对路径解码,防止路径中有 % 等字符。return URLDecoder.decode(springMvcXmlPath, Charset.defaultCharset());}return null;
}
定义系统常量类:Constant
package org.myspringmvc.web.constant;/*** ClassName: Constant* Description:SpringMVC系统常量类* Datetime: 2024/4/2 11:28* Author: sqnugy* Version: 1.0*/
public class Constant {public static final String CONTEXT_CONFIG_LOCATION = "contextConfigLocation";public static final String CLASSPATH = "classpath:";
}
12.3.2 编写处理器Controller
package com.powernode.springmvc.controller;import org.myspringmvc.stereotype.Controller;
import org.myspringmvc.web.bind.annotation.RequestMapping;
import org.myspringmvc.web.bind.annotation.RequestMethod;/*** ClassName: UserController* Description:* Datetime: 2024/4/2 11:38* Author: sqnugy* Version: 1.0*/
@Controller
public class UserController {@RequestMapping(value = "/", method = RequestMethod.GET)public String index(){return "index";}
}
12.3.3 编写拦截器
package com.powernode.springmvc.interceptors;import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.myspringmvc.web.servlet.HandlerInterceptor;
import org.myspringmvc.web.servlet.ModelAndView;/*** ClassName: Interceptor1* Description:* Datetime: 2024/4/2 11:40* Author: sqnugy* Version: 1.0*/
public class Interceptor1 implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("Interceptor1's preHandle");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("Interceptor1's postHandle");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("Interceptor1's afterCompletion");}
}
package com.powernode.springmvc.interceptors;import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.myspringmvc.web.servlet.HandlerInterceptor;
import org.myspringmvc.web.servlet.ModelAndView;/*** ClassName: Interceptor2* Description:* Datetime: 2024/4/2 11:41* Author: sqnugy* Version: 1.0*/
public class Interceptor2 implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("Interceptor2's preHandle");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("Interceptor2's postHandle");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("Interceptor2's afterCompletion");}
}
12.3.4 编写springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans><!--组件扫描--><component-scan base-package="com.powernode.springmvc.controller"/><!--视图解析器--><bean class="org.myspringmvc.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/jsp/"/><property name="suffix" value=".jsp"/></bean><!--拦截器--><interceptors><bean class="com.powernode.springmvc.interceptors.Interceptor1"/><bean class="com.powernode.springmvc.interceptors.Interceptor2"/></interceptors>
</beans>
InternalResourceViewResolver
类中添加属性:suffix和prefix
package org.myspringmvc.web.servlet.view;import org.myspringmvc.web.servlet.View;
import org.myspringmvc.web.servlet.ViewResolver;import java.util.Locale;/*** ClassName: InternalResourceViewResolver* Description:* Datetime: 2024/4/2 9:45* Author: sqnugy* Version: 1.0*/
public class InternalResourceViewResolver implements ViewResolver {private String suffix;private String prefix;public String getSuffix() {return suffix;}public void setSuffix(String suffix) {this.suffix = suffix;}public String getPrefix() {return prefix;}public void setPrefix(String prefix) {this.prefix = prefix;}@Overridepublic View resolveViewName(String viewName, Locale locale) throws Exception {return null;}
}
12.3.5 提供视图
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>index jsp</title>
</head>
<body>
<h1>动力节点:手写Spring MVC框架</h1>
</body>
</html>
12.4 服务器启动阶段的处理
12.4.1 分析服务器启动阶段都需要初始化什么
- 初始化Spring容器
- 组件扫描包下的类纳入IoC容器的管理。
- 创建视图解析器对象
- 创建所有的拦截器对象
- 扫描这个包下所有的类:
org.myspringmvc.web.servlet.mvc.method.annotation
,全部实例化,纳入IoC容器管理
- 初始化
HandlerMapping
- 初始化
HandlerAdapter
- 初始化
ViewResolver
12.4.2 初始化Spring容器
Spring容器:ApplicationContext
Spring Web容器:WebApplicationContext
12.4.2.1 组件扫描
添加解析xml文件的依赖
<!--dom4j-->
<dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.6.1</version>
</dependency>
<!--jaxen-->
<dependency><groupId>jaxen</groupId><artifactId>jaxen</artifactId><version>1.1.6</version>
</dependency>
package org.myspringmvc.context;import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;import java.io.File;
import java.lang.reflect.Constructor;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;/*** ClassName: ApplicationContext* Description: Spring容器,启动服务器时,初始化* Datetime: 2024/4/2 13:52* Author: sqnugy* Version: 1.0*/
public class ApplicationContext {private Map<String, Object> beanMap = new HashMap<>();public ApplicationContext(String xmlPath) throws Exception {// 组件扫描SAXReader saxReader = new SAXReader();Document document = saxReader.read(new File(xmlPath));Element componentScanElement = (Element)document.selectSingleNode("/beans/context:component-scan");String basePackage = componentScanElement.attributeValue("base-package");System.out.println("组件扫描:" + basePackage);componentScan(basePackage);System.out.println("Spring Web容器当下状态:" + beanMap);}private void componentScan(String basePackage) throws Exception{String dirPath = Thread.currentThread().getContextClassLoader().getResource(basePackage.replace(".", "/")).getPath();File file = new File(URLDecoder.decode(dirPath));if(file.isDirectory()){File[] files = file.listFiles();for (File classFile : files){if(classFile.getName().endsWith(".class")){String className = basePackage + "." + classFile.getName().substring(0, classFile.getName().lastIndexOf("."));Class<?> clazz = Class.forName(className);Constructor<?> defaultCon = clazz.getDeclaredConstructor();Object bean = defaultCon.newInstance();beanMap.put(firstCharLowerCase(clazz.getSimpleName()), bean);}}}}private String firstCharLowerCase(String simpleName) {return simpleName.substring(0, 1).toLowerCase() + simpleName.substring(1);}public Object getBean(String beanName){return beanMap.get(beanName);}
}
package org.myspringmvc.context;import jakarta.servlet.ServletContext;/*** ClassName: WebApplicationContext* Description:* Datetime: 2024/4/2 14:24* Author: sqnugy* Version: 1.0*/
public class WebApplicationContext extends ApplicationContext{private ServletContext servletContext;public WebApplicationContext(String xmlPath, ServletContext servletContext) throws Exception {super(xmlPath);this.servletContext = servletContext;}public ServletContext getServletContext() {return servletContext;}
}
在DispatcherServlet
中添加如下代码:
添加常量值:
启动服务器测试:
12.4.2.2 创建视图解析器对象
InternalResourceViewResolver
类代码改动,添加prefix和suffix属性:
package org.myspringmvc.web.servlet.view;import org.myspringmvc.web.servlet.View;
import org.myspringmvc.web.servlet.ViewResolver;import java.util.Locale;/*** ClassName: InternalResourceViewResolver* Description:* Datetime: 2024/4/2 9:45* Author: sqnugy* Version: 1.0*/
public class InternalResourceViewResolver implements ViewResolver {private String suffix;private String prefix;public String getSuffix() {return suffix;}public void setSuffix(String suffix) {this.suffix = suffix;}public String getPrefix() {return prefix;}public void setPrefix(String prefix) {this.prefix = prefix;}@Overridepublic View resolveViewName(String viewName, Locale locale) throws Exception {return null;}
}
// 创建视图解析器对象
Element viewResolverBean = (Element) document.selectSingleNode("/beans/bean");
String viewResolverClassName = viewResolverBean.attributeValue("class");
Class viewResolverClass = Class.forName(viewResolverClassName);
Object viewResolverObj = viewResolverClass.newInstance();
if(viewResolverObj instanceof InternalResourceViewResolver internalResourceViewResolver){// 前缀Element prefixProperty = (Element)viewResolverBean.selectSingleNode("property[@name='prefix']");internalResourceViewResolver.setPrefix(prefixProperty.attributeValue("value"));// 后缀Element suffixProperty = (Element)viewResolverBean.selectSingleNode("property[@name='suffix']");internalResourceViewResolver.setSuffix(suffixProperty.attributeValue("value"));
}
beanMap.put(Constant.VIEW_RESOLVER, viewResolverObj);
System.out.println("Spring Web容器当下状态:" + beanMap);
12.4.2.3 创建所有的拦截器对象
在ApplicationContext
构造方法中继续添加如下代码:
// 创建所有拦截器对象
Element interceptorsElement = (Element) document.selectSingleNode("/beans/interceptors");
List<Element> interceptorBeans = interceptorsElement.elements("bean");
List<HandlerInterceptor> interceptors = new ArrayList<>();
for(Element interceptorBean : interceptorBeans){String className = interceptorBean.attributeValue("class");Class<?> clazz = Class.forName(className);interceptors.add((HandlerInterceptor) clazz.newInstance());
}
beanMap.put(Constant.INTERCEPTORS, interceptors);
System.out.println("Spring Web容器当下状态:" + beanMap);
12.4.2.4 初始化annotation包下所有类的实例
// 将这个包下所有的类实例化:org.myspringmvc.web.servlet.mvc.method.annotation
String dirPath = Thread.currentThread().getContextClassLoader().getResource(Constant.PACKAGE_AUTO_CREATE.replace(".", "/")).getPath();
File file = new File(URLDecoder.decode(dirPath));
if(file.isDirectory()){File[] files = file.listFiles();for (File classFile : files){if(classFile.getName().endsWith(".class")){String className = Constant.PACKAGE_AUTO_CREATE + "." + classFile.getName().substring(0, classFile.getName().lastIndexOf("."));Class<?> clazz = Class.forName(className);Constructor<?> defaultCon = clazz.getDeclaredConstructor();Object bean = defaultCon.newInstance();if(bean instanceof HandlerMapping){beanMap.put(Constant.HANDLER_MAPPING, bean);}if(bean instanceof HandlerAdapter){beanMap.put(Constant.HANDLER_ADAPTER, bean);}}}
}
System.out.println("Spring Web容器当下状态:" + beanMap);
12.4.3 初始化HandlerMapping
12.4.4 初始化HandlerAdapter
12.4.5 初始化ViewResolver
12.5 根据请求流程补充代码
12.5.1 根据请求获取处理器执行链
private void doDispatch(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {try {// 根据请求获取处理器执行链HandlerExecutionChain mappedHandler = handlerMapping.getHandler(request);System.out.println(mappedHandler);} catch (Exception e) {e.printStackTrace();}
}
package org.myspringmvc.web.servlet.mvc.method.annotation;import jakarta.servlet.http.HttpServletRequest;
import org.myspringmvc.context.WebApplicationContext;
import org.myspringmvc.web.constant.Constant;
import org.myspringmvc.web.method.HandlerMethod;
import org.myspringmvc.web.servlet.HandlerExecutionChain;
import org.myspringmvc.web.servlet.HandlerInterceptor;
import org.myspringmvc.web.servlet.HandlerMapping;
import org.myspringmvc.web.servlet.mvc.RequestMappingInfo;import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** ClassName: RequestMappingHandlerMapping* Description:* Datetime: 2024/4/2 9:44* Author: sqnugy* Version: 1.0*/
public class RequestMappingHandlerMapping implements HandlerMapping {private Map<RequestMappingInfo, HandlerMethod> map;public RequestMappingHandlerMapping(Map<RequestMappingInfo, HandlerMethod> map) {this.map = map;}@Overridepublic HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {RequestMappingInfo requestMappingInfo = new RequestMappingInfo(request.getServletPath(), request.getMethod());HandlerExecutionChain handlerExecutionChain = new HandlerExecutionChain();handlerExecutionChain.setHandler(map.get(requestMappingInfo));WebApplicationContext wac = (WebApplicationContext) request.getServletContext().getAttribute(Constant.WEB_APPLICATION_CONTEXT);handlerExecutionChain.setInterceptorList((List<HandlerInterceptor>)wac.getBean(Constant.INTERCEPTORS));return handlerExecutionChain;}
}
private Map<RequestMappingInfo, HandlerMethod> componentScan(String basePackage) throws Exception{// 初始化HandlerMethodMap<RequestMappingInfo, HandlerMethod> handlerMethodMap = new HashMap<>();String dirPath = Thread.currentThread().getContextClassLoader().getResource(basePackage.replace(".", "/")).getPath();File file = new File(URLDecoder.decode(dirPath));if(file.isDirectory()){File[] files = file.listFiles();for (File classFile : files){if(classFile.getName().endsWith(".class")){String className = basePackage + "." + classFile.getName().substring(0, classFile.getName().lastIndexOf("."));Class<?> clazz = Class.forName(className);Constructor<?> defaultCon = clazz.getDeclaredConstructor();Object bean = defaultCon.newInstance();beanMap.put(firstCharLowerCase(clazz.getSimpleName()), bean);// 如果clazz被@Controller注解标注if(clazz.isAnnotationPresent(Controller.class)){// 获取该类中所有的方法Method[] methods = clazz.getDeclaredMethods();for(Method method : methods){if(method.isAnnotationPresent(RequestMapping.class)){RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);// 创建RequestMappingInfo对象RequestMappingInfo requestMappingInfo = new RequestMappingInfo();requestMappingInfo.setRequestURI(requestMapping.value()[0]);requestMappingInfo.setRequestMethod(requestMapping.method().toString());// 创建HandlerMethod对象HandlerMethod handlerMethod = new HandlerMethod();handlerMethod.setMethod(method);handlerMethod.setHandler(bean);handlerMethodMap.put(requestMappingInfo, handlerMethod);}}}}}}return handlerMethodMap;
}
ApplicationContext
代码还有以下改造:
添加一个新的类:RequestMappingInfo
package org.myspringmvc.web.servlet.mvc;import java.util.Objects;/*** ClassName: RequestMappingInfo* Description:* Datetime: 2024/4/2 17:58* Author: sqnugy* Version: 1.0*/
public class RequestMappingInfo {private String requestURI;private String requestMethod;public RequestMappingInfo() {}public RequestMappingInfo(String requestURI, String requestMethod) {this.requestURI = requestURI;this.requestMethod = requestMethod;}public String getRequestURI() {return requestURI;}public void setRequestURI(String requestURI) {this.requestURI = requestURI;}public String getRequestMethod() {return requestMethod;}public void setRequestMethod(String requestMethod) {this.requestMethod = requestMethod;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;RequestMappingInfo that = (RequestMappingInfo) o;return Objects.equals(requestURI, that.requestURI) && Objects.equals(requestMethod, that.requestMethod);}@Overridepublic int hashCode() {return Objects.hash(requestURI, requestMethod);}@Overridepublic String toString() {return "RequestMappingInfo{" +"requestURI='" + requestURI + '\'' +", requestMethod='" + requestMethod + '\'' +'}';}
}
12.5.2 执行拦截器的preHandle
添加以下代码:
HandlerExecutionChain
添加以下代码:
public boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {for (int i = 0; i < interceptorList.size(); i++) {HandlerInterceptor handlerInterceptor = interceptorList.get(i);boolean result = handlerInterceptor.preHandle(request, response, handler);if(!result){return false;}interceptorIndex = i;}return true;
}
12.5.3 执行处理器方法
DispatcherServlet
中的doDispatch
方法:
先让handle方法返回一个固定的ModelAndView
,后期在详细编写 handle 方法:
12.5.4 执行拦截器的postHandle
DispatcherServlet
的 doDispatch
方法中:
HandlerExecutionChain
的方法:
12.5.5 处理响应
在 DispatcherServlet
的 doDispatch
方法中:
package org.myspringmvc.web.servlet.view;import org.myspringmvc.web.servlet.View;
import org.myspringmvc.web.servlet.ViewResolver;import java.util.Locale;/*** ClassName: InternalResourceViewResolver* Description:* Datetime: 2024/4/2 9:45* Author: sqnugy* Version: 1.0*/
public class InternalResourceViewResolver implements ViewResolver {private String suffix;private String prefix;public String getSuffix() {return suffix;}public void setSuffix(String suffix) {this.suffix = suffix;}public String getPrefix() {return prefix;}public void setPrefix(String prefix) {this.prefix = prefix;}@Overridepublic View resolveViewName(String viewName, Locale locale) throws Exception {return new InternalResourceView("text/html;charset=UTF-8", prefix + viewName + suffix);}
}
package org.myspringmvc.web.servlet.view;import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.myspringmvc.web.servlet.View;import java.util.Map;/*** ClassName: InternalResourceView* Description:* Datetime: 2024/4/2 10:17* Author: sqnugy* Version: 1.0*/
public class InternalResourceView implements View {private String contentType;private String path;public InternalResourceView(String contentType, String path) {this.contentType = contentType;this.path = path;}public String getPath() {return path;}public void setPath(String path) {this.path = path;}public void setContentType(String contentType) {this.contentType = contentType;}@Overridepublic String getContentType() {return contentType;}@Overridepublic void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {// 设置响应内容类型response.setContentType(getContentType());// 向request域中绑定数据if(model != null){model.forEach(request::setAttribute); }// 转发request.getRequestDispatcher(path).forward(request, response);}
}
12.5.6 执行拦截器的afterCompletion
在DispatcherServlet
类的 doDispatch
方法中:
在HandlerExecutionChain
中:
12.5.7 初步测试
启动服务器,浏览器地址栏:http://localhost:8080/myspringmvc
后台效果:
如果让第二个拦截器返回false尝试一下:
初步测试通过!!!
相关文章:
第12章 手写Spring MVC
第十二章 手写Spring MVC 12.1 基本结构搭建 12.1.1 创建Maven模块 12.1.2 引入Servlet依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XM…...
C#基础31-35
31.已知学生的记录由学号和学习成绩构成,N名学生的数据已存入student数组中。找出成绩最低的学生记录(假定最低成绩的记录是唯一的)并输出。 32.在键盘上任意输入一个字符串,该字符串全部由英文字母组成,把字符串中的最后一个字符的ASCII值右移4位后加最后第二个字符的ASCII…...
GPT相关的学术库——收藏更新自用
GOT-OCR2.0 General OCR Theory: Towards OCR-2.0 via a Unified End-to-end Model https://github.com/Ucas-HaoranWei/GOT-OCR2.0/tree/main ChatPaper 工具名称工具作用是否在线?在线预览备注ChatPaper通过ChatGPT实现对论文进行总结,帮助科研人进…...
速度革命:esbuild如何改变前端构建游戏 (1)
什么是 esbuild? esbuild 是一款基于 Go 语言开发的 JavaScript 构建打包工具,以其卓越的性能著称。相比传统的构建工具(如 Webpack),esbuild 在打包速度上有着显著的优势,能够将打包速度提升 10 到 100 倍…...
AR商业化的“AI转身”
AI会不会是AR厂商良药? 作者|周立青 编辑|杨舟 11月,国内“AR四小龙”之一的Rokid举办了新品发布会,在发布会的同步直播间,出现了一条不合时宜的“什么时候发去年的工资”的评论。 “公司确实没有给我们发2023年的十三薪和年终…...
Python 中的装饰器是什么?
装饰器是Python中一种非常强大的功能,它允许你在不修改原始函数代码的前提下,增加额外的功能或改变函数的行为。 装饰器本质上是一个接受函数作为参数的函数,并返回一个新的函数。 通过装饰器,我们可以轻松地实现诸如日志记录、…...
jupyter notebook的 markdown相关技巧
目录 1 先选择为markdown类型 2 开关技巧 2.1 运行markdown 2.2 退出markdown显示效果 2.3 注意点:一定要 先选择为markdown类型 3 一些设置技巧 3.1 数学公式 3.2 制表 3.3 目录和列表 3.4 设置各种字体效果:加粗,斜体&#x…...
oracle 创建只可以查询权限用户+sqldeveloper如何看到对应表
声明 申明部分是从其他csdn用户哪里复制的,只是自己操作后发现无法达到我最后的预期,所以关闭忘记是看的那篇了,如果有侵权请见谅,联系我删除谢谢。 好了,故事的开始是我最近删投产表了。没错职业黑点,清…...
化工行业 FMEA 与安全生产的关系
【大家好,我是唐Sun,唐Sun的唐,唐Sun的Sun。】 在化工行业这个充满复杂性和潜在危险的领域中,确保安全生产是至关重要的目标。失效模式及影响分析(FMEA)作为一种有效的风险管理工具,在保障化工行…...
前端node.js
一.什么是node.js 官网解释:Node.js 是一个开源的、跨平台的 JavaScript 运行时环境。 二.初步使用node.js 需要区分开的是node.js和javascript互通的只有console和定时器两个API. 三.Buffer Buffer 是一个类似于数组的 对象,用于表示固定长度的字节序列。Buffer…...
深度学习:GPT-2的MindSpore实践
GPT-2简介 GPT-2是一个由OpenAI于2019年提出的自回归语言模型。与GPT-1相比,仍基于Transformer Decoder架构,但是做出了一定改进。 模型规格上: GPT-1有117M参数,为下游微调任务提供预训练模型。 GPT-2显著增加了模型规模&…...
Java线程池详解:从基础到实践
Java线程池详解:从基础到实践 在现代多线程编程中,线程池是一个非常重要的工具。它能帮助开发者更高效地管理线程资源,避免频繁创建和销毁线程导致的性能损耗,并提供更灵活的任务调度机制。本文将详细讲解线程池相关知识…...
A*(A-star)算法
概述 A*(A-star)算法是一种在图中寻找从初始节点到目标节点最短路径的启发式搜索算法。它结合了Dijkstra算法的确保性(保证找到一条最短路径)和贪心算法的高效性(快速找到目标)。A*算法通过评估函数f(n) …...
计算机类大厂实习春招秋招开发算法面试问答练习题
计算机类大厂实习春招秋招开发算法面试问答练习题 下面有十个非常重要且常问,面试者却注意不到的问题,我们一个个来看,一个个来学。 线程创建到删除过程中,底层是怎么实现的 1.线程创建 线程创建是线程生命周期的起点。在操作系统中,线程可以通过多种方式创建,但无论哪…...
C++ 11重点总结1
智能指针 智能指针: C11引入了四种智能指针: auto_ptr(已弃用)、unique_ptr、shared_ptr和weak_ptr。智能指针可以更有效地管理堆内存,并避免常见的内存泄漏问题。 shared_ptr: 自定义删除器。 shared_ptr使用引用计数来管理它指向的对象的生命周期。多个shared_ptr实例可以指向…...
Java异常
目录 一、异常的层次结构 1.1、Throwable 1.2、Error(错误) 1.3、Exception(异常) 运行时异常 非运行时异常 (编译异常) 1.4、可查的异常(checked exceptions)和不可查的异常…...
《热带气象学报》
《热带气象学报》创刊于1984年,前身为《热带气象》,1993年更名为《热带气象学报》,是广东省气象局主管,中国气象局广州热带海洋气象研究所主办的中文学术期刊。 本刊坚持“热带气象”的办刊特色,主要刊登:…...
解决docker不加载 /etc/docker/daemon.json文件的问题
文章目录 问题起源解决方案问题反思 问题起源 如题,最近在ubuntu24.04-LTS-server安装docker时,安装成功后设置源来设置镜像。 设置完成功拉取镜像,我就关机下次使用。 但是当我重启发现呢,镜像都不在了,但是由于网络…...
数据结构(初阶7)---七大排序法(堆排序,快速排序,归并排序,希尔排序,冒泡排序,选择排序,插入排序)(详解)
排序 1.插入排序2.希尔排序3.冒泡排序4.选择排序(双头排序优化版)5.堆排序6.快速排序1). 双指针法2).前后指针法3).非递归法 7.归并排序1).递归版本(递归的回退就是归并)2).非递归版本(迭代版本) 计算机执行的最多的操作之一就有排序,排序是一项极其重要的技能 接下…...
题解 洛谷 Luogu P1182 数列分段 Section II 二分答案 C/C++
题目传送门: P1182 数列分段 Section II - 洛谷 | 计算机科学教育新生态https://www.luogu.com.cn/problem/P1182思路: 二分答案,每次以区间 [l, r] 中点 m 为每段和的阈值 判断在此前提下,划分段数是否不大于 M 是就记录答案…...
鸿蒙心路旅程:从实践到创新——开发者的深度技术分享
目录 1. 引言:成为HarmonyOS NEXT开发者的动机 2. 项目初始化与架构设计:从零开始的技术规划 2.1 DevEco Studio的配置与项目初始化 2.2 分层架构设计 3. 分布式应用设计:挑战与解决方案 3.1 分布式架构设计:分布式软总线 …...
elementUI非常规数据格式渲染复杂表格(副表头、合并单元格)
效果 数据源 前端代码 (展示以及表格处理/数据处理) 标签 <el-table :data"dataList" style"width: 100%" :span-method"objectSpanMethod"><template v-for"(item, index) in headers"><el-table-column prop"…...
mysl数据库(八)事务、三种读现象
事务、三种读现象 文章目录 事务、三种读现象一、事务介绍二、事务的使用三、三种读现象 一、事务介绍 事务是mysql的一种机制,一个事务里可以包含多条sql语句。执行事务相当于拍了一张快照,在事务执行完提交以前可以回滚至最初的状态,当然事…...
【DVWA】File Inclusion文件包含实战
安能有术无道有道无心,乐得仁心仁义正心行道。 1.File Inclusion(Low) 相关代码分析 <?php// The page we wish to display $file $_GET[ page ];?>可以看到,服务器端对page参数没有做任何的过滤跟检查。 服务器期望用户的操作是点击下面的…...
(免费送源码)计算机毕业设计原创定制:Java+ssm+JSP+Ajax SSM棕榈校园论坛的开发
摘要 随着计算机科学技术的高速发展,计算机成了人们日常生活的必需品,从而也带动了一系列与此相关产业,是人们的生活发生了翻天覆地的变化,而网络化的出现也在改变着人们传统的生活方式,包括工作,学习,社交…...
异常处理(4)throws
异常处理(4) throws 前言:运行时异常(RuntimeException)或它的子类可以不做处理,因为这类异常很普遍,若全部处理,可能会对程序的可读性和运行效率产生影响。此外,即使不使…...
点云欧式聚类,条件欧式聚类算法原理及推导
点云欧式聚类算法数学推导 点云欧式聚类(Euclidean Clustering for Point Clouds)是点云处理中常用的一种无监督聚类方法。它基于欧式距离将点云中的点划分为多个簇,常用于分割、目标检测等任务。以下是算法的数学推导和实现原理。 问题定义…...
每日十题八股-2024年11月27日
1.类型互转会出现什么问题吗? 2.为什么用bigDecimal 不用double ? 3.装箱和拆箱是什么? 4.Java为什么要有Integer? 5.Integer相比int有什么优点? 6.那为什么还要保留int类型? 7.说一下 integer的缓存 8.怎么…...
C++虚函数面试题及参考答案
什么是虚函数?它的作用是什么? 虚函数是在基类中使用关键字 virtual 声明的成员函数。当在派生类中重写(override)这个函数时,会根据对象的实际类型来调用相应的函数版本,而不是仅仅根据指针或引用的类型来…...
如何搭建C++环境--1.下载安装并调试Microsoft Visual Studio Previerw(Windows)
1.首先,打开浏览器 首先,搜索“Microsoft Visual Studio Previerw” 安装 1.运行VisualStudioSetup (1).exe 无脑一直点继续 然后就到 选择需要的语言 我一般python用pycharm Java,HTML用vscode(Microsoft Visual Studio cod…...
大数据新视界 -- Hive 函数应用:复杂数据转换的实战案例(下)(12/ 30)
💖💖💖亲爱的朋友们,热烈欢迎你们来到 青云交的博客!能与你们在此邂逅,我满心欢喜,深感无比荣幸。在这个瞬息万变的时代,我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…...
深入理解 TypeScript:联合类型与交叉类型的应用
在 TypeScript 的世界里,类型系统是核心特性之一,它提供了强大的工具来帮助开发者编写更安全、更可靠的代码。今天,我们将深入探讨 TypeScript 中的两个高级类型特性:联合类型(Union Types)和交叉类型&…...
fiddler抓包工具与requests库构建自动化报告
一. Fiddler 抓包工具 1.1 Fiddler 工具介绍和安装 Fiddler 是一款功能强大的 HTTP 调试代理工具,能够全面记录并深入检查您的计算机与互联网之间的 HTTP 和 HTTPS 通信数据。其主界面布局清晰,主要包含菜单栏、工具栏、树形标签栏和内容栏。 1.2 Fid…...
数据结构——排序算法第二幕(交换排序:冒泡排序、快速排序(三种版本) 归并排序:归并排序(分治))超详细!!!!
文章目录 前言一、交换排序1.1 冒泡排序1.2 快速排序1.2.1 hoare版本 快排1.2.2 挖坑法 快排1.2.3 lomuto前后指针 快排 二、归并排序总结 前言 继上篇学习了排序的前面两个部分:直接插入排序和选择排序 今天我们来学习排序中常用的交换排序以及非常稳定的归并排序 快排可是有多…...
Vue-常用指令
🌈个人主页:前端青山 🔥系列专栏:Vue篇 🔖人终将被年少不可得之物困其一生 依旧青山,本期给大家带来Vue篇专栏内容:Vue-常用指令 目录 1.1 v-cloak 1.2 双向数据绑定指令 v-model 1.3 v-once 1.4 绑定属性 v-bind…...
守护进程
目录 守护进程 前台进程 后台进程 session(进程会话) 前台任务和后台任务比较好 本质 绘画和终端都关掉了,那些任务仍然在 bash也退了,然后就托孤了 编辑 守护进程化---不想受到任何用户登陆和注销的影响编辑 如何…...
GPON原理
GPON网络架构 对于OLT来说,它就相当于一个指挥官,它指挥PON口下的ONU在指定的时间段内发送数据以及发起测距过程等 而ONU则是一个士兵,按照OLT的指挥做出相应 而ODN它主要就是提供一个传输通道,主要包括分光器和光纤组成 对于PO…...
华三(HCL)和华为(eNSP)模拟器共存安装手册
接上章叙述,解决同一台PC上同时部署华三(HCL)和华为(eNSP)模拟器。原因就是华三HCL 的老版本如v2及以下使用VirtualBox v5版本,可以直接和eNSP兼容Oracle VirtualBox,而其他版本均使用Oracle VirtualBox v6以上的版本,…...
类和对象--中--初始化列表(重要)、隐式类型转化(理解)、最后两个默认成员函数
1.初始化列表 1.1作用: 通过特定的值,来初始化对象。 1.2定义: 初始化列表,就相当于定义对象(开空间)。不管写不写初始化列表,每个成员变量都会走一遍初始化列表(开出对应的空间…...
clickhouse 使用global in 优化 in查询
文章目录 in例子使用global in in例子 SELECT uniq(UserID) FROM distributed_table WHERE CounterID 101500 AND UserID IN (SELECT UserID FROM distributed_table WHERE CounterID 34)对于in 查询来说,本来查询的就是分布式表,假设这个表有100 个…...
macos 14.0 Monoma 修改顶部菜单栏颜色
macos 14.0 设置暗色后顶部菜单栏还维持浅色,与整体不协调。 修改方式如下:...
鸿蒙动画开发07——粒子动画
1、概 述 粒子动画是在一定范围内随机生成的大量粒子产生运动而组成的动画。 动画元素是一个个粒子,这些粒子可以是圆点、图片。我们可以通过对粒子在颜色、透明度、大小、速度、加速度、自旋角度等维度变化做动画,来营造一种氛围感,比如下…...
Matlab 2016b安装教程附安装包下载
软件介绍 MATLAB(矩阵实验室)是MathWorks公司推出的用于算法开发、数据可视化、数据分析以及数值计算的高级技术计算语言和交互式环境的商业数学软件。MATLAB具有数值分析、数值和符号计算、工程与科学绘图、控制系统的设计与仿真、数字图像处理、数字信…...
Container image .... already present on machine 故障排除
故障现象: Normal Pulled 12s (x2 over 15s) kubelet Container image “registry.cn-hangzhou.aliyuncs.com/yinzhengjie-k8s/apps:v1” already present on machine kubectl get pods NAME READY STATUS RESTARTS AGE two-pod 1/2 Error …...
力扣 二叉树的层序遍历-102
二叉树的层序遍历-102 class Solution { public:vector<vector<int>> levelOrder(TreeNode* root) {vector<vector<int>> res; // 二维数组用来存储每层节点if (root nullptr)return res;queue<TreeNode*> q; // 队列用来进行层序遍历q.push(r…...
Java 平衡二叉树 判断 详解
判断平衡二叉树的详解(Java 实现) 平衡二叉树的定义: 平衡二叉树(Balanced Binary Tree)是指一棵二叉树中任意节点的左右子树高度差不超过 1。即: ∣ h e i g h t ( l e f t ) − h e i g h t ( r i g h …...
Java设计模式笔记(一)
Java设计模式笔记(一) (23种设计模式由于篇幅较大分为两篇展示) 一、设计模式介绍 1、设计模式的目的 让程序具有更好的: 代码重用性可读性可扩展性可靠性高内聚,低耦合 2、设计模式的七大原则 单一职…...
【人工智能学习之yolov8改进的网络怎么指定规模】
yolov8改进的网络怎么指定规模 在你更换主干网络或者做了其他修改之后,发现模型总是默认使用的n规模,而n规模有可能无法完成任务,怎么办呢,有什么办法指定规模大小呢? WARNING ⚠️ no model scale passed. Assuming …...
网络安全概述
网络安全 物理安全 网络的物理安全是整个网络系统安全的前提。在 校园网工程建设中,由于网络系统属于 弱电工程,耐压值很低。因此,在 网络工程的设计和施工中,必须优先考虑保护人和 网络设备不受电、火灾和雷击的侵害࿱…...
[MySQL#2] 库 | 表 | 详解CRUD命令 | 字符集 | 校验规则
目录 一. 库操作 1. 创建数据库 2. 字符集和校验规则 校验规则对数据库的影响 显示创建数据库时对应的命令 3. 修改数据库 4. 数据库删除 备份和恢复 还原 查看连接情况 二. 表操作 1. 创建表(定义实例化格式 2. 创建表案例 (实例化数据类型…...