Spring------手写体验MVC(升级版)
目录
1、前言
本篇博客并非是对Spring源码的深入研究。而是对上一篇博客《Spring源码------手写体验MVC》结构的优化:进行职责对的解耦。过程中主要是体验Spring IOC和DI的初始化过程。那么这篇博客涉及到的知识点大致有以下几点:
- 如何自定义注解,如何通过反射机制去赋予注解强大的功能(说白了,就是体验在反射机制下,注解功能是多么的强大)
- Spring Ioc容器的实现原理
- Spring DI 注解注入
- Java反射机制
- Java I/O流(加载配置文件,读取配置文件信息)
- 正则表达式
2、构建思想
2.1 Spring MVC的大致工作流程
- 用户发送请求至前端控制器DispatcherServlet;
- DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle;
- 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet;
- DispatcherServlet 调用 HandlerAdapter处理器适配器;
- HandlerAdapter 经过适配调用 具体处理器(Handler,也叫后端控制器);
- Handler执行完成返回ModelAndView;
- HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet;
- DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析;
- ViewResolver解析后返回具体View;
- DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
- DispatcherServlet响应用户。
2.2 Spring MVC的九大组件
序号 | 组件名 | 解释 |
1 | MultipartResolver | 多文件上传组件 |
2 | LocaleResolver | 本地语言环境 |
3 | ThemeResolver | 主题模板处理器 |
4 | HandlerMapping | 保存Url映射关系 |
5 | HandlerAdapter | 动态参数适配器 |
6 | HandlerExceptiomResolver | 异常拦截器 |
7 | RequestToViewNameTransltor | 视图读取器,从request中获取viewname |
8 | ViewResolvers | 视图转换器,模板引擎 |
9 | FlashMapManager | 参数缓存器 |
2.3 Spring MVC的核心执行流程
3、核心代码
3.1 核心类结构图
3.2 核心代码
/**
* @description: 委派模式,职责:负责任务调度,请求分发
* @author: zps
* @create: 2020-05-09 8:25
**/
public class ZPSDispatcherServlet extends HttpServlet {
private ZPSApplicationContext applicationContext;
private List<ZPSHandlerMapping> handlerMappings = new ArrayList<ZPSHandlerMapping>();
private Map<ZPSHandlerMapping, ZPSHandlerAdapter> handlerAdapters = new HashMap<ZPSHandlerMapping, ZPSHandlerAdapter>();
private List<ZPSViewResolver> viewResolvers = new ArrayList<ZPSViewResolver>();
@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 {
//委派,根据URL去找到一个对应的Method并通过response返回
try {
doDispatch(req,resp);
} catch (Exception e) {
try {
processDispatchResult(req,resp,new ZPSModelAndView("500"));
} catch (Exception e1) {
e1.printStackTrace();
resp.getWriter().write("500 Exception,Detail : " + Arrays.toString(e.getStackTrace()));
}
}
}
@Override
public void init(ServletConfig config) throws ServletException {
//初始化Spring核心IoC容器
applicationContext = new ZPSApplicationContext(config.getInitParameter("contextConfigLocation"));
//初始化SpringMVC的九大组件
initStrategies(applicationContext);
System.out.println("GP Spring framework is init.");
}
//核心处理
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
//完成了对HandlerMapping的封装
//完成了对方法返回值的封装ModelAndView
//通过URL获得一个HandlerMapping
ZPSHandlerMapping handler = getHandler(req);
if(handler == null){
processDispatchResult(req,resp,new ZPSModelAndView("404"));
return;
}
//根据一个HandlerMaping获得一个HandlerAdapter
ZPSHandlerAdapter ha = getHandlerAdapter(handler);
//解析某一个方法的形参和返回值之后,统一封装为ModelAndView对象
ZPSModelAndView mv = ha.handler(req,resp,handler);
//就把ModelAndView变成一个ViewResolver
processDispatchResult(req,resp,mv);
}
private ZPSHandlerAdapter getHandlerAdapter(ZPSHandlerMapping handler) {
if(this.handlerAdapters.isEmpty()){return null;}
return this.handlerAdapters.get(handler);
}
private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, ZPSModelAndView mv) throws Exception {
if(null == mv){return;}
if(this.viewResolvers.isEmpty()){return;}
for (ZPSViewResolver viewResolver : this.viewResolvers) {
ZPSView view = viewResolver.resolveViewName(mv.getViewName());
//直接往浏览器输出
view.render(mv.getModel(),req,resp);
return;
}
}
private ZPSHandlerMapping getHandler(HttpServletRequest req) {
if(this.handlerMappings.isEmpty()){return null;}
String url = req.getRequestURI();
String contextPath = req.getContextPath();
url = url.replaceAll(contextPath,"").replaceAll("/+","/");
for (ZPSHandlerMapping mapping : handlerMappings) {
Matcher matcher = mapping.getPattern().matcher(url);
if(!matcher.matches()){continue;}
return mapping;
}
return null;
}
//这里主要是进行九大主键的初始化
private void initStrategies(ZPSApplicationContext context) {
// //多文件上传的组件
// initMultipartResolver(context);
// //初始化本地语言环境
// initLocaleResolver(context);
// //初始化模板处理器
// initThemeResolver(context);
//c初始化处理器映射器
initHandlerMappings(context);
//初始化参数适配器
initHandlerAdapters(context);
// //初始化异常拦截器
// initHandlerExceptionResolvers(context);
// //初始化视图预处理器
// initRequestToViewNameTranslator(context);
//初始化视图转换器
initViewResolvers(context);
// //FlashMap管理器
// initFlashMapManager(context);
}
//初始化视图转换器
private void initViewResolvers(ZPSApplicationContext context) {
String templateRoot = context.getConfig().getProperty("templateRoot");
String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();
File templateRootDir = new File(templateRootPath);
for (File file : templateRootDir.listFiles()) {
this.viewResolvers.add(new ZPSViewResolver(templateRoot));
}
}
private void initHandlerAdapters(ZPSApplicationContext context) {
for (ZPSHandlerMapping handlerMapping : handlerMappings) {
this.handlerAdapters.put(handlerMapping,new ZPSHandlerAdapter());
}
}
//初始化处理器映射器
private void initHandlerMappings(ZPSApplicationContext context) {
if(this.applicationContext.getBeanDefinitionCount() == 0){ return;}
for (String beanName : this.applicationContext.getBeanDefinitionNames()) {
Object instance = applicationContext.getBean(beanName);
Class<?> clazz = instance.getClass();
if(!clazz.isAnnotationPresent(ZPSController.class)){ continue; }
//相当于提取 class上配置的url
String baseUrl = "";
if(clazz.isAnnotationPresent(ZPSRequestMapping.class)){
ZPSRequestMapping requestMapping = clazz.getAnnotation(ZPSRequestMapping.class);
baseUrl = requestMapping.value();
}
//只获取public的方法
for (Method method : clazz.getMethods()) {
if(!method.isAnnotationPresent(ZPSRequestMapping.class)){continue;}
//提取每个方法上面配置的url
ZPSRequestMapping requestMapping = method.getAnnotation(ZPSRequestMapping.class);
// //demo//query
String regex = ("/" + baseUrl + "/" + requestMapping.value().replaceAll("\\*",".*")).replaceAll("/+","/");
Pattern pattern = Pattern.compile(regex);
//handlerMapping.put(url,method);
handlerMappings.add(new ZPSHandlerMapping(pattern,instance,method));
System.out.println("Mapped : " + regex + "," + method);
}
}
}
}
/**
* @description: 处理器映射器,由相应的url找到相应的handler
* @author: zps
* @create: 2020-05-09 8:30
**/
public class ZPSHandlerMapping {
private Pattern pattern; //URL
private Method method; //对应的Method
private Object controller;//Method对应的实例对象
public ZPSHandlerMapping(Pattern pattern, Object controller, Method method) {
this.pattern = pattern;
this.method = method;
this.controller = controller;
}
public Pattern getPattern() {
return pattern;
}
public void setPattern(Pattern pattern) {
this.pattern = pattern;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public Object getController() {
return controller;
}
public void setController(Object controller) {
this.controller = controller;
}
}
/**
* @description: 处理器适配器,这里主要是完成请求方法与处理方法的参数转换
* @author: zps
* @create: 2020-05-09 9:30
**/
public class ZPSHandlerAdapter {
public ZPSModelAndView handler(HttpServletRequest req, HttpServletResponse resp, ZPSHandlerMapping handler) throws Exception{
//保存形参列表
//将参数名称和参数的位置,这种关系保存起来
Map<String,Integer> paramIndexMapping = new HashMap<String, Integer>();
//通过运行时的状态
Annotation[] [] pa = handler.getMethod().getParameterAnnotations();
for (int i = 0; i < pa.length ; i ++) {
for(Annotation a : pa[i]){
if(a instanceof ZPSRequestParam){
String paramName = ((ZPSRequestParam) a).value();
if(!"".equals(paramName.trim())){
// String value = Arrays.toString(params.get(paramName))
// .replaceAll("\\[|\\]","")
// .replaceAll("\\s+",",");
// paramValues[i] = value;
paramIndexMapping.put(paramName,i);
}
}
}
}
//初始化一下
Class<?> [] paramTypes = handler.getMethod().getParameterTypes();
for (int i = 0; i < paramTypes.length; i++) {
Class<?> paramterType = paramTypes[i];
if(paramterType == HttpServletRequest.class || paramterType == HttpServletResponse.class){
paramIndexMapping.put(paramterType.getName(),i);
}
}
//去拼接实参列表
Map<String,String[]> params = req.getParameterMap();
Object [] paramValues = new Object[paramTypes.length];
for (Map.Entry<String,String[]> param : params.entrySet()) {
String value = Arrays.toString(params.get(param.getKey()))
.replaceAll("\\[|\\]","")
.replaceAll("\\s+",",");
if(!paramIndexMapping.containsKey(param.getKey())){continue;}
int index = paramIndexMapping.get(param.getKey());
//允许自定义的类型转换器Converter,这里硬编码
paramValues[index] = castStringValue(value,paramTypes[index]);
}
if(paramIndexMapping.containsKey(HttpServletRequest.class.getName())){
int index = paramIndexMapping.get(HttpServletRequest.class.getName());
paramValues[index] = req;
}
if(paramIndexMapping.containsKey(HttpServletResponse.class.getName())){
int index = paramIndexMapping.get(HttpServletResponse.class.getName());
paramValues[index] = resp;
}
Object result = handler.getMethod().invoke(handler.getController(),paramValues);
if(result == null || result instanceof Void){return null;}
boolean isModelAndView = handler.getMethod().getReturnType() == ZPSModelAndView.class;
if(isModelAndView){
return (ZPSModelAndView)result;
}
return null;
}
private Object castStringValue(String value, Class<?> paramType) {
if(String.class == paramType){
return value;
}else if(Integer.class == paramType){
return Integer.valueOf(value);
}else if(Double.class == paramType){
return Double.valueOf(value);
}else {
if(value != null){
return value;
}
return null;
}
}
}
/**
* @description: 返回结果封装
* @author: zps
* @create: 2020-05-09 11:30
**/
public class ZPSModelAndView {
private String viewName; //视图名
private Map<String,?> model; //返回结果
public ZPSModelAndView(String viewName, Map<String, ?> model) {
this.viewName = viewName;
this.model = model;
}
public ZPSModelAndView(String viewName) {
this.viewName = viewName;
}
public String getViewName() {
return viewName;
}
public Map<String, ?> getModel() {
return model;
}
}
/**
* @description: 视图解析器
* @author: zps
* @create: 2020-05-09 11:40
**/
public class ZPSViewResolver {
private final String DEFAULT_TEMPLATE_SUFFIX = ".html";
private File tempateRootDir;
public ZPSViewResolver(String templateRoot) {
String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();
tempateRootDir = new File(templateRootPath);
}
public ZPSView resolveViewName(String viewName){
if(null == viewName || "".equals(viewName.trim())){return null;}
viewName = viewName.endsWith(DEFAULT_TEMPLATE_SUFFIX)? viewName : (viewName + DEFAULT_TEMPLATE_SUFFIX);
File templateFile = new File((tempateRootDir.getPath() + "/" + viewName).replaceAll("/+","/"));
return new ZPSView(templateFile);
}
}
/**
* @description: 视图渲染
* @author: zps
* @create: 2020-05-09 12:40
**/
public class ZPSView {
private File viewFile;
public ZPSView(File templateFile) {
this.viewFile = templateFile;
}
//渲染
public void render(Map<String, ?> model, HttpServletRequest req, HttpServletResponse resp) throws Exception {
StringBuffer sb = new StringBuffer();
RandomAccessFile ra = new RandomAccessFile(this.viewFile,"r");
String line = null;
while (null != (line = ra.readLine())){
line = new String(line.getBytes("ISO-8859-1"),"utf-8");
Pattern pattern = Pattern.compile("¥\\{[^\\}]+\\}",Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(line);
while (matcher.find()){
String paramName = matcher.group();
paramName = paramName.replaceAll("¥\\{|\\}","");
Object paramValue = model.get(paramName);
line = matcher.replaceFirst(makeStringForRegExp(paramValue.toString()));
matcher = pattern.matcher(line);
}
sb.append(line);
}
resp.setCharacterEncoding("utf-8");
resp.getWriter().write(sb.toString());
}
//处理特殊字符
public static String makeStringForRegExp(String str) {
return str.replace("\\", "\\\\").replace("*", "\\*")
.replace("+", "\\+").replace("|", "\\|")
.replace("{", "\\{").replace("}", "\\}")
.replace("(", "\\(").replace(")", "\\)")
.replace("^", "\\^").replace("$", "\\$")
.replace("[", "\\[").replace("]", "\\]")
.replace("?", "\\?").replace(",", "\\,")
.replace(".", "\\.").replace("&", "\\&");
}
}
4、总结
结合前面三篇博客,大致可以得出Spring框架的初始化过程和原理:
- 首先就是初始化Spring IoC 容器 (Ioc和DI)
1. 根据配置文件的信息,扫描相关的包,然后将类信息封装成BeanDefinition对象
2. 把BeanDefinition进行缓存,如果是延迟加载,则在进行getBean()时获取相关的BeanDefinition进行实例化
3.当调用getBean()方法时,创建Bean实例,并对创建的实例进行一层包装,封装成BeanWrapper对象进行缓存
4.进行bean的一个属性依赖注入
- Spring MVC启动时调用init()方法,进行九大组件的初始化(这里只介绍三个)
1.初始化处理器映射器,即把处理器的url与handler(也就是我们常用的RequestMapping注解方法)进行绑定
2.初始化处理器适配器,主要是进行handler请求与用户请求中参数的适配
3.初始化视图解析器,主要是对相应视图模板进行初始化
- 初始化后,便可以进行请求的处理了
个人一点小结,若有误,希望指出!!!!