仅做流程和源码的仿真,细节上的一些BUG可忽略(读者可自行完善)
源码剖析可查看我的其他springMVC相关博文
1.maven依赖
<dependencies>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.10</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils -->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
</dependencies>
2.配置程序入口
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.HandlesTypes;
import java.lang.reflect.Method;
import java.util.Set;
/**
* @Description todo
* @Author: yanxh<br>
* @Date 2020-04-09 09:39<br>
* @Version 1.0<br>
*/
@HandlesTypes(YXHWebInitializer.class)
public class MvcServletContainerInitializer implements ServletContainerInitializer {
public void onStartup(Set<Class<?>> webInitializerClasses, ServletContext servletContext) throws ServletException {
if (webInitializerClasses.isEmpty()) {
servletContext.log("没有启动的配置类...");
return;
}
try {
for (Class<?> clazz : webInitializerClasses) {
// YXHWebInitializer
Method method = clazz.getMethod("onStartup", ServletContext.class);
YXHWebInitializer webInitializer = (YXHWebInitializer) clazz.newInstance();
method.invoke(webInitializer, servletContext);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* @Description todo
* @Author: yanxh<br>
* @Date 2020-04-09 09:39<br>
* @Version 1.0<br>
*/
public interface YXHWebInitializer {
void onStartup(ServletContext servletContext);
}
import live.yanxiaohui.create.servlet.DispatcherServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
/**
* @Description todo
* @Author: yanxh<br>
* @Date 2020-04-09 09:35<br>
* @Version 1.0<br>
*/
public class SpringMvcInitializer implements YXHWebInitializer {
public void onStartup(ServletContext servletContext) {
// 1.将dispatcher注册至servle容器
ServletRegistration.Dynamic dynamic =servletContext.addServlet("dispatcher", new DispatcherServlet(SpringMvcConfig.class));
// 2.设置拦截的路径
dynamic.addMapping("/");
// 3.设置加载顺序的优先级
dynamic.setLoadOnStartup(1);
}
}
package live.yanxiaohui.create.config;
import live.yanxiaohui.create.annotation.YXHComponentScan;
/**
* @Description todo
* @Author: yanxh<br>
* @Date 2020-04-09 10:43<br>
* @Version 1.0<br>
*/
@YXHComponentScan("live.yanxiaohui.create.controller")
public class SpringMvcConfig {
}
3.servlet相关类
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
/**
* @Description todo
* @Author: yanxh<br>
* @Date 2020-04-09 10:29<br>
* @Version 1.0<br>
*/
public abstract class HttpServletBean extends HttpServlet {
@Override
public final void init() throws ServletException {
// 1.公共初始化的操作
// 2.子类的操作
initServletBean();
}
protected void initServletBean() throws ServletException {
}
}
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Description todo
* @Author: yanxh<br>
* @Date 2020-04-09 10:29<br>
* @Version 1.0<br>
*/
public abstract class FrameworkServlet extends HttpServletBean {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doService(req, resp);
}
protected abstract void doService(HttpServletRequest req, HttpServletResponse resp);
@Override
protected final void initServletBean() throws ServletException {
initWebApplicationContext();
}
protected void initWebApplicationContext() {
onRefresh();
}
protected void onRefresh() {
}
}
import live.yanxiaohui.create.annotation.YXHComponentScan;
import live.yanxiaohui.create.annotation.YXHController;
import live.yanxiaohui.create.annotation.YXHRequestMapping;
import live.yanxiaohui.create.entity.HandlerMethod;
import live.yanxiaohui.create.entity.MappingRegistry;
import live.yanxiaohui.create.utils.ReflectionUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Locale;
/**
* @Description todo
* @Author: yanxh<br>
* @Date 2020-04-09 10:29<br>
* @Version 1.0<br>
*/
public class DispatcherServlet extends FrameworkServlet {
// 配置类
private Class<?> configClass;
private final MappingRegistry mappingRegistry = new MappingRegistry();
public DispatcherServlet(Class<?> configClass) {
this.configClass = configClass;
}
@Override
protected void doService(HttpServletRequest req, HttpServletResponse resp) {
doDispatch(req, resp);
}
@Override
protected void onRefresh() {
initStrategies();
}
protected void initStrategies() {
// springMVC的九大组件
/*initMultipartResolver();
initLocaleResolver();
initThemeResolver();
initHandlerMappings();
initHandlerAdapters();
initHandlerExceptionResolvers();
initRequestToViewNameTranslator();
initViewResolvers();
initFlashMapManager();*/
initHandlerMappings();
}
/**
* 初始化路径映射
*/
private void initHandlerMappings() {
try {
// 1.拿到扫包范围
YXHComponentScan componentScan = configClass.getAnnotation(YXHComponentScan.class);
if (componentScan == null) {
System.out.println("扫包范围为空...");
return;
}
String componentScanValue = componentScan.value();
// 2.拿到包下有类
List<Class<?>> classList = ReflectionUtils.getClasses(componentScanValue);
// 3.判断类是否加controller
for (Class<?> clazz : classList) {
if (clazz.getAnnotation(YXHController.class) == null) {
continue;
}
Method[] methods = clazz.getMethods();
for (Method method : methods) {
// 4.判断方法是否加requestmapping
YXHRequestMapping mapping = method.getAnnotation(YXHRequestMapping.class);
if (mapping == null) {
continue;
}
HandlerMethod handlerMethod = new HandlerMethod(clazz.newInstance(), clazz, method, method.getParameters());
// 5.将requestmapping和方法做绑定放入map
mappingRegistry.regist(mapping.value(), handlerMethod);
}
}
System.out.println("路径映射注册完成....");
} catch (Exception e) {
e.printStackTrace();
}
}
protected void doDispatch(HttpServletRequest req, HttpServletResponse resp) {
try {
// 通过路径获取对应的mapping
String url = req.getServletPath();
HandlerMethod handlerMethod = mappingRegistry.getHandlerMethod(url);
// 执行目标方法
Object target = handlerMethod.getBean();
Method method = handlerMethod.getMethod();
Object[] parameters = handlerMethod.getParameters();
Object retrunValue = method.invoke(target, parameters);
// 视图解析
render(retrunValue, req, resp);
} catch (Exception e) {
e.printStackTrace();
}
}
protected void render(Object retrunValue, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 只做简单的仿真
String viewName = String.valueOf(retrunValue);
String path = "/WEB-INF/views/"+viewName+".jsp";
// 请求转发
request.getRequestDispatcher(path).forward(request, response);
}
}
4.自定义注解
import java.lang.annotation.*;
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface YXHRequestMapping {
String value() default "";
}
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface YXHController {
String value() default "";
}
import java.lang.annotation.*;
/**
* @Description todo
* @Author: yanxh<br>
* @Date 2020-04-09 09:36<br>
* @Version 1.0<br>
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface YXHComponentScan {
String value();
}
5.工具类
package live.yanxiaohui.create.utils;
/**
* @Description todo
* @Author: yanxh<br>
* @Date 2020-04-09 09:52<br>
* @Version 1.0<br>
*/
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* 反射工具类 <br />
* <br />
* 提供了一系列的获取某一个类的信息的方法<br />
* 包括获取全类名,实现的接口,接口的泛型等<br />
* 并且提供了根据Class类型获取对应的实例对象的方法,以及修改属性和调用对象的方法等
*/
public class ReflectionUtils {
/**
* 从包package中获取所有的Class
*
* @param packageName
* @return
*/
public static List<Class<?>> getClasses(String packageName) {
//第一个class类的集合
List<Class<?>> classes = new ArrayList<Class<?>>();
//是否循环迭代
boolean recursive = true;
//获取包的名字 并进行替换
String packageDirName = packageName.replace('.', '/');
//定义一个枚举的集合 并进行循环来处理这个目录下的things
Enumeration<URL> dirs;
try {
dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
//循环迭代下去
while (dirs.hasMoreElements()) {
//获取下一个元素
URL url = dirs.nextElement();
//得到协议的名称
String protocol = url.getProtocol();
if ("file".equals(protocol)) {
//获取包的物理路径
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
//以文件的方式扫描整个包下的文件 并添加到集合中
findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
} else if ("jar".equals(protocol)) {
//如果是jar包文件
//定义一个JarFile
JarFile jar;
try {
//获取jar
jar = ((JarURLConnection) url.openConnection()).getJarFile();
//从此jar包 得到一个枚举类
Enumeration<JarEntry> entries = jar.entries();
//同样的进行循环迭代
while (entries.hasMoreElements()) {
//获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
JarEntry entry = entries.nextElement();
String name = entry.getName();
//如果是以/开头的
if (name.charAt(0) == '/') {
//获取后面的字符串
name = name.substring(1);
}
//如果前半部分和定义的包名相同
if (name.startsWith(packageDirName)) {
int idx = name.lastIndexOf('/');
//如果以"/"结尾 是一个包
if (idx != -1) {
//获取包名 把"/"替换成"."
packageName = name.substring(0, idx).replace('/', '.');
}
//如果可以迭代下去 并且是一个包
if ((idx != -1) || recursive) {
//如果是一个.class文件 而且不是目录
if (name.endsWith(".class") && !entry.isDirectory()) {
//去掉后面的".class" 获取真正的类名
String className = name.substring(packageName.length() + 1, name.length() - 6);
try {
//添加到classes
classes.add(Class.forName(packageName + '.' + className));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return classes;
}
/**
* 以文件的形式来获取包下的所有Class
*
* @param packageName
* @param packagePath
* @param recursive
* @param classes
*/
public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, List<Class<?>> classes) {
//获取此包的目录 建立一个File
File dir = new File(packagePath);
//如果不存在或者 也不是目录就直接返回
if (!dir.exists() || !dir.isDirectory()) {
return;
}
//如果存在 就获取包下的所有文件 包括目录
File[] dirfiles = dir.listFiles(new FileFilter() {
//自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
public boolean accept(File file) {
return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
}
});
//循环所有文件
for (File file : dirfiles) {
//如果是目录 则继续扫描
if (file.isDirectory()) {
findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, classes);
} else {
//如果是java类文件 去掉后面的.class 只留下类名
String className = file.getName().substring(0, file.getName().length() - 6);
try {
//添加到集合中去
classes.add(Class.forName(packageName + '.' + className));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
6.用到的entity
package live.yanxiaohui.create.entity;
import java.lang.reflect.Method;
/**
* @Description todo
* @Author: yanxh<br>
* @Date 2020-04-09 11:17<br>
* @Version 1.0<br>
*/
public class HandlerMethod {
private final Object bean;
private final Class<?> beanType;
private final Method method;
private final Object[] parameters;
public HandlerMethod(Object bean, Class<?> beanType, Method method, Object[] parameters) {
this.bean = bean;
this.beanType = beanType;
this.method = method;
this.parameters = parameters;
}
public Object getBean() {
return bean;
}
public Class<?> getBeanType() {
return beanType;
}
public Method getMethod() {
return method;
}
public Object[] getParameters() {
return parameters;
}
}
package live.yanxiaohui.create.entity;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @Description todo
* @Author: yanxh<br>
* @Date 2020-04-09 11:05<br>
* @Version 1.0<br>
*/
public class MappingRegistry {
private final Map<String, HandlerMethod> registry = new ConcurrentHashMap<String, HandlerMethod>();
public void regist(String url, HandlerMethod handlerMethod){
registry.put(url,handlerMethod);
}
public HandlerMethod getHandlerMethod(String url){
return registry.get(url);
}
}
7.控制器
package live.yanxiaohui.create.controller;
import live.yanxiaohui.create.annotation.YXHController;
import live.yanxiaohui.create.annotation.YXHRequestMapping;
/**
* @Description todo
* @Author: yanxh<br>
* @Date 2020-04-09 09:42<br>
* @Version 1.0<br>
*/
@YXHController
public class IndexController {
@YXHRequestMapping("/")
public String index(){
return "index";
}
@YXHRequestMapping("/create")
public String create(){
return "create";
}
public String delete(){
return "delete";
}
}
8.新建视图
9.将项目放至tomcat服务器启动访问