[TOC]
在完全掌握 Spring 系统结构,实现原理,在理解设计模式的基础上,自己动手写一个高仿真版本的 Spring 框架,以达彻理解 Spring 的目的,感受作者创作意图
1. MVC 顶层设计
1.1 GPDispatcherServlet 请求调度
public class GPDispatcherServlet extends HttpServlet {
private GPApplicationContext applicationContext;
private List handlerMappings = new ArrayList();
private Map handlerAdapters = new HashMap();
private List viewResolvers = new ArrayList();
@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 {
//6、委派,根据URL去找到一个对应的Method并通过response返回
try {
doDispatch(req,resp);
} catch (Exception e) {
try {
processDispatchResult(req,resp,new GPModelAndView("500"));
} catch (Exception e1) {
e1.printStackTrace();
resp.getWriter().write("500 Exception,Detail : " + Arrays.toString(e.getStackTrace()));
}
}
}
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
//完成了对HandlerMapping的封装
//完成了对方法返回值的封装ModelAndView
//1、通过URL获得一个HandlerMapping
GPHandlerMapping handler = getHandler(req);
if(handler == null){
processDispatchResult(req,resp,new GPModelAndView("404"));
return;
}
//2、根据一个HandlerMaping获得一个HandlerAdapter
GPHandlerAdapter ha = getHandlerAdapter(handler);
//3、解析某一个方法的形参和返回值之后,统一封装为ModelAndView对象
GPModelAndView mv = ha.handler(req,resp,handler);
// 就把ModelAndView变成一个ViewResolver
processDispatchResult(req,resp,mv);
}
private GPHandlerAdapter getHandlerAdapter(GPHandlerMapping handler) {
if(this.handlerAdapters.isEmpty()){return null;}
return this.handlerAdapters.get(handler);
}
private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, GPModelAndView mv) throws Exception {
if(null == mv){return;}
if(this.viewResolvers.isEmpty()){return;}
for (GPViewResolver viewResolver : this.viewResolvers) {
GPView view = viewResolver.resolveViewName(mv.getViewName());
//直接往浏览器输出
view.render(mv.getModel(),req,resp);
return;
}
}
private GPHandlerMapping getHandler(HttpServletRequest req) {
if(this.handlerMappings.isEmpty()){return null;}
String url = req.getRequestURI();
String contextPath = req.getContextPath();
url = url.replaceAll(contextPath,"").replaceAll("/+","/");
for (GPHandlerMapping mapping : handlerMappings) {
Matcher matcher = mapping.getPattern().matcher(url);
if(!matcher.matches()){continue;}
return mapping;
}
return null;
}
@Override
public void init(ServletConfig config) throws ServletException {
//初始化Spring核心IoC容器
applicationContext = new GPApplicationContext(config.getInitParameter("contextConfigLocation"));
//完成了IoC、DI和MVC部分对接
//初始化九大组件
initStrategies(applicationContext);
System.out.println("GP Spring framework is init.");
}
private void initStrategies(GPApplicationContext context) {
// //多文件上传的组件
// initMultipartResolver(context);
// //初始化本地语言环境
// initLocaleResolver(context);
// //初始化模板处理器
// initThemeResolver(context);
//handlerMapping
initHandlerMappings(context);
//初始化参数适配器
initHandlerAdapters(context);
// //初始化异常拦截器
// initHandlerExceptionResolvers(context);
// //初始化视图预处理器
// initRequestToViewNameTranslator(context);
//初始化视图转换器
initViewResolvers(context);
// //FlashMap管理器
// initFlashMapManager(context);
}
private void initViewResolvers(GPApplicationContext 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 GPViewResolver(templateRoot));
}
}
private void initHandlerAdapters(GPApplicationContext context) {
for (GPHandlerMapping handlerMapping : handlerMappings) {
this.handlerAdapters.put(handlerMapping,new GPHandlerAdapter());
}
}
private void initHandlerMappings(GPApplicationContext 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(GPController.class)){ continue; }
//相当于提取 class上配置的url
String baseUrl = "";
if(clazz.isAnnotationPresent(GPRequestMapping.class)){
GPRequestMapping requestMapping = clazz.getAnnotation(GPRequestMapping.class);
baseUrl = requestMapping.value();
}
//只获取public的方法
for (Method method : clazz.getMethods()) {
if(!method.isAnnotationPresent(GPRequestMapping.class)){continue;}
//提取每个方法上面配置的url
GPRequestMapping requestMapping = method.getAnnotation(GPRequestMapping.class);
// //demo//query
String regex = ("/" + baseUrl + "/" + requestMapping.value().replaceAll("\\*",".*")).replaceAll("/+","/");
Pattern pattern = Pattern.compile(regex);
//handlerMapping.put(url,method);
handlerMappings.add(new GPHandlerMapping(pattern,instance,method));
System.out.println("Mapped : " + regex + "," + method);
}
}
}
}
1.2 GPHandlerMapping 请求映射
public class GPHandlerMapping {
private Pattern pattern; //URL
private Method method; //对应的Method
private Object controller;//Method对应的实例对象
public GPHandlerMapping(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;
}
}
1.3 GPHandlerAdapter 请求方法适配器
public class GPHandlerAdapter {
public GPModelAndView handler(HttpServletRequest req, HttpServletResponse resp, GPHandlerMapping handler) throws Exception{
//保存形参列表
//将参数名称和参数的位置,这种关系保存起来
Map paramIndexMapping = new HashMap();
//通过运行时的状态去拿到你
Annotation[] [] pa = handler.getMethod().getParameterAnnotations();
for (int i = 0; i < pa.length ; i ++) {
for(Annotation a : pa[i]){
if(a instanceof GPRequestParam){
String paramName = ((GPRequestParam) 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);
}
}
//去拼接实参列表
//http://localhost/web/query?name=Tom&Cat
Map params = req.getParameterMap();
Object [] paramValues = new Object[paramTypes.length];
for (Map.Entry 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() == GPModelAndView.class;
if(isModelAndView){
return (GPModelAndView)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;
}
}
}
1.4 GPModelAndView 页面数据封装
public class GPModelAndView {
private String viewName;
private Map model;
public GPModelAndView(String viewName, Map model) {
this.viewName = viewName;
this.model = model;
}
public GPModelAndView(String viewName) {
this.viewName = viewName;
}
public String getViewName() {
return viewName;
}
public Map getModel() {
return model;
}
}
1.5 GPViewResolver 视图解析器
public class GPViewResolver {
private final String DEFAULT_TEMPLATE_SUFFIX = ".html";
private File tempateRootDir;
public GPViewResolver(String templateRoot) {
String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();
tempateRootDir = new File(templateRootPath);
}
public GPView 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 GPView(templateFile);
}
}
1.6 GPView 自定义模板引擎
public class GPView {
private File viewFile;
public GPView(File templateFile) {
this.viewFile = templateFile;
}
public void render(Map 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("&", "\\&");
}
}
2. 业务代码实现
2.1 QueryService 查询业务接口定义
public interface IQueryService {
/**
* 查询
*/
public String query(String name);
}
2.2 QueryService 查询处理业务逻辑
@GPService
@Slf4j
public class QueryService implements IQueryService {
/**
* 查询
*/
public String query(String name) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = sdf.format(new Date());
String json = "{name:\"" + name + "\",time:\"" + time + "\"}";
log.info("这是在业务方法中打印的:" + json);
return json;
}
}
2.3 IModifyService 增、删、改业务接口定义
public interface IModifyService {
/**
* 增加
*/
public String add(String name, String addr);
/**
* 修改
*/
public String edit(Integer id, String name);
/**
* 删除
*/
public String remove(Integer id);
}
2.4 ModifyService 增、删、改业务逻辑实现
@GPService
public class ModifyService implements IModifyService {
/**
* 增加
*/
public String add(String name,String addr) {
return "modifyService add,name=" + name + ",addr=" + addr;
}
/**
* 修改
*/
public String edit(Integer id,String name) {
return "modifyService edit,id=" + id + ",name=" + name;
}
/**
* 删除
*/
public String remove(Integer id) {
return "modifyService id=" + id;
}
}
2.5 MyAction 数据逻辑处理
@GPController
@GPRequestMapping("/web")
public class MyAction {
@GPAutowired IQueryService queryService;
@GPAutowired IModifyService modifyService;
@GPRequestMapping("/query.json")
public GPModelAndView query(HttpServletRequest request, HttpServletResponse response,
@GPRequestParam("name") String name){
String result = queryService.query(name);
return out(response,result);
}
@GPRequestMapping("/add*.json")
public GPModelAndView add(HttpServletRequest request,HttpServletResponse response,
@GPRequestParam("name") String name,@GPRequestParam("addr") String addr){
String result = modifyService.add(name,addr);
return out(response,result);
}
@GPRequestMapping("/remove.json")
public GPModelAndView remove(HttpServletRequest request, HttpServletResponse response,
@GPRequestParam("id") Integer id){
String result = modifyService.remove(id);
return out(response,result);
}
@GPRequestMapping("/edit.json")
public GPModelAndView edit(HttpServletRequest request,HttpServletResponse response,
@GPRequestParam("id") Integer id,
@GPRequestParam("name") String name){
String result = modifyService.edit(id,name);
return out(response,result);
}
private GPModelAndView out(HttpServletResponse resp,String str){
try {
resp.getWriter().write(str);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
2.6 PageAction 页面逻辑处理
@GPController
@GPRequestMapping("/")
public class PageAction {
@GPAutowired
IQueryService queryService;
@GPRequestMapping("/first.html")
public GPModelAndView query(@GPRequestParam("teacher") String teacher){
String result = queryService.query(teacher);
Map model = new HashMap();
model.put("teacher", teacher);
model.put("data", result);
model.put("token", "123456");
return new GPModelAndView("first.html",model);
}
}
3. 定制模板页面
3.1 first.html 动态展示页
咕泡学院SpringMVC模板引擎演示
大家好,我是¥{teacher}老师
欢迎大家一起来探索Spring的世界
Hello,My name is ¥{teacher}
¥{data}
Token值:¥{token}
3.2 404.html 页面未找到提示
页面去火星了
404 Not Found
Copyright@GupaoEDU
3.3 500.html 错误提示页面
服务器好像累了
500 服务器好像有点累了,需要休息一下
Message:¥{detail}
StackTrace:¥{stackTrace}
Copyright@GupaoEDU
4. 运行效果演示
访问http://localhost/web/query.json?name=Tom
返回信息
{name:"Tom", time:"2019-02-11 16:01:10"}
访问http://localhost/web/addTom.json?name=tom&addr=HunanChangsha
返回信息
modifyService
add, name=tom, addr=HunanChangsha
访问http://localhost/web/remove.json?id=66
返回信息
modifyService id=66
访问http://localhost/web/edit.json?id=666&name=Tom
返回信息
modifyService edit, id=666, name=Tom