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中,并指定要初始化的参数

 1 <!-- 核心servlet -->
 2     <servlet>
 3         <servlet-name>dispatcherServlet</servlet-name>
 4         <servlet-class>org.wl.test.spring.mvc.DispatcherServlet</servlet-class>
 5         <!-- 初始化参数 -->
 6         <init-param>
 7             <param-name>contextConfigLocation</param-name>
 8             <param-value>classpath:application.properties</param-value>
 9         </init-param>
10         <!-- 启动时加载 -->
11         <load-on-startup>0</load-on-startup>
12     </servlet>
13     <servlet-mapping>
14         <servlet-name>dispatcherServlet</servlet-name>
15         <url-pattern>/</url-pattern>
16     </servlet-mapping>

  核心控制器,在web容器启动时执行init方法进行文件的初始化

  1 public class DispatcherServlet extends HttpServlet {
  2
  3     private List<HandlerMapping> handlerMappingList = new ArrayList<HandlerMapping>();
  4
  5     private Map<HandlerMapping, HandlerAdapter> adapterMap = new HashMap<>();
  6
  7     @Override
  8     public void init(ServletConfig config) throws ServletException {
  9         // web.xml 配置核心servlet 获取配置的信息
 10         String configFile = config.getInitParameter("contextConfigLocation");
 11         //定义一个当前上下文对象,实现基础包的扫描、IOC、DI
 12         ApplicationContext context = new ApplicationContext(configFile.replace("classpath:", ""));
 13         //获取扫描到的有Controller注解的类
 14         List<Object> controllerList = context.getControllerList();
 15         //初始化HandlerMapping
 16         initHandlerMapping(controllerList);
 17         //初始化HandlerAdapter
 18         initHandlerAdapter();
 19     }
 20
 21
 22     private void initHandlerAdapter() {
 23         if (handlerMappingList.size() == 0) {
 24             return;
 25         }
 26
 27         handlerMappingList.forEach(handlerMapping -> {
 28             Method method = handlerMapping.getMethod();
 29             //方法的参数  <参数索引,参数名字>
 30             Map<Integer, String> paramMap = new HashMap<>();
 31
 32             //使用了注解参数
 33             Annotation[][] annos = method.getParameterAnnotations();
 34             if(annos.length > 0){
 35                 for(int i=0; i<annos.length; i++){
 36                     for(Annotation anno : annos[i]){
 37                         if(anno instanceof RequestParam){
 38                             RequestParam requestParam = (RequestParam) anno;
 39                             String paramName = requestParam.value();
 40
 41                             paramMap.put(i, paramName);
 42                         }
 43                     }
 44                 }
 45             }
 46             //直接用的servlet参数,如HttpServletRequest
 47             Class<?>[] paramTypes = method.getParameterTypes();
 48             if(paramTypes.length > 0){
 49                 for(int i=0; i<paramTypes.length; i++){
 50                     Class<?> typeClass = paramTypes[i];
 51                     if (typeClass == HttpServletRequest.class || typeClass == HttpServletResponse.class) {
 52                         String paramName = typeClass.getName();
 53
 54                         paramMap.put(i, paramName);
 55                     }
 56                 }
 57             }
 58
 59             HandlerAdapter handlerAdapter = new HandlerAdapter(paramMap);
 60             adapterMap.put(handlerMapping, handlerAdapter);
 61         });
 62     }
 63
 64     /**
 65      * 完成请求方法与请求处理实例的映射关系
 66      * @param controllerList
 67      */
 68     private void initHandlerMapping(List<Object> controllerList) {
 69         if(controllerList.size() == 0){
 70             return;
 71         }
 72
 73         controllerList.forEach(controllerObj -> {
 74             //类上的请求路径
 75             String classRequestUrl = "";
 76             if (controllerObj.getClass().isAnnotationPresent(RequestMapping.class)) {
 77                 RequestMapping classRequestMapping = controllerObj.getClass().getAnnotation(RequestMapping.class);
 78                 if(classRequestMapping != null){
 79                     classRequestUrl += urlHandler(classRequestMapping.value());
 80                 }
 81             }
 82             //方法上的请求路径
 83             Method[] methods = controllerObj.getClass().getDeclaredMethods();
 84             if(methods.length > 0){
 85                 for(int i=0; i<methods.length; i++){
 86                     String methodRequestUrl = "";
 87                     Method method = methods[i];
 88                     //必须是public修饰的方法
 89                     if(method.getModifiers() == Modifier.PUBLIC){
 90                         if (method.isAnnotationPresent(RequestMapping.class)) {
 91                             RequestMapping methodRequestMapping = method.getAnnotation(RequestMapping.class);
 92                             if(methodRequestMapping != null){
 93                                 methodRequestUrl += urlHandler(methodRequestMapping.value());
 94                             }
 95
 96                             String requestUrl = classRequestUrl + methodRequestUrl;
 97
 98                             HandlerMapping handlerMapping = new HandlerMapping();
 99                             handlerMapping.setMethod(method);
100                             handlerMapping.setUrl(requestUrl);
101
102                             handlerMapping.setControllerInstance(controllerObj);
103                             handlerMappingList.add(handlerMapping);
104                         }
105                     }
106
107                 }
108             }
109
110         });
111
112     }
113
114     /**
115      * url处理
116      * @param url
117      * @return
118      */
119     public String urlHandler( String url){
120         if(!url.startsWith("/")){
121             url = "/" + url;
122         }
123         if(url.endsWith("/")){
124             url = url.substring(0, url.length() - 1);
125         }
126         return url;
127     }
128
129     @Override
130     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
131         this.doPost(req, resp);
132     }
133
134     @Override
135     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
136         doDispatcher(req, resp);
137     }
138
139     /**
140      * 请求处理
141      * @param req
142      * @param resp
143      */
144     private void doDispatcher(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
145         req.setCharacterEncoding("utf-8");
146         resp.setContentType("text/html;charset=UTF-8");
147
148         String contextUrl = req.getContextPath();
149         String requestUrl = req.getRequestURI();
150
151         String url = requestUrl.replace(contextUrl, "");
152         HandlerMapping handlerMapping = null;
153         for(int i=0; i<handlerMappingList.size(); i++){
154             if(url.equals(handlerMappingList.get(i).getUrl())){
155                 handlerMapping = handlerMappingList.get(i);
156                 break;
157             }
158         }
159         if(handlerMapping == null){
160             resp.getWriter().write("404, 未知的请求!");
161         }else{
162             HandlerAdapter adapter = adapterMap.get(handlerMapping);
163             try {
164                 Object result = adapter.handler(req, resp, handlerMapping);
165
166                 viewResolve(req, resp, result);
167             } catch (Exception e) {
168                 e.printStackTrace();
169                 resp.getWriter().write("500, 服务器发生异常!");
170             }
171         }
172
173     }
174
175     /**
176      * 视图解析 返回
177      * @param result
178      */
179     private void viewResolve(HttpServletRequest request, HttpServletResponse response, Object result) throws Exception{
180         if (result.getClass() == ModelAndView.class) {
181             ModelAndView mv = (ModelAndView) result;
182             String view = mv.getViewName();
183             Map<String, Object> dataMap = mv.getData();
184             if(dataMap.size() > 0){
185                 for(String key : dataMap.keySet()){
186                     request.setAttribute(key, dataMap.get(key));
187                 }
188             }
189             request.getRequestDispatcher(view).forward(request, response);
190         }
191     }
192
193 }

  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和要执行的方法和方法所在对象实例,构建请求与请求处理的映射关系

