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 ...
随机推荐
- x13 vs md5
x13 vs md5 阅读: 评论: 作者:Rybby 日期: 来源:rybby.com 最近在设计巴巴变时想对用户设计的节点模块添加锁定功能,比如你的网站可以让用户发表文章或评论,而你想让用 ...
- 【转】C#发送Email邮件
转自:http://hi.baidu.com/bluesky_cn/item/8bb060ace834c53f020a4df2 下面用到的邮件账号和密码都不是真实的,需要测试就换成自己的邮件账号. 需 ...
- 微软儿童编程技术,kodu(酷豆)为儿童创造一个游戏世界
受微软青年星火项目组的邀请,我和微软项目组的朋友参加了一场针对儿童编程的技术指导. 儿童和编程,本来是两个完全不靠边的词.在大家的常规思维中,编程一直是软件开发人员的事情,是高科技类型的工作,高不可攀 ...
- LeetCode145:Binary Tree Postorder Traversal
题目: Given a binary tree, return the postorder traversal of its nodes' values. For example: Given bin ...
- MvcPager分页控件使用注意事项!
初学MVC,做了个单页面应用,需要显示多个分页,并无刷新更新. 找到了MvcPager控件,非常好用,在使用ajax过程中遇到很多问题.慢慢调试和杨老师(MvcPaegr作者)请教,总于都解决了. 首 ...
- 在ASP.NET MVC中使用区域来方便管理controller和view
在ASP.NET MVC中使用区域来方便管理controller和view 在mvc架构中,一般在controllers和views中写所有控制器和视图, 太多控制器时候,为了方便管理,想要将关于pe ...
- .net core webapi+EF Core
.net core webapi+EF Core 一.描述: EF Core必须下载.net core2.0版本 Micorsoft.EntityFrameworkCore:EF框架的核心包Micor ...
- RabbitMQ之消息持久化(转)
原文地址 https://blog.csdn.net/u013256816/article/details/60875666/ 消息的可靠性是RabbitMQ的一大特色,那么RabbitMQ是如何保证 ...
- pageadmin CMS网站制作教程:栏目单页内容如何修改
pageadmin CMS网站制作教程:栏目单页内容如何修改 一般情况下,如公司介绍,联系方式等介绍内页面都属于单页,单页内容可以直接在栏目设置界面进行修改,如下 1.对栏目单页内容进行设置,登录后台 ...
- Tomcat 7.x/8.x 优化
一.优化Connector http://www.aikaiyuan.com/8466.html tomcat的运行模式有3种 1)bio 默认的模式,性能非常低下,没有经过任何优化处理和支持. 2) ...