mini版自定义SpringMVC框架及实现

mini版自定义SpringMVC框架及实现

自定义springmvc 框架的实现其实就是自定义DispatcherServle类
有以下几个步骤:
1、加载配置文件 springmvc.properties
2、扫描相关类 扫描注解
3、初始化bean对象 实现ioc容器 基于注解
4、实现依赖注入
5、构造HandlerMapping 处理器映射器 将配置好的url和method建立映射关系
6、配置web.xm
7、请求

LwlDispatcherServlet

public class LwlDispatcherServlet extends HttpServlet {

    private Properties properties = new Properties();
    //缓存扫描到的类的权限定类名
    private List<String> classNames = new ArrayList<>();

    private Map<String,Object> ioc = new HashMap<>();
    //建立url与method之间的映射关系
    private List<Handler> handlerMapping = new ArrayList<>();
    //访问权限控制
    private Map<Handler,List<String>> securityMap = new HashMap<>();
    @Override
    public void init(ServletConfig config) throws ServletException {
        //加载配置文件  springmvc.properties
        String contextConfigLocation = config.getInitParameter("ContextConfigLocation");

        doLoadConfig(contextConfigLocation);

        //扫描相关类  扫描注解
        doScan(properties.getProperty("scanPackage"));

        //初始化bean对象   实现ioc容器  基于注解
        doInstance();

        //实现依赖注入
        doAutowired();

        //构造HandlerMapping 处理器映射器  将配置好的url和method建立映射关系
        initHandlerMapping();
        System.out.println(" mvc 初始化完成....");
        //等待请求

    }

}

1、加载配置文件 springmvc.properties

加载配置文件