1 public class HandlerMapping {
2
3     private String url;
4     private Method method;
5     private Object controllerInstance;
6
7     //此处省去getter和setter
8 }

  处理适配器HandlerAdapter,每一个请求映射都有一个请求处理适配器来完成请求的处理(handler)

 1 public class HandlerAdapter {
 2
 3     private Map<Integer, String> paramMap;
 4
 5     public HandlerAdapter(Map<Integer, String> paramMap){
 6         this.paramMap = paramMap;
 7     }
 8
 9     public Object handler(HttpServletRequest request, HttpServletResponse response, HandlerMapping handlerMapping) throws Exception {
10         Method method = handlerMapping.getMethod();
11         Object classInstance = handlerMapping.getControllerInstance();
12
13         int paramNum = method.getParameterCount();
14         Object[] paramObj = new Object[paramNum];
15         for(int i=0; i<paramNum; i++){
16             String paramName = paramMap.get(i);
17             if(paramName.equals(HttpServletRequest.class.getName())){
18                 paramObj[i] = request;
19             }else if(paramName.equals(HttpServletResponse.class.getName())){
20                 paramObj[i] = response;
21             } else {
22                 paramObj[i] = request.getParameter(paramName);
23             }
24         }
25         Object result = method.invoke(classInstance, paramObj);
26         return result;
27     }
28
29
30     public Map<Integer, String> getParamMap() {
31         return paramMap;
32     }
33
34     public void setParamMap(Map<Integer, String> paramMap) {
35         this.paramMap = paramMap;
36     }
37 }

  处理结果ModelAndView,用来存放当前请求要返回的视图和数据

 1 public class ModelAndView {
 2
 3     private String viewName;
 4     private Map<String, Object> data = new HashMap<>();
 5
 6     public ModelAndView(String viewName) {
 7         this.viewName = viewName;
 8     }
 9
10     public void addAttribute(String name, Object value){
11         data.put(name, value);
12     }
13
14     //此处省略getter和setter
15 }

  视图展示返回的结果

<html>
<head>
    <title>Title</title>
