使用JDK8 ,idea2018.2, maven3.5.4
使用XML 解析 + 反射来写一个springMVC框架:
如下思路实现;
。
首先需要一个前置控制器 DispatcherServlet,作为整个流程的核心,由它去调用其他组件,共同完成业务。主要组件有两个:
一是 Controller,调用其业务方法 Method,执行业务逻辑;
二是 ViewResolver 视图解析器,将业务方法的返回值解析为物理视图 + 模型数据,返回客户端。
Spring MVC 的实现流程:
客户端请求被 DispatcherServlet(前端控制器)接收。
->根据 HandlerMapping 映射到 Handler。
->生成 Handler 和 HandlerInterceptor(如果有则生成)。
->Handler 和 HandlerInterceptor 以 HandlerExecutionChain 的形式一并返回给 DispatcherServlet。
->DispatcherServlet 通过 HandlerAdapter 调用 Handler 的方法做业务逻辑处理。
->返回一个 ModelAndView 对象给 DispatcherServlet。
->DispatcherServlet 将获取的 ModelAndView 对象传给 ViewResolver 视图解析器,将逻辑视图解析成物理视图 View。
->ViewResolver 返回一个 View 给 DispatcherServlet。
->DispatcherServlet 根据 View 进行视图渲染(将模型数据填充到视图中)。
->DispatcherServlet 将渲染后的视图响应给客户端。
分析:
HTTP 请求是通过注解找到对应的 Controller 对象…;
Controller 的 Method 也是通过注解与 HTTP 请求映射的;
使用map 当做 ioC 容器,完成储存所有参数与业务的class;
.业务逻辑:
初始化工作完成,接下来处理 HTTP 请求,业务流程如下:
DispatcherServlet 接收请求,通过映射从 IoC 容器中获取对应的 Controller 对象;
根据映射获取 Controller 对象对应的 Method;
调用 Method,获取返回值;
将返回值传给视图解析器,返回物理视图;
完成页面跳转。
doPost 方法处理 HTTP 请求:
解析 HTTP,分别得到 Controller 和 Method 对应的 URL
通过 URL 分别在 iocContainer 和 handlerMapping 中获取对应的 Controller 及 Method
使用反射调用 Method,执行业务方法,获取结果
结果传给 MyViewResolver 进行解析,返回真正的物理视图(JSP 页面)
完成 JSP 的页面跳转
项目结构如图
自定义注解:
@MyController
@MyRequestMapping
/**
* @auther SyntacticSugar
* @data 2018/11/13 0013下午 9:15
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyController {
String value() default "";
}
/**
* 自定义 @RequestMapping 注解
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRequestMapping {
String value() default "";
}
定义一个核心控制器MyDispatcherServlet ;
/**
* @auther SyntacticSugar
* @data 2018/11/13 0013下午 9:20
*
* 创建控制器
*/
public class MyDispatcherServlet extends HttpServlet {
//创建ioC 创建 handler存放容器
private HashMap<String, Object> ioC = new HashMap<>();
private HashMap<String, Method> handlerMapping = new HashMap<>();
//自定义视图解析
private MyViewResolver myViewResolver;
@Override
public void init(ServletConfig config) throws ServletException {
// 把controller放到ioC中
scanController(config);
//初始化handler 映射
initHandlerMapping();
//加载视图解析器
loadViewResolver(config);
}
@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 {
String handlerUri = req.getRequestURI().split("/")[2];
String methodUri = req.getRequestURI().split("/")[3];
//
Object o = ioC.get(handlerUri);
Method method = handlerMapping.get(methodUri);
// 使用反射机制,调用执行 业务
try {
String value = (String) method.invoke(o);
// 将逻辑视图 转化为 物理视图,交给 view渲染返回前端
String result = myViewResolver.jspMapping(value);
req.getRequestDispatcher(result).forward(req,resp );
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* saxReader 解析springmvc.xml
* @param config
*/
private void scanController(ServletConfig config) {
SAXReader saxReader = new SAXReader();
try {
String path = config.getServletContext().getRealPath("") + "\\WEB-INF\\classes\\" +
config.getInitParameter("contextConfigLocation");
Document document = saxReader.read(path);
// 获取根元素
Element rootElement = document.getRootElement();
Iterator iterator = rootElement.elementIterator();
// 遍历 nodes 、sax解析每一行xml
while (iterator.hasNext()) {
Element next = (Element) iterator.next();
// 把每一个元素的name和 component-scan 比较,获取base-package值
if (next.getName().equals("component-scan")) {
String packageName = next.attributeValue("base-package");
// 获取包下 每个子包
List<String> classNames = getClassNames(packageName);
for (String className : classNames) {
/**
通过反射获取clazz ,判断class上是否存在MyController注解
* 若存在 、获取MyRequestMapping 的 value , 并将其 装入 自定义的ioC
*/
Class<?> clazz = Class.forName(className);
if (clazz.isAnnotationPresent(MyController.class)) {
MyRequestMapping annotation = clazz.getAnnotation(MyRequestMapping.class);
String value = annotation.value().substring(1);
// 放入ioC
ioC.put(value,clazz.newInstance() );
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取 所有class 全路径名
* @param packageName
* @return
*/
private List<String> getClassNames(String packageName) {
List<String> classNameList = new ArrayList<String>();
String path = packageName.replace(".", "/");
//已知存在包路径,获取每一级路径下的file、 获取类加载器,
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
URL url = classLoader.getResource(path);
//非空判断
if (url != null) {
File[] files = new File(url.getPath()).listFiles();
//遍历取值
for (File childFile : files) {
String className = packageName + "." + childFile.getName().replace(".class", "");
classNameList.add(className);
}
}
// return
return classNameList;
}
/**
* 初始化 handler
*/
private void initHandlerMapping() {
//从ioC中取出 MyController注解的 class
for (String s : ioC.keySet()) {
Class<?> clazz = ioC.get(s).getClass();
Method[] methods = clazz.getMethods();
//遍历
for (Method method : methods) {
// 判断哪一个被 @MyRequestMapping 注解标识
if (method.isAnnotationPresent(MyRequestMapping.class)) {
MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.class);
String value = annotation.value().substring(1);
// 存入 handler
handlerMapping.put(value,method );
}
}
}
}
/**
* 加载自定义视图 saxReader 解析springmvc.xml
* @param config
*/
private void loadViewResolver(ServletConfig config) {
SAXReader reader = new SAXReader();
try {
String path = config.getServletContext().getRealPath("")+"\\WEB-INF\\classes\\"+config.getInitParameter("contextConfigLocation");
Document document = reader.read(path);
Element root = document.getRootElement();
Iterator iter = root.elementIterator();
//遍历
while(iter.hasNext()){
Element ele = (Element) iter.next();
if(ele.getName().equals("bean")){
String className = ele.attributeValue("class");
Class clazz = Class.forName(className);
Object obj = clazz.newInstance();
//获取 setter 方法
Method prefixMethod = clazz.getMethod("setPrefix", String.class);
Method suffixMethod = clazz.getMethod("setSuffix", String.class);
Iterator beanIter = ele.elementIterator();
//获取 property 值
Map<String,String> propertyMap = new HashMap<String,String>();
while(beanIter.hasNext()){
Element beanEle = (Element) beanIter.next();
String name = beanEle.attributeValue("name");
String value = beanEle.attributeValue("value");
propertyMap.put(name, value);
}
for(String str:propertyMap.keySet()){
//反射机制调用 setter 方法,完成赋值
if(str.equals("prefix")){
prefixMethod.invoke(obj, propertyMap.get(str));
}
if(str.equals("suffix")){
suffixMethod.invoke(obj, propertyMap.get(str));
}
}
myViewResolver = (MyViewResolver) obj;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
自定义一个视图解析器,MyViewResolver
/**
* @auther SyntacticSugar
* @data 2018/11/13 0013下午 9:31
*
* 自定义视图解析器 MyViewResolver
*/
public class MyViewResolver {
private String prefix;
private String suffix;
// 目标资源路径
public String jspMapping(String value){
return this.prefix+value+this.suffix;
}
//setter getter
......
}
创建测试 TestController ,对自定义的springmvc 进行测试;
/**
* @auther SyntacticSugar
* @data 2018/11/13 0013下午 10:47
*/
@MyController
@MyRequestMapping("/testController")
public class TestController {
@MyRequestMapping("/test")
public String test(){
System.out.println("执行test相关业务");
return "index";
}
}
springmvc.xml配置:
<beans>
<component-scan base-package="com.baidu"/>
<bean class="com.baidu.view.MyViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
bean>
beans>
启动tomcat访问:
http://localhost:8080/SpringMVCImitate-master/testController/test
源码下载:
https://download.csdn.net/download/weixin_42323802/10783231