知音专栏

程序员的出路

写程序时该追求什么,什么是次要的?

如何准备Java初级和高级的技术面试

上一篇:手写spring+springmvc+mybatis框架篇【springIOC容器】

题外话:技术交流,欢迎加入QQ群:696209224 。广告勿扰!

先放一张网上的很好的一张原理图

图片出自,这篇博客原理也写的很清晰明了。我的实现也是借鉴了这张图

https://www.cnblogs.com/xiaoxi/p/6164383.html

先说一下我的实现思路:

1 在MyDispatcherServlet中的servlet初始化的时候,绑定标有@MyController注解类下面的@MyRequestMappign的value值和对应的方法。绑定的方式是放在map集合中。这个map集合就是上图说的handlerMapping,返回的handler也就是一组键值对。

2 找到对应的方法后,反射执行方法,在方法中创建一个modelandview对象,model也就是我们说的数据域,view返回的是一个视图名称,也就是我们说的视图域,当然,我这里只有jsp,spring做的很复杂。支持多种类型。最后所谓的渲染,也就是将这个数据域中的数据会添加到request请求中,然后转发。返回客户端。

3 绑定参数模型这一部分略为复杂。在下面讲解

下面是MyDispatcherServlet

这个servlet的作用就是接收用户请求,然后派发注意标红处bingdingMethodParamters方法,这个方法实现了参数的绑定。

