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.初始化視圖解析器,主要是對相應視圖模板進行初始化
- 初始化後,便可以進行請求的處理了
個人一點小結,若有誤,希望指出!!!!