</head>
<body>
<h3>hello mvc...</h3>
<hr/>
用 户 信 息:<%=request.getAttribute("user") %>
</body>
</html>

  浏览器端显示信息

以上是模拟实现springmvc的主要代码,实现的源代码我已经上传在github,感兴趣的朋友可以访问

https://github.com/wlzq/spring

原文地址:https://www.cnblogs.com/love-wzy/p/10203655.html

时间: 2024-11-05 14:48:11

spring之mvc原理分析及简单模拟实现的相关文章

Jquery源码分析与简单模拟实现

前言 最近学习了一下jQuery源码,顺便总结一下,版本:v2.0.3 主要是通过简单模拟实现jQuery的封装/调用.选择器.类级别扩展等.加深对js/Jquery的理解. 正文 先来说问题: 1.jQuery为什么能使用$的方式调用,$是什么.$()又是什么.链式调用如何实现的 2.jQuery的类级别的扩展内部是怎样实现的,方法级别的扩展有是怎样实现的,$.fn又是什么 3.jQuery选择器是如何执行的,又是如何将结果包装并返回的 带着这些问题,我们进行jquery的模拟实现,文章下方有

Spring Web MVC 原理学习(二)

接着上一篇博客,这一篇,我们根据一个简单的demo,来对SpringMVC的原理再次学习: 一.配置web.xml 我们新建一个web项目,在web.xml配置网站的统一访问点,把Dispatcher Servlet配置在里面,进行全局流程控制: <!-- 前台控制器的配置 ,配置统一访问点,进行全局的流程控制--> <servlet> <servlet-name>chapter2</servlet-name> <servlet-class>or

Spring学习笔记——Spring依赖注入原理分析

我们知道Spring的依赖注入有四种方式,分别是get/set方法注入.构造器注入.静态工厂方法注入.实例工厂方法注入 下面我们先分析下这几种注入方式 1.get/set方法注入 public class SpringAction { //注入对象springDao private SpringDao springDao; //一定要写被注入对象的set方法 public void setSpringDao(SpringDao springDao) { this.springDao = spri

spring容器启动原理分析1

在项目的web.xml中配置 1 <listener> 2 <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 3 </listener> 此配置为spring容器加载入口,因为其javax.servlet.ServletContextListener接口. 下面代码为ServletContextListener的源码: public i

ajax小demo---CORS的原理分析及简单使用

一. CORS:CORS(Cross-Origin Resourse Sharing),跨源资源共享,是一份浏览器技术的规范,提供了Web服务从不同网域传来沙盒脚本的方法,以避开浏览器的同源策略,是JSONP模式的现代版. 二. CORS的原理: CORS定义一种跨域访问的机制,可以让AJAX实现跨域访问.CORS 允许一个域上的网络应用向另一个域提交跨域 AJAX 请求.实现此功能非常简单,只需由服务器发送一个响应标头即可,利用自定义的HTTP头部让浏览器与服务器进行沟通. 例如:当服务器端是

(hdu step 8.1.6)士兵队列训练问题(数据结构,简单模拟——第一次每2个去掉1个,第二次每3个去掉1个.知道队伍中的人数&lt;=3,输出剩下的人 )

题目: 士兵队列训练问题 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 56 Accepted Submission(s): 37   Problem Description 某部队进行新兵队列训练,将新兵从一开始按顺序依次编号,并排成一行横队,训练的规则如下:从头开始一至二报数,凡报到二的出列,剩下的向小序号方向靠拢,再从头开始进行一至三

Android官方架构组件:Lifecycle详解&amp;迪士尼彩乐园网站架设原理分析

我们先将重要的这些类挑选出来: LifecycleObserver接口( Lifecycle观察者):实现该接口的类,通过注解的方式,可以通过被LifecycleOwner类的addObserver(LifecycleObserver o)方法注册,被注册后,LifecycleObserver便可以观察到LifecycleOwner的生命周期事件. LifecycleOwner接口(Lifecycle持有者):实现该接口的类持有生命周期(Lifecycle对象),该接口的生命周期(Lifecyc

JavaWeb学习总结(四十九)——简单模拟Sping MVC

在Spring MVC中,将一个普通的java类标注上Controller注解之后,再将类中的方法使用RequestMapping注解标注,那么这个普通的java类就够处理Web请求,示例代码如下: 1 /** 2 * 使用Controller注解标注LoginUI类 3 */ 4 @Controller 5 public class LoginUI { 6 7 //使用RequestMapping注解指明forward1方法的访问路径 8 @RequestMapping("LoginUI/Lo

菜鸟学SSH(十五)——简单模拟Hibernate实现原理

之前写了Spring的实现原理,今天我们接着聊聊Hibernate的实现原理,这篇文章仅仅是简单的模拟一下Hibernate的原理,主要是模拟了一下Hibernate的Session类.好了,废话不多说,先看看我们的代码: package com.tgb.hibernate; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.DriverManager; import java.sql.Pre