zoukankan      html  css  js  c++  java
  • 手搓SSM

    相关资料,网上的资料很多,但是文章看不懂,看别人写好的代码比较好理解
    ssm-example
    mysssm

    整个流程和原理

    • 一个入口类,入口类需要在tomcat启动的时候执行
    • 通过扫描文件加把文件取出来,存进classList里
    • 把mapper的注解扫描,创建一个叫mapper的map
    • 收集注解,存进各自的Map里【controllerMap,serviceMap,mapperMap】
    • 生成mybatis代理函数
    • 把serviceMap里的值赋值到controller层的Autowired上
    • 把mapperMap里的值赋值到service层的Autowired上
    • 把RequestMapping拼接成key,value是对应的类方法,存进handlerMap里
    • 重写HttpServlet的doGet和doPost方法

    前提,下载maven依赖

    • servlet,tomocat依赖
    • dom4j,转化xml文件
    • mysql,连接数据库插件
    • lang3,工具包
    • fastjson,json工具

    文件目录

    image.png

    创建入口文件

    // core/springmvc/DispatcherServlet.Java
    
    public class DispatcherServlet extends HttpServlet {
        //收集文件路径的List
        List<String> classList = new ArrayList();
        Map<String, Object> controllerMap = new HashMap<String, Object>();
        Map<String, Object> serviceMap = new HashMap<String, Object>();
        Map<String, Object> mapperMap = new HashMap<String, Object>();
        // key是拼接的全路径,value是方法
        Map<String, Object> handlerMap = new HashMap<String, Object>();
    
        // 核心的入口方法
        // 上面的流程都在这个方法里
        @Override
        public void init() throws ServletException {
            // 扫描文件
            scanPackage("com.pdt.ssm");
            // 收集注解,存进对应的map里
            doInstance();
            // 生成proxy函数
            mybatisDo();
            // 把controller层的Autowired赋值上serviceMap的对应类
            controllerIoc();
            // 把service层的Autowired赋值上mapperMap的对应proxy函数
            serviceIoc();
            // 把RequestMapping拼接好作为key存进map里
            buildUrlMapping();
        }
    }
    

    web设置

    // webapp/web-inf/web.xml
    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>com.pdt.core.springmvc.DispatcherServlet</servlet-class>
        <load-on-startup>0</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    

    创建注解

    • springMvc注解【RequestMapping,RequestParam】
    • spring注解【Controller,Service,Autowired】
    • mybatis注解【Mapper,Select】

    难点是Mybatis
    使用过Mybatis可以知道,不过是xml方式还是注解方式,那个接口的方法并不存在,所以需要自己创建相对应的方法,需要两个用于储存的Bean

    • Function,用于存Select注解的数据
    • MapperBean,用于存Mapeer注解接口里的所有方法
    private void mybatisDo() {
        // mapperMap是在上一个方法里搜集到的mapper注解的类
        for (Map.Entry<String, Object> entry : mapperMap.entrySet()) {
            Class clazz = (Class) entry.getValue();
            MapperBean mapper = new MapperBean();
            // 用来存储方法的list
            List<Function> list = new ArrayList<Function>();
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                int annsLen = method.getAnnotations().length;
                if(annsLen==0){
                    System.out.println("没有mybatis注解");
                }else{
                    // 用来存储一条方法的记录
                    Function function = new Function();
                    // 如果是select注解
                    if (method.isAnnotationPresent(Select.class)) {
                        function.setSqltype("select");
                        function.setSql(method.getAnnotation(Select.class).value());
                        function.setFuncName(method.getName());
                        function.setResultType(method.getReturnType().getName());
                        // 这个是获取泛型,为了自动注入
                        if(method.getGenericReturnType().getTypeName().contains("<")){
                            function.setSetGenericReturnType(method.getGenericReturnType().getTypeName().split("<")[1].split(">")[0]);
                        }
                    }else{
                        // 其他注解
                    }
                    list.add(function);
                    mapper.setInterfaceName(entry.getKey());
                    mapper.setList(list);
                }
            }
            // 核心内容,通过Proxy创建一个新的方法
            Object obj = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] {clazz},
                    new MapperProxy(mapper));
            // 把创建出来的方法替代原本的key的内容
            entry.setValue(obj);
        }
    }
    

    核心中的核心,MapperProxy类

    public class MapperProxy<T> implements InvocationHandler{
         private MapperBean mapperBean;
         public MapperProxy(MapperBean mapperBean) {
    	this.mapperBean = mapperBean;
         }
    
         @Override
         public T invoke(Object proxy, Method method, Object[] args) throws Exception{
    	List<Function> list = mapperBean.getList();
    	if(null != list || 0 != list.size()){
    	    for(Function function : list) {
    	    // 看id是否和接口的方法名一样
     	    // 应该判断参数数量和格式是不是对应的
    	    if(method.getName().equals(function.getFuncName())){
    	       String ann = function.getSqltype();
    	       // 如果是select注解
                   if(ann.equals("select")){
    	          // 判断返回类型,决定执行什么sql方法,具体查看代码
    	       }
    	  }
    	  return null;
         }
    }
    

    给service的Autowired注解赋值,controller同理

    private void serviceIoc() {
        for (Map.Entry<String, Object> entry : serviceMap.entrySet()) {
            Object instance = entry.getValue();
            Class<?> clazz = instance.getClass();
            if (clazz.isAnnotationPresent(Service.class)) {
                //获得所有声明的参数 得到参数数组
                Field[] fields = clazz.getDeclaredFields();
                for (Field field : fields) {
                    if (field.isAnnotationPresent(Autowired.class)) {
                        Autowired autowired = field.getAnnotation(Autowired.class);
                        String key = autowired.value();
                        //打开私有属性的权限修改
                        field.setAccessible(true);
                        try {
                            //给变量重新设值,这个mapperMap取出的就是上面的Proxy生成的方法
                            field.set(instance, mapperMap.get(key));
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    } else {
                        continue;
                    }
                }
            } else {
                continue;
            }
        }
    }
    

    自动注入,这里实现了两处

    • 前端请求自动注入,查看下面的代码
    • 查询数据自动注入,同理
    public static Object handleRequest(String typeStr, HttpServletRequest req) throws Exception{
        // typeStr是全路径的bean位置字符串,例如com.pdt.bean.User
        Object res = Class.forName(typeStr).newInstance();
        Field[] fields = Class.forName(typeStr).getDeclaredFields();
        for (Field field : fields) {
            String fieldType = field.getType().getName();
            String fieldName = field.getName();
            //打开私有属性的权限修改
            field.setAccessible(true);
            field.set(res,req.getParameter(fieldName));
        }
        // 这个返回的值就是已经存好了值的
        return res;
    }
    

    重写响应方法

    // DispatcherServlet.Java
    
    @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 {
        //获取请求路径
        String url = req.getRequestURI();
        String context = req.getContextPath();
        String path = url.replace(context, "");
        Method method= (Method) handlerMap.get(path);
        // 从controllerMap取出对应key的类方法
        Object controller= null;
        //根据key去拿实例
        if(path.equals("/")){
            controller= controllerMap.get("/");
        }else{
            controller= controllerMap.get("/"+path.split("/")[1]);
        }
        try {
            if(controller==null || method==null){
                resp.setContentType("text/html; charset=utf-8");
                resp.getWriter().println("<h1>404</h1>");
            }else{
                Class<?> returnType = method.getReturnType();
                if(returnType == null){
                    System.out.println("Controller层不能无返回");
                }else{
                    // 参数处理
                    String returnTypeSimpleName = returnType.getSimpleName();
                    List parameList = RequsetParameter.handle(req,resp,method.getParameters());
                    // 最后返回处理
                    ReturnParameter.handle(returnTypeSimpleName,parameList,method,controller,resp);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    

    最后返回处理,根据返回类型给前端不同的响应

    public class ReturnParameter {
        public static void handle(String returnTypeSimpleName, List parameList, Method method, Object controller, HttpServletResponse resp)throws Exception{
            if(returnTypeSimpleName.equals("String")){
                // 这里应该判断有没有body注解的,没有就返回页面,有返回字符串
                String res = (String) method.invoke(controller,parameList.toArray());
                resp.setContentType("text/plain;charset=UTF-8");
                resp.getWriter().println(res);
            }else if(returnTypeSimpleName.equals("List")){
                List list = (List) method.invoke(controller,parameList.toArray());
                String res = JSON.toJSONString(list);
                resp.setContentType("application/json;charset=UTF-8");
                resp.getWriter().println(res);
            }else if(returnTypeSimpleName.equals("Map")){
                Map map = (Map) method.invoke(controller,parameList.toArray());
                String res = JSON.toJSONString(map);
                resp.setContentType("application/json;charset=UTF-8");
                resp.getWriter().println(res);
            }
        }
    }
    

    到这里,接受请求,处理请求,返回响应整个流程都完成了,代码上传到github上

  • 相关阅读:
    CAP原理、一致性模型、BASE理论和ACID特性
    MyBatis双数据源配置
    MySQL中间件Atlas安装及使用
    MySQL主从切换
    MySQL定时逻辑备份
    MySQL主从搭建
    zabbix监控nginx
    SVN Files 的值“ < < < < < < < .mine”无效。路径中具有非法字符。
    ie8下table的colspan属性与max-with属性的显示错乱问题
    MVC 自定义异常错误页面处理
  • 原文地址:https://www.cnblogs.com/pengdt/p/12362513.html
Copyright © 2011-2022 走看看