springmvc是Spring框架中的一个模块,是一个基于mvc模式的web模块,负责前后台的交互工作;所谓的mvc其实就是一种设计模式,从而实现系统的解耦。
SpringMVC执行流程
01、用户发送出请求到前端控制器DispatcherServlet。
02、DispatcherServlet收到请求调用HandlerMapping(处理器映射器)
03、HandlerMapping找到具体的处理器(可查找xml配置或注解配置),生成处理器对象及处理器拦截器(如果有),再一起返回给DispatcherServlet。
04、DispatcherServlet调用HandlerAdapter(处理器适配器)。
05、HandlerAdapter经过适配调用具体的处理器(Handler/Controller)。
06、Controller执行完成返回ModelAndView对象。
07、HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet。
08、DispatcherServlet将ModelAndView传给ViewReslover(视图解析器)。
09、ViewReslover解析后返回具体View(视图)。
10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11、DispatcherServlet响应用户。
SpringMVC的特点优势
1.清晰的角色划分:控制器(controller)、验证器(validator)、命令对象(command obect)、表单对象(form object)、模型对象(model object)、Servlet分发器(DispatcherServlet)、处理器映射(handler mapping)、试图解析器(view resoler)等等。每一个角色都可以由一个专门的对象来实现。
2.强大而直接的配置方式:将框架类和应用程序类都能作为JavaBean配置,支持跨多个context的引用,例如,在web控制器中对业务对象和验证器validator)的引用。
3.可适配、非侵入:可以根据不同的应用场景,选择何事的控制器子类(simple型、command型、from型、wizard型、multi-action型或者自定义),而不是一个单一控制器(比如Action/Actio
nForm)继承。
4.可重用的业务代码:可以使用现有的业务对象作为命令或表单对象,而不需要去扩展某个特定框架的基类。
5.可定制的绑定(binding)和验证(validation):比如将类型不匹配作为应用级的验证错误,这可以保证错误的值。再比如本地化的日期和数字绑定等等。在其他某些框架中,你只能使用字符串表单对象,需要手动解析它并转换到业务对象。
6.可定制的handler mapping和view resolution:Spring提供从最简单的URL映射,到复杂的、专用的定制策略。与某些web MVC框架强制开发人员使用单一特定技术相比,Spring显得更加灵活。
7.灵活的model转换:在Springweb框架中,使用基于Map的键/值对来达到轻易的与各种视图技术集成。
8.可定制的本地化和主题(theme)解析:支持在JSP中可选择地使用Spring标签库、支持JSTL、支持Velocity(不需要额外的中间层)等等。
9.简单而强大的JSP标签库(Spring Tag Library):支持包括诸如数据绑定和主题(theme)之类的许多功能。他提供在标记方面的最大灵活性。
10.JSP表单标签库:在Spring2.0中引入的表单标签库,使用在JSP编写表单更加容易。
11.Spring Bean的生命周期:可以被限制在当前的HTTp Request或者HTTp Session。准确的说,这并非Spring MVC框架本身特性,而应归属于Spring MVC使用的WebApplicationContext容器。
手写一个简单的SpringMVC框架
1、新建一个maven项目,工程目录如下
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.xuwei</groupId>
<artifactId>xw-springmvc</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>xw-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>servlet-api</artifactId>
<version>2.4</version>
</dependency>
</dependencies>
<build>
<finalName>xw-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、在WEB-INF目录下修改web.xml文件,resources目录下新建application.properties
<!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>myspringmvc</servlet-name>
<servlet-class>com.xuwei.servlet.MyDispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>application.properties</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myspringmvc</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
3、设置自己相关注解
package com.xuwei.annotaion;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRestController {
String value() default "";
}
package com.xuwei.annotaion;
import java.lang.annotation.*;
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestMapping {
String value() default "";
}
package com.xuwei.annotaion;
import java.lang.annotation.*;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestParam {
String value();
}
4、手写自己的DispatcherServlet
package com.xuwei.servlet;
import com.xuwei.annotaion.MyRequestMapping;
import com.xuwei.annotaion.MyRestController;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
/**
* @program: xwspringmvc
* @author: xuWei
* @create: 2019/12/22
* @description:
*/
public class MyDispatcherServlet extends HttpServlet {
private Properties properties = new Properties();
private List<String>classNames=new ArrayList<>();
private Map<String ,Object>ioc=new HashMap<>();
private Map<String, Method>handlerMapping = new HashMap<>();
private Map<String ,Object>controllerMap=new HashMap<>();
@Override
public void init(ServletConfig config) throws ServletException {
//1、加载配置文件
doLoadConfig(config.getInitParameter("contextConfigLocation"));
//2、初始化所有相关联的累,扫描用户设定的包下面所有的类
doScanner(properties.getProperty("scanPackage"));
//3、通过扫描到的累,通过反射机制实例化,并放到ioc容器之中
doInstance();
//4、初始化HandlerMapping(将url和Method对应)
initHandlerMapping();
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
doDispatch(req,resp);
}catch (Exception e){
e.printStackTrace();
}
}
private void doDispatch(HttpServletRequest req, HttpServletResponse resp)throws Exception{
if(handlerMapping.isEmpty()){
return;
}
String url=req.getRequestURI();
String contextPath=req.getContextPath();
url=url.replace(contextPath,"").replaceAll("/+","/");
if(!this.handlerMapping.containsKey(url)){
resp.getWriter().write("404 NOT FOUND!");
return;
}
Method method = this.handlerMapping.get(url);
Class<?>[] parameterTypes = method.getParameterTypes();
//保存参数值
Object[]paramValues = new Object[parameterTypes.length];
//获取请求的参数
Map<String,String[]> parameterMap = req.getParameterMap();
for(int i=0;i<parameterTypes.length;i++){
String requestParam = parameterTypes[i].getSimpleName();
if(requestParam.equals("HttpServletRequest")){
paramValues[i]=req;
continue;
}
if(requestParam.equals("HttpServletResponse")){
paramValues[i]=resp;
continue;
}
if(requestParam.equals("String")){
for(Map.Entry<String,String[]> param:parameterMap.entrySet()){
String value=Arrays.toString(param.getValue()).replaceAll("\\[|\\]","").replaceAll(",\\s","");
paramValues[i]=value;
}
}
}
try {
method.invoke(this.controllerMap.get(url),paramValues);
}catch (Exception e){
e.printStackTrace();
}
}
private void doLoadConfig(String location) {
InputStream resourceAsStream =
this.getClass().getClassLoader().getResourceAsStream(location);
try {
properties.load(resourceAsStream);
}catch (Exception e){
e.printStackTrace();
}finally {
if(null != resourceAsStream){
try {
resourceAsStream.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
private void doScanner(String scanPackage) {
URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
File dir=new File(url.getFile());
for(File file:dir.listFiles()){
if(file.isDirectory()){
doScanner(scanPackage+"."+file.getName());
}else {
String className = scanPackage + "." + file.getName().replace(".class", "");
classNames.add(className);
}
}
}
private void doInstance() {
if(classNames.isEmpty()){
return;
}
for(String className:classNames){
try {
Class<?> clazz = Class.forName(className);
if(clazz.isAnnotationPresent(MyRestController.class)){
ioc.put(toLowerFirstWord(clazz.getSimpleName()),clazz.newInstance());
}else {
continue;
}
}catch (Exception e){
e.printStackTrace();
}
}
}
private void initHandlerMapping() {
if(ioc.isEmpty()){
return;
}
try {
for(Map.Entry<String,Object>entry:ioc.entrySet()){
Class<?> clazz = entry.getValue().getClass();
if(!clazz.isAnnotationPresent(MyRestController.class)){
continue;
}
String baseUrl="";
if(clazz.isAnnotationPresent(MyRequestMapping.class)){
MyRequestMapping annotation = clazz.getAnnotation(MyRequestMapping.class);
baseUrl=annotation.value();
}
Method[] methods = clazz.getMethods();
for(Method method:methods){
if(!method.isAnnotationPresent(MyRequestMapping.class)){
continue;
}
MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.class);
String url=annotation.value();
url=(baseUrl+"/"+url).replaceAll("/+","/");
handlerMapping.put(url,method);
controllerMap.put(url,clazz.newInstance());
System.out.println(url+"----"+method);
}
}
}catch (Exception e){
e.printStackTrace();
}
}
private String toLowerFirstWord(String simpleName) {
char[] chars = simpleName.toCharArray();
chars[0]+=32;
return String.valueOf(chars);
}
}
5、测试类
package com.xuwei.test;
import com.xuwei.annotaion.MyRequestMapping;
import com.xuwei.annotaion.MyRequestParam;
import com.xuwei.annotaion.MyRestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @program: xwspringmvc
* @author: xuWei
* @create: 2019/12/22
* @description:
*/
@MyRestController
@MyRequestMapping("/test")
public class UserController {
@MyRequestMapping("test1")
public void test1(HttpServletRequest request, HttpServletResponse response, @MyRequestParam("param")String param){
System.out.println(param);
try {
response.getWriter().write("doTest1 method success");
}catch (Exception e){
e.printStackTrace();
}
}
@MyRequestMapping("test2")
public void test2(HttpServletRequest request, HttpServletResponse response, @MyRequestParam("param")String param){
System.out.println(param);
try {
response.getWriter().println("doTest2 method success");
}catch (Exception e){
e.printStackTrace();
}
}
}
运行项目,开始进行请求