package spring.servlet;import lombok.extern.slf4j.Slf4j;import spring.factory.InitBean;import spring.springmvc.Binding;import spring.springmvc.Handler;import spring.springmvc.MyModelAndView;import spring.springmvc.ViewResolver;import javax.servlet.ServletContext;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.List;import java.util.Map;import java.util.Set;import static spring.springmvc.BindingRequestAndModel.bindingRequestAndModel;/*** Created by Xiao Liang on 2018/6/27.*/@WebServlet(name = "MyDispatcherServlet")@Slf4jpublic class MyDispatcherServlet extends HttpServlet {   /**    * 初始化servlet,将bean容器和HandlerMapping放到servlet的全局变量中    */   @Override   public void init() {       InitBean initBean = new InitBean();       initBean.initBeans();       //根据bean容器中注册的bean获得HandlerMapping       Map<String, Method> bindingRequestMapping = Handler.bindingRequestMapping(initBean.beanContainerMap);       ServletContext servletContext = this.getServletContext();       servletContext.setAttribute("beanContainerMap", initBean.beanContainerMap);       servletContext.setAttribute("bindingRequestMapping", bindingRequestMapping);   }   protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {       try {           doDispatch(request, response);       } catch (Exception e) {           log.error("控制器处理异常");           e.printStackTrace();       }   }   protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {       doPost(request, response);   }   //接收到请求后转发到相应的方法上   private void doDispatch(HttpServletRequest request, HttpServletResponse response) throws IOException, InvocationTargetException, IllegalAccessException, InstantiationException {       ServletContext servletContext = this.getServletContext();       //获取扫描controller注解后url和方法绑定的mapping,也就是handlerMapping       Map<String, Method> bindingRequestMapping =               (Map<String, Method>) servletContext.getAttribute("bindingRequestMapping");       //获取实例化的bean容器       Map<String, Object> beanContainerMap = (Map<String, Object>) servletContext.getAttribute("beanContainerMap");       String url = request.getServletPath();       Set<Map.Entry<String, Method>> entries = bindingRequestMapping.entrySet();       List<Object> resultParameters = Binding.bingdingMethodParamters(bindingRequestMapping, request);       for (Map.Entry<String, Method> entry :               entries) {           if (url.equals(entry.getKey())) {               Method method = entry.getValue();               Class<?> returnType = method.getReturnType();                   //如果返回值是MyModelAndView,开始绑定               if ("MyModelAndView".equals(returnType.getSimpleName())){                   Object object = beanContainerMap.get(method.getDeclaringClass().getName());                   //获取springmvc.xml中配置的视图解析器                   ViewResolver viewResolver = (ViewResolver) beanContainerMap.get("spring.springmvc.ViewResolver");                   String prefix = viewResolver.getPrefix();                   String suffix = viewResolver.getSuffix();                   MyModelAndView myModelAndView = (MyModelAndView) method.invoke(object, resultParameters.toArray());                   //将request和model中的数据绑定,也就是渲染视图                   bindingRequestAndModel(myModelAndView,request);                   String returnViewName = myModelAndView.getView();                   //返回的路径                   String resultAddress = prefix + returnViewName + suffix;                   try {                       request.getRequestDispatcher(resultAddress).forward(request,response);                   } catch (ServletException e) {                       e.printStackTrace();                   }               }           }       }   }}

首先是绑定方法和url,是Handler类,用如下对象绑定

Map<String, Method> handlerMapping = new ConcurrentHashMap<>();
package spring.springmvc;import lombok.extern.slf4j.Slf4j;import spring.Utils.AnnotationUtils;import spring.annotation.MyController;import spring.annotation.MyRequestMapping;import spring.exception.springmvcException;import java.lang.annotation.Annotation;import java.lang.reflect.Method;import java.util.Map;import java.util.Set;import java.util.concurrent.ConcurrentHashMap;/*** @ClassName Handler* @Description  遍历bean容器,在有controller注解的类中有requestmapping扫描的方法,则将方法和url和方法绑定* @Data 2018/7/3* @Author xiao liang*/@Slf4jpublic class Handler {   public static Map<String, Method> bindingRequestMapping(Map<String, Object> beanContainerMap){       Map<String, Method> handlerMapping = new ConcurrentHashMap<>();       if (beanContainerMap != null){           Set<Map.Entry<String, Object>> entries = beanContainerMap.entrySet();           for (Map.Entry<String, Object> entry :                   entries) {               Class aClass = entry.getValue().getClass();               Annotation annotation = aClass.getAnnotation(MyController.class);               Method[] methods = aClass.getMethods();               if (!AnnotationUtils.isEmpty(annotation) && methods != null){                   for (Method method:                           aClass.getMethods()) {                       MyRequestMapping requestMappingAnnotation = method.getAnnotation(MyRequestMapping.class);                       if (!AnnotationUtils.isEmpty(requestMappingAnnotation)){                           String key = requestMappingAnnotation.value();                           handlerMapping.put(key,method);                       }                   }               }           }       }       else{           throw new springmvcException("实例化bean异常,没有找到容器");       }       return handlerMapping;   }}

参数绑定支持

  1. @MyRequestMapping(用来绑定简单数据类型)

  2. @MyModelAndAttribute(绑定实体类)

  3. 不写注解,直接写实体类。

下面先贴一下这一部分的结构关系图

这里用多态的设计思想,对于bindingParamter方法写了两种实现,方便大家自行扩展

package spring.springmvc;import spring.Utils.AnnotationUtils;import spring.Utils.isBasicTypeUtils;import spring.annotation.MyModelAttribute;import spring.annotation.MyRequstParam;import spring.exception.springmvcException;import javax.servlet.http.HttpServletRequest;import java.lang.reflect.Method;import java.lang.reflect.Parameter;import java.util.ArrayList;import java.util.List;import java.util.Map;import java.util.Set;/*** @ClassName Binding* @Description* @Data 2018/7/4* @Author xiao liang*/public class Binding {   public static  List<Object> bingdingMethodParamters(Map<String, Method> bindingRequestMapping, HttpServletRequest request) {       List<Object> resultParameters  = new ArrayList<>();       Set<Map.Entry<String, Method>> entries = bindingRequestMapping.entrySet();       for (Map.Entry<String, Method> entry :               entries) {           Method method = entry.getValue();           Parameter[] parameters = method.getParameters();           for (Parameter parameter :                   parameters) {       //遍历每个参数,如果参数存在注解,将这个参数添加到resultParameters中               if (!AnnotationUtils.isEmpty(parameter.getAnnotations())){                   Object resultParameter = null;                   try {                       resultParameter = bingdingEachParamter(parameter, request);                   } catch (IllegalAccessException e) {                       e.printStackTrace();                       throw new springmvcException("绑定参数异常");                   } catch (NoSuchMethodException e) {                       e.printStackTrace();                       throw new springmvcException("绑定参数异常");                   } catch (InstantiationException e) {                       e.printStackTrace();                       throw new springmvcException("绑定参数异常");                   }                   resultParameters.add(resultParameter);               }           }       }       return resultParameters;   }private static Object bingdingEachParamter(Parameter parameter, HttpServletRequest request) throws IllegalAccessException, NoSuchMethodException, InstantiationException {        //如果注解是MyRequstParam,则用BindingByMyRequstParam来执行装配        if (!AnnotationUtils.isEmpty(parameter.getAnnotation(MyRequstParam.class))){            BindingParamter bindingParamter = new BindingByMyRequstParam();            Object resultParameter = bindingParamter.bindingParamter(parameter, request);            return resultParameter;        }        //如果注解是MyModelAttribute,则用BindingByMyModelAttribute来执行装配        else if (!AnnotationUtils.isEmpty(parameter.getAnnotation(MyModelAttribute.class))){            BindingParamter bindingParamter = new BindingByMyModelAttribute();            Object resultParameter = bindingParamter.bindingParamter(parameter,request);            return resultParameter;        }        //在没有注解的时候,自动识别,如果是基本数据类型用MyRequstParam装配,如果是用户自定义类型用MyModelAttribute装配        else if(parameter.getAnnotations() == null || parameter.getAnnotations().length ==0){            boolean flag = isBasicTypeUtils.isBasicType(parameter.getType().getSimpleName());            if (flag){                BindingParamter bindingParamter = new BindingByMyRequstParam();                Object resultParameter = bindingParamter.bindingParamter(parameter, request);                return resultParameter;            }            else{                BindingParamter bindingParamter = new BindingByMyModelAttribute();                Object resultParameter = bindingParamter.bindingParamter(parameter,request);                return resultParameter;            }        }        return null;    }}

下面是接口BindingParamter 和两个实现类BindingByMyModelAttribute和BindingByMyRequstParam

package spring.springmvc;import javax.servlet.http.HttpServletRequest;import java.lang.reflect.Parameter;/*** @ClassName BindingRoles* @Description* @Data 2018/7/4* @Author xiao liang*/public interface BindingParamter {    Object bindingParamter(Parameter parameter, HttpServletRequest request) throws IllegalAccessException, InstantiationException, NoSuchMethodException;}
package spring.springmvc;import spring.Utils.StringUtils;import spring.annotation.MyRequstParam;import spring.exception.springmvcException;import javax.servlet.http.HttpServletRequest;import java.lang.reflect.Parameter;/*** @ClassName BindingByMyRequstParam* @Description 参数注解是MyMyRequstParam时,绑定数据的类* @Data 2018/7/4* @Author xiao liang*/public class BindingByMyRequstParam implements BindingParamter {   @Override   public Object bindingParamter(Parameter parameter, HttpServletRequest request) {       MyRequstParam myRequstParam = parameter.getAnnotation(MyRequstParam.class);       //获得注解的value值       String MyRequstParamValue = myRequstParam.value();       //获得参数的类名       String parameterType = parameter.getType().getSimpleName();       String parameter1 = request.getParameter(MyRequstParamValue);       if (StringUtils.isEmpty(parameter1)) {           throw new springmvcException("绑定参数异常");       }       //parameter1赋值       if (parameterType.equals("String")) {           return parameter1;       } else if (parameterType.equals("Integer") || parameterType.equals("int")) {         Integer binddingParameter =  Integer.valueOf(parameter1);         return binddingParameter;       }       return null;   }}
package spring.springmvc;import lombok.extern.slf4j.Slf4j;import spring.Utils.AnnotationUtils;import spring.Utils.ConvertUtis;import spring.Utils.GetMethodName;import spring.Utils.StringUtils;import spring.annotation.MyModelAttribute;import spring.exception.springmvcException;import javax.servlet.http.HttpServletRequest;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.lang.reflect.Parameter;/*** @ClassName BindingByMyModelAttribute* @Description 参数注解是MyModelAttribute时,绑定数据的类* @Data 2018/7/4* @Author xiao liang*/@Slf4jpublic class BindingByMyModelAttribute implements   BindingParamter {   @Override   public Object bindingParamter(Parameter parameter, HttpServletRequest request) throws IllegalAccessException, InstantiationException, NoSuchMethodException {       MyModelAttribute myModelAttribute = parameter.getAnnotation(MyModelAttribute.class);       //获得参数的类名       Class<?> aClass = parameter.getType();       if (!AnnotationUtils.isEmpty(myModelAttribute)){           if (!aClass.getSimpleName().equals(myModelAttribute.value())){               throw new springmvcException("实体类绑定异常,请重新检查");           }       }       Field[] fields = aClass.getDeclaredFields();       Object object = aClass.newInstance();       //遍历每个属性,用set注入将值注入到对象中       for (Field field :               fields) {           //获得用户传来的值           String parameter1 = request.getParameter(field.getName());           if (!StringUtils.isEmpty(parameter1)){               //将用户传过来的值转换成对应的参数类型               Object setObject = ConvertUtis.convert(field.getType().getSimpleName(),parameter1);               String methodName = GetMethodName.getSetMethodNameByField(field.getName());               Method method = aClass.getMethod(methodName, field.getType());               try {                   //反射set注入                   method.invoke(object,setObject);               } catch (InvocationTargetException e) {                   log.error("{}属性赋值异常",field.getName());                   e.printStackTrace();               }           }       }       //返回对注入值后的对象       return object;   }}

绑定完参数,就该返回ModelAndView了,

package spring.springmvc;import lombok.Data;/*** @ClassName MyModelAndView* @Description* @Data 2018/7/4* @Author xiao liang*/@Datapublic class MyModelAndView {   private String view;   private MyModelMap modelMap;   public MyModelAndView(String view) {       this.view = view;   }}

view是视图名称,还有viewResolver,用来接收xml文件中定义的前缀和后缀。modelMap是数据域,最后渲染的时候要绑定到request中。

package spring.springmvc;import lombok.Data;/*** @ClassName ViewResolver* @Description 视图解析器 前缀和后缀* @Data 2018/7/4* @Author xiao liang*/@Datapublic class ViewResolver {   private String prefix = "";   private String suffix = "";}

最后的渲染类

package spring.springmvc;import javax.servlet.http.HttpServletRequest;import java.util.Map;import java.util.Set;/*** @ClassName BindingRequestAndModel* @Description* @Data 2018/7/6* @Author xiao liang*/public class BindingRequestAndModel {   //遍历modelMap,然后将model中的数据绑定到requst中   public static void bindingRequestAndModel(MyModelAndView myModelAndView, HttpServletRequest request) {       MyModelMap myModelMap = myModelAndView.getModelMap();       if (!myModelMap.isEmpty()){           Set<Map.Entry<String, Object>> entries1 = myModelMap.entrySet();           for (Map.Entry<String, Object> entryMap :                   entries1) {               String key = entryMap.getKey();               Object value = entryMap.getValue();               request.setAttribute(key,value);           }       }   }}

至此,最后在MyDispatcherServlet中用转发操作将试图返回。

request.getRequestDispatcher(resultAddress).forward(request,response);

我将此项目上传到了github,需要的童鞋可以自行下载。

https://github.com/836219171/MySSM

©著作权归作者所有:来自51CTO博客作者mob604756f6460e的原创作品,如需转载,请注明出处,否则将追究法律责任

更多相关文章

  1. 01-Vue_常用指令
  2. 就是要让你彻底学会 @Bean 注解
  3. 孙卫琴的《精通JPA与Hibernate》的读书笔记:用@ManyToMany注解映
  4. 孙卫琴的《精通JPA与Hibernate》的读书笔记:用@OneToOne注解映射
  5. Linux 运维需要掌握的 17 个实用技巧
  6. SpringMVC_Day01
  7. Vue的v-bind,v-on, v-model,v-if,v-for,计算属性和侦听器属性 --
  8. springboot研究五:springboot整合rabbitmq
  9. spring-boot+mybatis开发实战:如何在spring-boot中使用myabtis持

随机推荐

  1. MySQL5.7免安装版配置详细教程
  2. 如果有多个作者[重复],我怎么能阻止PHP显
  3. mysql利用st_distance函数查询附近的点的
  4. SQL按数字排序并保持分组
  5. 在命令行到处MYSQL数据到EXCEL表
  6. init-connectMysql对用户操作加审计功能
  7. mysql-zrm备份工具实现全备+增备策略
  8. 避免写出不走索引的SQL, MySQL
  9. 有没有一种方法可以在不破坏外键依赖关系
  10. mysql user表root 用户修改权限后出现无