/**
     * 加载配置文件
     * @param contextConfigLocation
     */
    private void doLoadConfig(String contextConfigLocation) {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
            properties.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

2、扫描相关类 扫描注解

 /**
     * 扫描相关类  扫描注解
     *
     * 找到传入的包路径的真实的磁盘路径
     *
     * @param scanPackage
     */
    private void doScan(String scanPackage) {
        // 找到传入的包路径的真实的磁盘路径
        String scanPackagePath = Thread.currentThread().getContextClassLoader().getResource("").getPath() + scanPackage.replaceAll("\\.", "/");

        File pack = new File(scanPackagePath);
        File[] files = pack.listFiles();
        for (File file : files) {
            if (file.isDirectory()){
                //递归
                doScan(scanPackage+"."+file.getName());

            }else if (file.getName().endsWith(".class")){
                String className = scanPackage + "." + file.getName().replaceAll(".class", "");
                classNames.add(className);
            }
        }
    }

3、初始化bean对象 实现ioc容器 基于注解

 /**
     * 初始化bean对象   实现ioc容器  基于注解
     * 基于classNames缓存的类全限定类名  以及反射技术完成对象的创建和管理
     */
    private void doInstance()  {
        if (classNames.size() == 0){
            return;
        }else {

            try {
                for (int i = 0; i < classNames.size(); i++) {
                    String className = classNames.get(i);

                    Class<?> aClass = Class.forName(className);


                    if (aClass.isAnnotationPresent(LwlController.class)){
                        //controller 的注解时不做过多的处理,不用获取value  就用类的首字母小写作为id  存在 ioc中
                        String simpleName = aClass.getSimpleName();
                        String lowerFistSimpleName = lowerFirst(simpleName);
                        Object o = aClass.newInstance();
                        ioc.put(lowerFistSimpleName,o);
                    }else if (aClass.isAnnotationPresent(LwlService.class)){
                        //获取value
                        LwlService annotation = aClass.getAnnotation(LwlService.class);
                        String beanName = annotation.value();
                        if (!"".equals(beanName)){
                            //如果指定了value的值  就以此为ID
                            ioc.put(beanName,aClass.newInstance());
                        }else {
                            //如果没有指定就有类名首字母小写
                            beanName = lowerFirst(aClass.getSimpleName());
                            ioc.put(beanName,aClass.getNestHost());
                        }

                        //service 层有接口的话  面向接口开发,此时的接口名为ID  放一份对象到IOC中 便于后期根据接口类型注入
                        Class<?>[] interfaces = aClass.getInterfaces();
                        for (int j = 0; j < interfaces.length; j++) {
                            Class<?> anInterface = interfaces[j];
                            //以接口的全限定类名 作为ID
                            ioc.put(anInterface.getName(),aClass.newInstance());
                        }

                    }else {
                        continue;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }


    }



首字母小写方法

// 首字母小写方法
    public String lowerFirst(String str) {
        char[] chars = str.toCharArray();
        if('A' <= chars[0] && chars[0] <= 'Z') {
            chars[0] += 32;
        }
        return String.valueOf(chars);
    }

4、实现依赖注入

/**
     * 实现依赖注入
     */
    private void doAutowired() {
        if (ioc.isEmpty()){
            return;
        }else {
            //不为空时  进行依赖处理
            //遍历对象中的字段,查看对象中的字段是否有 @LwlAutowired 注解  如果有就要维护依赖关系

           for (Map.Entry<String,Object> entry:ioc.entrySet()){
               //获取bean对象的字段
               Field[] declaredField = entry.getValue().getClass().getDeclaredFields();
               for (int i = 0; i < declaredField.length; i++) {
                   Field field = declaredField[i];
                   if (!field.isAnnotationPresent(LwlAutowired.class)){
                       continue;
                   }else {
                       LwlAutowired annotation = field.getAnnotation(LwlAutowired.class);

                       String beanName = annotation.value(); //需要注入的bean的id
                       if ("".equals(beanName)){
                            //没有配置具体的id
                           beanName = field.getType().getName();
                       }
                       //开启赋值
                       field.setAccessible(true);
                       try {
                           field.set(entry.getValue(),ioc.get(beanName));
                       } catch (IllegalAccessException e) {
                           e.printStackTrace();
                       }

                   }

               }
           }


        }

    }

5、构造HandlerMapping 处理器映射器 将配置好的url和method建立映射关系

  /**
     * 构造HandlerMapping 处理器映射器  将配置好的url和method建立映射关系
     *
     * 最关键的一个环节
     * 将url 与method 建立关联
     *
     * 只处理 HttpServletResponse  HttpServletRequest  string
     *
     */

    private void initHandlerMapping() {
        if (ioc.isEmpty()){
            return;
        }

        for (Map.Entry<String,Object> entry:ioc.entrySet()){
            //获取当前IOC的对象class类型
            Class<?> aClass = entry.getValue().getClass();


            if (!aClass.isAnnotationPresent(LwlController.class)){
                continue;
            }
            String baseUrl ="";
            if (aClass.isAnnotationPresent(LwlController.class)){
                LwlRequestMapping annotation = aClass.getAnnotation(LwlRequestMapping.class);
                 baseUrl = annotation.value(); //类上面的地址前缀
            }

            //获取方法上的地址
            Method[] methods = aClass.getMethods();
            for (int i = 0; i < methods.length; i++) {
                Method method = methods[i];
                if (!method.isAnnotationPresent(LwlRequestMapping.class)){
                    continue;
                }
                if (method.isAnnotationPresent(LwlRequestMapping.class)){
                    LwlRequestMapping annotation = method.getAnnotation(LwlRequestMapping.class);
                    String methodUrl = annotation.value();
                    String url = baseUrl + methodUrl;

                    //将method的所有信息及url封装在handler
                    Handler handler = new Handler(entry.getValue(),method,Pattern.compile(url));

                    Parameter[] parameters = method.getParameters();
                    for (int i1 = 0; i1 < parameters.length; i1++) {
                        Parameter parameter = parameters[i1];

                        if (parameter.getType() == HttpServletResponse.class || parameter.getType() == HttpServletRequest.class){
                            //如果是 HttpServletResponse 和HttpServletRequest对象  那么参数名称写HttpServletResponse HttpServletRequest

                            handler.getParamIndexMapping().put(parameter.getType().getSimpleName(),i1);

                        }else {
                            handler.getParamIndexMapping().put(parameter.getName(),i1);
                        }

                    }

                    //判断可以是否有权限进入该方法
                    if(aClass.isAnnotationPresent(Security.class) && method.isAnnotationPresent(Security.class)) {
                        //判断是否存在Security类
                        Security controllerSecurity = aClass.getAnnotation(Security.class);
                        String[] controllerUserNames = controllerSecurity.value();
                        List<String> controllerUserNamesList = Arrays.asList(controllerUserNames);

                        Security handlerSecurity = method.getAnnotation(Security.class);
                        String[] handlerUserNames = handlerSecurity.value();
                        List<String> handlerUserNameList = Arrays.asList(handlerUserNames);


                        controllerUserNamesList = new ArrayList<>(controllerUserNamesList);
                        handlerUserNameList = new ArrayList<>(handlerUserNameList);
                        controllerUserNamesList.addAll(handlerUserNameList);

                        securityMap.put( handler, controllerUserNamesList);
                    }else if(aClass.isAnnotationPresent(Security.class)) {
                        Security controllerSecurity = aClass.getAnnotation(Security.class);
                        String[] controllerUserNames = controllerSecurity.value();
                        List<String> controllerUserNamesList = Arrays.asList(controllerUserNames);
                        securityMap.put( handler, controllerUserNamesList);
                    }else if(method.isAnnotationPresent(Security.class)) {
                        Security handlerSecurity = method.getAnnotation(Security.class);
                        String[] handlerUserNames = handlerSecurity.value();
                        List<String> handlerUserNameList = Arrays.asList(handlerUserNames);
                        securityMap.put( handler, handlerUserNameList);
                    }


                    //建立url与method之间的映射关系
                    handlerMapping.add(handler);
                }


            }
            
        }

    }

6、配置web.xm

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <servlet>
    <servlet-name>lwlmvc</servlet-name>
    <servlet-class>com.lwl.mvcframework.servlet.LwlDispatcherServlet</servlet-class>
    <init-param>
      <param-name>ContextConfigLocation</param-name>
      <param-value>springmvc.properties</param-value>
    </init-param>
  </servlet>

  <servlet-mapping>
    <servlet-name>lwlmvc</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
</web-app>

7、请求

com.lwl.mvcframework.servlet.LwlDispatcherServlet#doGet

 //接受处理请求
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

com.lwl.mvcframework.servlet.LwlDispatcherServlet#doPost

 @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

//        //处理请求  根据url找到对应的method方法进行调用
//        String requestURI = req.getRequestURI();
//        //获取到反射的方法
//        Method method = handlerMapping.get(requestURI);
//        //反射调用  传入对象
//        method.invoke()



        //根据uri 获取到能够处理当前请求的handler
        Handler handler = getHandler(req);
        if (handler == null){
            resp.getWriter().write("404 not found");
            return;
        }

        // 安全认证
        List<String> usernameList = securityMap.get(handler);
        // 不为空说明需要认证,那么不包含的话就return
        String username = req.getParameter("username");
        if(usernameList != null && !usernameList.contains(username)) {
            System.out.println("username:"+username+"无权限访问");
            resp.getWriter().write(username+"sorry No permission");
            return;
        }

        System.out.println("username:"+username+"有权限访问,欢迎您");
        //参数绑定
        //获取所有类型的参数类型数组  这个数组的长度 就是我们在调用方法时 用的参数长度
        Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();

        //根据上述数组长度创建一个新的数组
        Object[] paraValues  = new  Object[parameterTypes.length];

        //以下就是为了想参数数组中赋值  而且还得保证参数的顺序和方法中的形参顺序一致

        Map<String, String[]> parameterMap = req.getParameterMap();

        //遍历request所有参数  填充除request  和response 以外的参数
        for (Map.Entry<String,String[]> param : parameterMap.entrySet()){
            String value = StringUtils.join(param.getValue(), ",");

            //如果参数和方法中的参数匹配上了,数据填充
            if (!handler.getParamIndexMapping().containsKey(param.getKey())){
                continue;
            }
            //方法形参确实有该参数  找到它的索引位置  把对应的参数值放入paramValues
            Integer index = handler.getParamIndexMapping().get(param.getKey());
            paraValues[index] = value;//把前台传递的参数值 填充到对应位置

        }

        Integer requestIndex = handler.getParamIndexMapping().get(HttpServletRequest.class.getSimpleName());
        paraValues[requestIndex] = req;

        Integer responseIndex = handler.getParamIndexMapping().get(HttpServletResponse.class.getSimpleName());
        paraValues[responseIndex] = resp;
        // 调用handler 和method属性
        try {
            handler.getMethod().invoke(handler.getController(),paraValues);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

com.lwl.mvcframework.servlet.LwlDispatcherServlet#getHandler

 private Handler getHandler(HttpServletRequest request){
        if (handlerMapping.isEmpty()){
            return null;
        }
        //处理请求  根据url找到对应的method方法进行调用
        String requestURI = request.getRequestURI();
        for (Handler handler : handlerMapping) {
            Matcher matcher = handler.getPattern().matcher(requestURI);
            if (!matcher.matches()){
                continue;
            }
            return handler;
        }
        return null;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时小浅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值