spring之mvc原理分析及简单模拟实现
在之前的一篇博客中已经简单的实现了spring的IOC和DI功能,本文将在之前的基础上实现mvc功能。
一 什么是MVC
MVC简单的说就是一种软件实现的设计模式,将整个系统进行分层,M(model 数据模型,业务逻辑层) 、V(view 视图层)、C(controller 控制器调度),实现应用程序的分层开发。实现原理如下图:
主要执行步骤:
1 用户在发起request请求给前端控制器;
2 控制器接收到请求后,经过一系统的过滤器,找到对应的请求处理映射;
3 根据请求映射获得请求处理适配器;
4 适配器将对请求进行处理并将处理结果(ModelAndView)返回给前端控制器;
5 前端处理器将处理结果交给视图解析器解析;
6 视图解析器将解析的结果返回给控制器;
7 控制器将结果返回给用户。
二 简单模拟实现
创建一个核心控制器(DispatcherServlet)继承HttpServlet,配置在web.xml中,并指定要初始化的参数
<!-- 核心servlet -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.wl.test.spring.mvc.DispatcherServlet</servlet-class>
<!-- 初始化参数 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application.properties</param-value>
</init-param>
<!-- 启动时加载 -->
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
核心控制器,在web容器启动时执行init方法进行文件的初始化
public class DispatcherServlet extends HttpServlet { private List<HandlerMapping> handlerMappingList = new ArrayList<HandlerMapping>(); private Map<HandlerMapping, HandlerAdapter> adapterMap = new HashMap<>(); @Override
public void init(ServletConfig config) throws ServletException {
// web.xml 配置核心servlet 获取配置的信息
String configFile = config.getInitParameter("contextConfigLocation");
//定义一个当前上下文对象,实现基础包的扫描、IOC、DI
ApplicationContext context = new ApplicationContext(configFile.replace("classpath:", ""));
//获取扫描到的有Controller注解的类
List<Object> controllerList = context.getControllerList();
//初始化HandlerMapping
initHandlerMapping(controllerList);
//初始化HandlerAdapter
initHandlerAdapter();
} private void initHandlerAdapter() {
if (handlerMappingList.size() == 0) {
return;
} handlerMappingList.forEach(handlerMapping -> {
Method method = handlerMapping.getMethod();
//方法的参数 <参数索引,参数名字>
Map<Integer, String> paramMap = new HashMap<>(); //使用了注解参数
Annotation[][] annos = method.getParameterAnnotations();
if(annos.length > 0){
for(int i=0; i<annos.length; i++){
for(Annotation anno : annos[i]){
if(anno instanceof RequestParam){
RequestParam requestParam = (RequestParam) anno;
String paramName = requestParam.value(); paramMap.put(i, paramName);
}
}
}
}
//直接用的servlet参数,如HttpServletRequest
Class<?>[] paramTypes = method.getParameterTypes();
if(paramTypes.length > 0){
for(int i=0; i<paramTypes.length; i++){
Class<?> typeClass = paramTypes[i];
if (typeClass == HttpServletRequest.class || typeClass == HttpServletResponse.class) {
String paramName = typeClass.getName(); paramMap.put(i, paramName);
}
}
} HandlerAdapter handlerAdapter = new HandlerAdapter(paramMap);
adapterMap.put(handlerMapping, handlerAdapter);
});
} /**
* 完成请求方法与请求处理实例的映射关系
* @param controllerList
*/
private void initHandlerMapping(List<Object> controllerList) {
if(controllerList.size() == 0){
return;
} controllerList.forEach(controllerObj -> {
//类上的请求路径
String classRequestUrl = "";
if (controllerObj.getClass().isAnnotationPresent(RequestMapping.class)) {
RequestMapping classRequestMapping = controllerObj.getClass().getAnnotation(RequestMapping.class);
if(classRequestMapping != null){
classRequestUrl += urlHandler(classRequestMapping.value());
}
}
//方法上的请求路径
Method[] methods = controllerObj.getClass().getDeclaredMethods();
if(methods.length > 0){
for(int i=0; i<methods.length; i++){
String methodRequestUrl = "";
Method method = methods[i];
//必须是public修饰的方法
if(method.getModifiers() == Modifier.PUBLIC){
if (method.isAnnotationPresent(RequestMapping.class)) {
RequestMapping methodRequestMapping = method.getAnnotation(RequestMapping.class);
if(methodRequestMapping != null){
methodRequestUrl += urlHandler(methodRequestMapping.value());
} String requestUrl = classRequestUrl + methodRequestUrl; HandlerMapping handlerMapping = new HandlerMapping();
handlerMapping.setMethod(method);
handlerMapping.setUrl(requestUrl); handlerMapping.setControllerInstance(controllerObj);
handlerMappingList.add(handlerMapping);
}
} }
} }); } /**
* url处理
* @param url
* @return
*/
public String urlHandler( String url){
if(!url.startsWith("/")){
url = "/" + url;
}
if(url.endsWith("/")){
url = url.substring(0, url.length() - 1);
}
return url;
} @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 {
doDispatcher(req, resp);
} /**
* 请求处理
* @param req
* @param resp
*/
private void doDispatcher(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=UTF-8"); String contextUrl = req.getContextPath();
String requestUrl = req.getRequestURI(); String url = requestUrl.replace(contextUrl, "");
HandlerMapping handlerMapping = null;
for(int i=0; i<handlerMappingList.size(); i++){
if(url.equals(handlerMappingList.get(i).getUrl())){
handlerMapping = handlerMappingList.get(i);
break;
}
}
if(handlerMapping == null){
resp.getWriter().write("404, 未知的请求!");
}else{
HandlerAdapter adapter = adapterMap.get(handlerMapping);
try {
Object result = adapter.handler(req, resp, handlerMapping); viewResolve(req, resp, result);
} catch (Exception e) {
e.printStackTrace();
resp.getWriter().write("500, 服务器发生异常!");
}
} } /**
* 视图解析 返回
* @param result
*/
private void viewResolve(HttpServletRequest request, HttpServletResponse response, Object result) throws Exception{
if (result.getClass() == ModelAndView.class) {
ModelAndView mv = (ModelAndView) result;
String view = mv.getViewName();
Map<String, Object> dataMap = mv.getData();
if(dataMap.size() > 0){
for(String key : dataMap.keySet()){
request.setAttribute(key, dataMap.get(key));
}
}
request.getRequestDispatcher(view).forward(request, response);
}
} }
ApplicationContext的具体实现如下,主要实现对执行资源文件的扫描,并完成IOC和DI
public class ApplicationContext { /**
* 配置文件
*/
private static String PROPERTIES_FILE = ""; /**
* 初始化一个集合,存放扫描到的class对象
*/
private List<Class<?>> classList = Collections.synchronizedList(new ArrayList<>()); /**
* 初始化map 存放别名与对象实例
*/
private Map<String, Object> aliasInstanceMap = new HashMap<>(); public ApplicationContext(String fileName) {
PROPERTIES_FILE = fileName;
try {
String basePackage = getBasePackage(PROPERTIES_FILE); buildAliasInstanceMap(basePackage); doAutowired(); } catch (Exception e) {
e.printStackTrace();
}
} /**
* 完成别名与实例的映射
*/
public void buildAliasInstanceMap(String basePackage) throws Exception { scanClasses(basePackage); if(classList.size() == 0){return;} for(Class<?> clazz : classList){
if (clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(Service.class)
|| clazz.isAnnotationPresent(Autowired.class)) {
String alias = getAlias(clazz);
Object obj = aliasInstanceMap.get(alias); //如果别名实例映射关系已经存在,则给出提示
if(obj != null){
throw new Exception("alias is exist!");
}else{
aliasInstanceMap.put(alias, clazz.newInstance());
}
}
} System.out.println(aliasInstanceMap);
} /**
* 属性对象的注入
*/
public void doAutowired(){
if (aliasInstanceMap.size() == 0) {
return;
} aliasInstanceMap.forEach((k, v)->{ Field[] fields = v.getClass().getDeclaredFields(); for(Field field : fields){
if (field.isAnnotationPresent(Autowired.class)) {
String alias = ""; Autowired autowired = field.getAnnotation(Autowired.class);
if(autowired != null){
//注入的对象是接口时,由于不知道接口有几个实现类,所以就必须在Autowired或者Qualifier上指定要注解的具体的实现类
if(!"".equals(autowired.value())){
alias = autowired.value();
}else{
Qualifier qualifier = field.getAnnotation(Qualifier.class);
if(qualifier != null){
alias = qualifier.value();
}
}
} if ("".equals(alias)) {
alias = getAlias(field.getType());
} Object instance = null;
if(!"".equals(alias)){
instance = aliasInstanceMap.get(alias);
} field.setAccessible(true); try {
field.set(v, instance);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} }
}); } /**
* 获取对象的别名,如果注解中配置了别名,别使用配置的别名,否则默认使用类名首字母小写
* @param clazz
* @return
*/
public String getAlias(Class<?> clazz){
String alias = "";
Controller controller = clazz.getAnnotation(Controller.class);
if(controller != null){
alias = controller.value();
}
Service service = clazz.getAnnotation(Service.class);
if (service != null) {
alias = service.value();
}
Autowired autowired = clazz.getAnnotation(Autowired.class);
if(autowired != null){
alias = autowired.value();
} //注解中没有配置别名
if("".equals(alias)){
String simpleName = clazz.getSimpleName();
alias = simpleName.substring(0, 1).toLowerCase() + simpleName.substring(1);
}
return alias;
} /**
* 跟据基础包名读取包及子包中的类对象
* @param basePackage
*/
public void scanClasses(String basePackage){
if(basePackage == null || "".equals(basePackage)){return;} doScan(basePackage);
System.out.println(classList);
} private void doScan(String basePackage) {
String path = basePackage.replaceAll("\\.","/");
URL url = this.getClass().getClassLoader().getResource(path);
File file = new File(url.getFile());
file.listFiles(new FileFilter() {
@Override
public boolean accept(File childFile) {
String fileName = childFile.getName();
if(childFile.isDirectory()){
//当前文件是目录,递归 扫描下级子目录下的class文件
doScan(basePackage + "." + fileName);
}else{
if(fileName.endsWith(".class")){
String className = basePackage + "." + fileName.replace(".class", "");
try {
Class<?> clazz = this.getClass().getClassLoader().loadClass(className);
classList.add(clazz);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
return false;
}
});
} /**
* 从配置的属性文件中读取要扫描的包
* @return
*/
public String getBasePackage(String fileName) throws IOException {
String basePackage;
Properties prop = new Properties();
InputStream in = this.getClass().getClassLoader().getResourceAsStream(fileName);
prop.load(in);
basePackage = prop.getProperty("basePackage");
return basePackage;
} /**
* 根据beanName 获取
* @param beanName
* @return
*/
public Object getBean(String beanName){
return aliasInstanceMap.get(beanName);
} /**
* 获取所有标注了controller的注解
* @return
*/
public List<Object> getControllerList(){
List<Object> controllerList = new ArrayList<>();
if(aliasInstanceMap.size() > 0) {
aliasInstanceMap.values().forEach(obj -> {
if(obj.getClass().isAnnotationPresent(Controller.class)){
controllerList.add(obj);
}
});
}
return controllerList;
} public static void main(String[] args) throws Exception {
String fileName = "application.properties";
ApplicationContext context = new ApplicationContext(fileName);
String basePackage = context.getBasePackage(PROPERTIES_FILE); context.buildAliasInstanceMap(basePackage); context.doAutowired();
//测试
UserController controller = (UserController) context.getBean("userController");
controller.save();
} }
请求映射HandlerMapping,用来存放请求url和要执行的方法和方法所在对象实例,构建请求与请求处理的映射关系
public class HandlerMapping { private String url;
private Method method;
private Object controllerInstance; //此处省去getter和setter
}
处理适配器HandlerAdapter,每一个请求映射都有一个请求处理适配器来完成请求的处理(handler)
public class HandlerAdapter { private Map<Integer, String> paramMap; public HandlerAdapter(Map<Integer, String> paramMap){
this.paramMap = paramMap;
} public Object handler(HttpServletRequest request, HttpServletResponse response, HandlerMapping handlerMapping) throws Exception {
Method method = handlerMapping.getMethod();
Object classInstance = handlerMapping.getControllerInstance(); int paramNum = method.getParameterCount();
Object[] paramObj = new Object[paramNum];
for(int i=0; i<paramNum; i++){
String paramName = paramMap.get(i);
if(paramName.equals(HttpServletRequest.class.getName())){
paramObj[i] = request;
}else if(paramName.equals(HttpServletResponse.class.getName())){
paramObj[i] = response;
} else {
paramObj[i] = request.getParameter(paramName);
}
}
Object result = method.invoke(classInstance, paramObj);
return result;
} public Map<Integer, String> getParamMap() {
return paramMap;
} public void setParamMap(Map<Integer, String> paramMap) {
this.paramMap = paramMap;
}
}
处理结果ModelAndView,用来存放当前请求要返回的视图和数据
public class ModelAndView { private String viewName;
private Map<String, Object> data = new HashMap<>(); public ModelAndView(String viewName) {
this.viewName = viewName;
} public void addAttribute(String name, Object value){
data.put(name, value);
} //此处省略getter和setter
}
视图展示返回的结果
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>hello mvc...</h3>
<hr/>
用 户 信 息:<%=request.getAttribute("user") %>
</body>
</html>
浏览器端显示信息
以上是模拟实现springmvc的主要代码,实现的源代码我已经上传在github,感兴趣的朋友可以访问
https://github.com/wlzq/spring
spring之mvc原理分析及简单模拟实现的更多相关文章
- Shiro框架 (原理分析与简单实现)
Shiro框架(原理分析与简单实现) 有兴趣的同学也可以阅读我之前分享的:Java权限管理(授权与认证)CRM权限管理 (PS : 这篇博客里面的实现方式没有使用框架,完全是手写的授权与认证,可以 ...
- Spring依赖注入原理分析
在分析原理之前我们先回顾下依赖注入的概念: 我们常提起的依赖注入(Dependency Injection)和控制反转(Inversion of Control)是同一个概念.具体含义是:当某个角色( ...
- tomcat原理分析与简单实现
tomcat原理分析与简单实现 https://blog.csdn.net/u014795347/article/details/52328221 2016年08月26日 14:48:18 卫卫羊习习 ...
- vue2.0 双向绑定原理分析及简单实现
Vue用了有一段时间了,每当有人问到Vue双向绑定是怎么回事的时候,总是不能给大家解释的很清楚,正好最近有时间把它梳理一下,让自己理解的更清楚,下次有人问我的时候,可以侃侃而谈. 一.首先介绍Obje ...
- Jquery源码分析与简单模拟实现
前言 最近学习了一下jQuery源码,顺便总结一下,版本:v2.0.3 主要是通过简单模拟实现jQuery的封装/调用.选择器.类级别扩展等.加深对js/Jquery的理解. 正文 先来说问题: 1. ...
- Spring Mvc原理分析(一)
Servlet生命周期了解 Servlet的生命(周期)是由容器(eg:Tomcat)管理的,换句话说,Servlet程序员不能用代码控制其生命. 加载和实例化:时机取决于web.xml的定义,如果有 ...
- Spring Boot 启动原理分析
https://yq.aliyun.com/articles/6056 转 在spring boot里,很吸引人的一个特性是可以直接把应用打包成为一个jar/war,然后这个jar/war是可以直接启 ...
- Spring Web MVC 原理学习(下)
接着上一篇博客,这一篇.我们依据一个简单的demo,来对SpringMVC的原理再次学习: 一.配置web.xml 我们新建一个web项目.在 ...
- Spring + Mybatis 集成原理分析
由于我之前是写在wizNote上的,迁移过来比较浪费时间,所以,这里我直接贴个图片,PDF文件我上传到百度云盘了,需要的可直接下载. 地址:https://pan.baidu.com/s/12ZJmw ...
随机推荐
- application cache 应用缓存
这些应用还是要自己实现一遍,否则真不知道哪里会出问题. 客户端: <!DOCTYPE html> <html manifest = 'demo.appcache'> <h ...
- java实现自动生成四则运算
Github项目链接:https://github.com/shoulder01/Fouroperation.git 一.项目相关要求 1. 使用 -n 参数控制生成题目的个数(实现) 2.使用 -r ...
- web.xml中Filter,Listener,Servlet的区别
一.Servlet Servlet是基本的服务端程序,他来自接口Servlet,接口中有方法service.而Servlet的一个重要实现类,则是tomcat服务器的核心,那就是HttpServlet ...
- 微软儿童编程技术,kodu(酷豆)为儿童创造一个游戏世界
受微软青年星火项目组的邀请,我和微软项目组的朋友参加了一场针对儿童编程的技术指导. 儿童和编程,本来是两个完全不靠边的词.在大家的常规思维中,编程一直是软件开发人员的事情,是高科技类型的工作,高不可攀 ...
- BCP IN示例
参考:http://www.cnblogs.com/qanholas/archive/2011/07/05/2098616.html bcp {dbtable | query} {in | out | ...
- asp.net—单例模式
一.单例模式是什么? 定义:确保一个类仅仅能产生一个实例,并且提供一个全局访问点来获取该实例. 二.单例模式怎么用? class SingleCase { public string Name{get ...
- lnmp下thinkphp 500错误指南
先在php.ini打开报错,display_errors: on: 如果是open_basedir的问题,修改nginx的配置文件fastcgi.conf 将fastcgi_param PHP_ADM ...
- 廖雪峰Python学习笔记——类和实例
Class MyList(list): __metaclass__ = ListMetaclass #它表示在创建MyList这个类时,必须通过 ListMetaclass这个元类的LIstMetac ...
- 内置装饰器二:@property
property 装饰器的作用 property 装饰器将方法包装成属性,将私有属性公有化,此属性只能被读取.相当于实现get方法的对象 class People: def __init__(self ...
- 使用pipreqs生成项目依赖
作用 导出当前项目的环境依赖 使用 # 安装 pip3 install pipreqs # 导出项目环境依赖 pipreqs ./ # 如果是Windows系统,会报编码错误 (UnicodeDeco ...