码迷,mamicode.com
首页 > 编程语言 > 详细

spring之mvc原理分析及简单模拟实现

时间:2019-01-01 00:16:24      阅读:190      评论:0      收藏:0      [点我收藏+]

标签:color   ice   adapter   bean   源代码   dispatch   replace   break   patch   

  在之前的一篇博客中已经简单的实现了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

 

spring之mvc原理分析及简单模拟实现

标签:color   ice   adapter   bean   源代码   dispatch   replace   break   patch   

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!