发布时间:2024-04-01 13:01
根据SpringMVC执⾏的⼤致原理,⼿写一个spring mvc的简易版框架
<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.0modelVersion>
<groupId>com.lagou.edugroupId>
<artifactId>my-mvcartifactId>
<version>1.0-SNAPSHOTversion>
<packaging>warpackaging>
<name>my-mvc Maven Webappname>
<url>http://www.example.comurl>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<maven.compiler.source>11maven.compiler.source>
<maven.compiler.target>11maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>4.0.1version>
<scope>providedscope>
dependency>
<dependency>
<groupId>commons-langgroupId>
<artifactId>commons-langartifactId>
<version>2.6version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.1version>
<configuration>
<source>11source>
<target>11target>
<encoding>UTF-8encoding>
<compilerArgs>-parameterscompilerArgs>
configuration>
plugin>
<plugin>
<groupId>org.apache.tomcat.mavengroupId>
<artifactId>tomcat7-maven-pluginartifactId>
<version>2.2version>
<configuration>
<port>8082port>
<path>/path>
configuration>
plugin>
plugins>
build>
project>
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAutowired {
String value() default \"\";
}
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyController {
String value() default \"\";
}
@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRequestMapping {
String value() default \"\";
}
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyService {
String value() default \"\";
}
scanPackage=com.lagou.demo
<web-app>
<display-name>Archetype Created Web Applicationdisplay-name>
<servlet>
<servlet-name>mymvcservlet-name>
<servlet-class>com.lagou.edu.mvcframework.servlet.MyDispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>springmvc.propertiesparam-value>
init-param>
servlet>
<servlet-mapping>
<servlet-name>mymvcservlet-name>
<url-pattern>/* url-pattern>
servlet-mapping>
web-app>
package com.lagou.demo.controller;
import com.lagou.demo.service.IDemoService;
import com.lagou.edu.mvcframework.annotation.MyAutowired;
import com.lagou.edu.mvcframework.annotation.MyController;
import com.lagou.edu.mvcframework.annotation.MyRequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author lane
* @date 2021年04月02日 下午2:28
*/
@MyController
@MyRequestMapping(\"/demo\")
public class DemoController {
@MyAutowired
private IDemoService demoService;
@MyRequestMapping(\"/query\")
public String query(HttpServletRequest request, HttpServletResponse response,String name){
return demoService.get(name);
}
}
package com.lagou.demo.service;
/**
* @author lane
* @date 2021年04月02日 下午2:30
*/
public interface IDemoService {
String get(String name);
}
package com.lagou.demo.service.impl;
import com.lagou.demo.service.IDemoService;
import com.lagou.edu.mvcframework.annotation.MyService;
/**
* @author lane
* @date 2021年04月02日 下午2:31
*/
@MyService
public class DemoServiceImpl implements IDemoService {
@Override
public String get(String name) {
System.out.println(\"service 实现类中的name为:\"+name);
return name;
}
}
主要是因为反射调用metho.invoke(obj,args[])
需要传递参数,故而封装成对象,以便和url绑定
package com.lagou.edu.mvcframework.pojo;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
/**
* uri和方法映射需要多个参数封装才行
* @author lane
* @date 2021年04月06日 上午11:13
*/
public class Handler {
//对象
private Object controller;
//方法
private Method method;
//url 可以存字符串,pattern是正则类型
private Pattern pattern;
//参数顺序,参数绑定,key是参数名value 是参数的顺序
private Map<String,Integer> paramIndexMapping;
public Handler(Object controller, Method method, Pattern pattern) {
this.controller = controller;
this.method = method;
this.pattern = pattern;
this.paramIndexMapping = new HashMap<>();
}
public Object getController() {
return controller;
}
public void setController(Object controller) {
this.controller = controller;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public Pattern getPattern() {
return pattern;
}
public void setPattern(Pattern pattern) {
this.pattern = pattern;
}
public Map<String, Integer> getParamIndexMapping() {
return paramIndexMapping;
}
public void setParamIndexMapping(Map<String, Integer> paramIndexMapping) {
this.paramIndexMapping = paramIndexMapping;
}
}
package com.lagou.edu.mvcframework.servlet;
import com.lagou.edu.mvcframework.annotation.MyAutowired;
import com.lagou.edu.mvcframework.annotation.MyController;
import com.lagou.edu.mvcframework.annotation.MyRequestMapping;
import com.lagou.edu.mvcframework.annotation.MyService;
import com.lagou.edu.mvcframework.pojo.Handler;
import org.apache.commons.lang.StringUtils;
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.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author lane
* @date 2021年04月02日 上午11:35
*/
public class MyDispatcherServlet extends HttpServlet {
//配置文件信息
private Properties properties = new Properties();
//缓存扫描到的类的全限定类名
private List<String> classNames = new ArrayList<>();
//ioc容器
private Map<String, Object> ioc = new HashMap<>();
//handlerMapping映射器
//private Map handlerMapping = new HashMap<>();
//添加新的handlerMapping
private List<Handler> handlerMapping = new ArrayList<>();
@Override
public void init(ServletConfig config) throws ServletException {
//1、 加载配置文件 springmvc.properties
String contextConfigLocation = config.getInitParameter(\"contextConfigLocation\");
System.out.println(contextConfigLocation);
doLoadConfig(contextConfigLocation);
//2、 扫描相关的类相关的注解
doScan(properties.getProperty(\"scanPackage\"));
//3、 初始化bean对象(ioc注解实现)
doInstance();
//4、 实现依赖注入 di
doAutowired();
//5、 构造handlerMapping处理器映射器,建立url和方法的对应关系
initHandlerMapping();
System.out.println(\"spring mvc 初始化完成~~~~~~~~\");
//6、 等待请求进入
}
/**
* 构建handlerMapping,建立url和method之间到关系
* @author lane
* @date 2021/4/2 下午4:26
*/
private void initHandlerMapping() {
if (ioc.isEmpty())return;
for (Map.Entry<String, Object> entry : ioc.entrySet()) {
Class<?> aClass = entry.getValue().getClass();
if (!aClass.isAnnotationPresent(MyController.class)){continue;}
String baseUrl = \"\";
//是否有注解@MyRequestMapping
if (aClass.isAnnotationPresent(MyRequestMapping.class)){
String value = aClass.getAnnotation(MyRequestMapping.class).value();
baseUrl = value;
}
//获取方法
Method[] methods = aClass.getMethods();
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
if (!method.isAnnotationPresent(MyRequestMapping.class)){continue;}
MyRequestMapping methodAnnotation = method.getAnnotation(MyRequestMapping.class);
String methodValue = methodAnnotation.value();
baseUrl = baseUrl+methodValue;
//handlerMapping.put(baseUrl,method);
//把方法信息封装成一个handler对象
Handler handler = new Handler(entry.getValue(),method, Pattern.compile(baseUrl));
//获取参数信息
Parameter[] parameters = method.getParameters();
//绑定参数顺序
for (int j = 0; j < parameters.length; j++) {
Parameter parameter = parameters[j];
if(parameter.getType()==HttpServletRequest.class||parameter.getType()==HttpServletResponse.class){
//如果参数为request或response那么就是获取简单的名字为HttpServletRequest或HttpServletResponse
handler.getParamIndexMapping().put(parameter.getType().getSimpleName(),j);
}else {
//
handler.getParamIndexMapping().put(parameter.getName(),j);
}
}
//缓存起来url和method之间的关系
handlerMapping.add(handler);
}
}
}
//di
private void doAutowired() {
if (ioc.isEmpty()) return;
// 遍历容器获取里面的对象属性
for (Map.Entry<String, Object> entry : ioc.entrySet()) {
Field[] declaredFields = entry.getValue().getClass().getDeclaredFields();
//遍历属性
for (int i = 0; i < declaredFields.length; i++) {
Field declaredField = declaredFields[i];
//判断是否存在@Myautowired注解
if (declaredField.isAnnotationPresent(MyAutowired.class)) {
MyAutowired annotation = declaredField.getAnnotation(MyAutowired.class);
declaredField.setAccessible(true);
String value = annotation.value();
//判断是否注解存在值,注入属性对象到遍历对象中
try {
if (!\"\".equals(value.trim())) {
//如果注解值不为空,就以注解中的value为key获取对象进行di
declaredField.set(entry.getValue(), ioc.get(value));
} else {
//如果注解中的值为空,就获取接口的全线定名为key获取对象进行注入
declaredField.set(entry.getValue(), ioc.get(declaredField.getType().getName()));
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
//ioc容器
private void doInstance() {
if (classNames.size() == 0) return;
try {
for (int i = 0; i < classNames.size(); i++) {
String className = classNames.get(i);
Class<?> aClass = Class.forName(className);
//如果有controller注解
if (aClass.isAnnotationPresent(MyController.class)) {
//获取controller类名小写作为bean的ID,就不自定义beanid了
String aClassName = aClass.getSimpleName();
//首字母小写
String beanNameLower = lowerFirst(aClassName);
ioc.put(beanNameLower, aClass.newInstance());
} else if (aClass.isAnnotationPresent(MyService.class)) {
MyService annotation = aClass.getAnnotation(MyService.class);
//获取注解的value值
String value = annotation.value();
//判断是否指定value,若指定就按指定的为key,否则就类名首字母小写
if (!\"\".equals(value.trim())) {
ioc.put(value, aClass.newInstance());
} else {
ioc.put(lowerFirst(aClass.getSimpleName()), aClass.newInstance());
}
//service层一般是有接口的在放一份接口为id对象到ioc中,便于接口依赖注入
Class<?>[] interfaces = aClass.getInterfaces();
for (int j = 0; j < interfaces.length; j++) {
//接口的全线定名作为id
ioc.put(interfaces[j].getName(), aClass.newInstance());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
//首字母小写方法
public String lowerFirst(String str) {
char[] chars = str.toCharArray();
if (chars[0] >= \'A\' && chars[0] <= \'Z\') {
chars[0] = (char) (chars[0] + 32);
}
return String.valueOf(chars);
}
//扫描类
private void doScan(String scanPackage) {
// 获取路径
String scanPagePath = Thread.currentThread().getContextClassLoader().getResource(\"\").getPath() + scanPackage.replaceAll(\"\\\\.\", \"/\");
System.out.println(scanPagePath);
File pack = new File(scanPagePath);
File[] files = pack.listFiles();
for (File file : files) {
if (file.isDirectory()) {
//递归扫描 com.lagou.edu.controller
doScan(scanPackage + \".\" + file.getName());
} else if (file.getName().endsWith(\".class\")) {
String className = scanPackage + \".\" + file.getName().replaceAll(\".class\", \"\");
classNames.add(className);
}
}
}
//加载配置文件
private void doLoadConfig(String contextConfigLocation) {
//获取文件流
InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
try {
properties.load(resourceAsStream);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 处理请求,获取uri
Handler handler = getHandler(req);
if (handler==null){
resp.getWriter().write(\"404 not found!\");
return;
}
// 参数绑定
// 获取我们要传入的参数数组类型,因而获取其长度
Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();
// 传入参数的数组,以便反射调用方法执行
Object[] paramValues = new Object[parameterTypes.length];
// 获取请求中的参数集合
Map<String, String[]> parameterMap = req.getParameterMap();
// 遍历所有的参数,填充除了request 和response
for (Map.Entry<String,String[]> parameter : parameterMap.entrySet() ) {
//多个同类型的参数值改成,拼接形式
String value = StringUtils.join(parameter.getValue(), \",\");
//判断参数是否在我们的handler对象的参数集合中
if (!handler.getParamIndexMapping().containsKey(parameter.getKey())){continue;}
//如果存在则获取index
Integer index = handler.getParamIndexMapping().get(parameter.getKey());
//放入要传的参数有序数组中
paramValues[index] = value;
}
//最后放入req和resp
Integer reqIndex = handler.getParamIndexMapping().get(HttpServletRequest.class.getSimpleName());
Integer respIndex = handler.getParamIndexMapping().get(HttpServletResponse.class.getSimpleName());
paramValues[reqIndex] = req;
paramValues[respIndex] = resp;
//调用handler的method方法执行
try {
handler.getMethod().invoke(handler.getController(), paramValues);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
// 获取handler对象
private Handler getHandler(HttpServletRequest req) {
if (handlerMapping.isEmpty()){return null;}
String url = req.getRequestURI();
System.out.println(url);
for (Handler handler:handlerMapping) {
Matcher matcher = handler.getPattern().matcher(url);
if (!matcher.matches()){return null;}
return handler;
}
return null;
}
}
<filter>
<filter-name>encodingfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>UTF-8param-value>
init-param>
filter>
<filter-mapping>
<filter-name>encodingfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
<Connector URIEncoding=\"utf-8\" connectionTimeout=\"20000\" port=\"8080\"
protocol=\"HTTP/1.1\" redirectPort=\"8443\"/>
4月 06, 2021 2:14:37 下午 org.apache.coyote.AbstractProtocol init
信息: Initializing ProtocolHandler [\"http-bio-8082\"]
4月 06, 2021 2:14:37 下午 org.apache.catalina.core.StandardService startInternal
信息: Starting service Tomcat
4月 06, 2021 2:14:37 下午 org.apache.catalina.core.StandardEngine startInternal
信息: Starting Servlet Engine: Apache Tomcat/7.0.47
4月 06, 2021 2:14:38 下午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler [\"http-bio-8082\"]
springmvc.properties
/Volumes/workspace/startspace/my-mvc/target/classes/com/lagou/demo
/Volumes/workspace/startspace/my-mvc/target/classes/com/lagou/demo/controller
/Volumes/workspace/startspace/my-mvc/target/classes/com/lagou/demo/service
/Volumes/workspace/startspace/my-mvc/target/classes/com/lagou/demo/service/impl
spring mvc 初始化完成~~~~~~~~
/demo/query
service 实现类中的name为:lisi
虽然只是简单的实现的Spring mvc功能,但是里面包含了很多知识比如自定义注解和注解实现ioc等,还是很值得去反复学习和敲代码debug的。再总结下mvc的执行过程就是
//1、 加载配置文件 springmvc.properties
String contextConfigLocation = config.getInitParameter(\"contextConfigLocation\");
doLoadConfig(contextConfigLocation);
//2、 扫描相关的类相关的注解
doScan(properties.getProperty(\"scanPackage\"));
//3、 初始化bean对象(ioc注解实现)
doInstance();
//4、 实现依赖注入 di
doAutowired();
//5、 构造handlerMapping处理器映射器,建立url和方法的对应关系
initHandlerMapping();
System.out.println(\"spring mvc 初始化完成~~~~~~~~\");
//6、 等待请求进入