当前位置: 首页 > news >正文

手写springmvc框架

为什么80%的码农都做不了架构师?>>>   hot3.png

一、定义注解

package com.ayo.annotation;

import java.lang.annotation.*;

/**
 * 模拟springmvc@Controller注解
 */
@Documented //JAVADOC
@Target(ElementType.TYPE)   //作用于类上
@Retention(RetentionPolicy.RUNTIME) //限制Annotation的生命周期,我这里自定义的注解显然需要运行时保留
public @interface Controller {
    /**
     * 作用于该类上的注解有一个value属性,说白了就是Controller的名称
     * @return
     */
    public String value();
}
package com.ayo.annotation;

import java.lang.annotation.*;

/**
 * @Qualifier提供依赖注入
 */
@Documented
@Target(ElementType.FIELD)  //作用于字段上,实现注入
@Retention(RetentionPolicy.RUNTIME)
public @interface Qualifier {

    public String value();
}
package com.ayo.annotation;

import java.lang.annotation.*;

/**
 * dao层注解
 */
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Repository {
    public String value();
}
package com.ayo.annotation;

import java.lang.annotation.*;

/**
 * RequestMapping提供url地址处理映射
 */
@Documented
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
    public String value();
}
package com.ayo.annotation;

import java.lang.annotation.*;

/**
 * 业务层注解
 */
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
    public String value();
}

二、写一个DispatcherServlet

package com.ayo.servlet;

import com.ayo.annotation.*;
import com.ayo.controller.UserController;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@WebServlet(name = "dispatcherServlet", urlPatterns = "/*", loadOnStartup = 1, initParams = {@WebInitParam(name = "base-package", value = "com.ayo")})
public class DispatcherServlet extends HttpServlet{

    //扫描的基包
    private String basePackage = "";

    //基包下面所有的带包路径权限定类名
    private List<String> packageNames = new ArrayList<String>();

    //注解实例化,注解上的名称:实例化对象
    private Map<String, Object> instanceMap = new HashMap<String, Object>();

    //带包路径的权限定名称:注解上的名称
    private Map<String, Object> nameMap = new HashMap<String, Object>();

    //url地址和方法的映射关系,springmvc就是方法调用链
    private Map<String, Method> urlMethodMap = new HashMap<String, Method>();

    //Method和权限定类名映射关系, 主要是为了通过method找到该方法的对象利用反射执行
    private Map<Method, String> methodPackageMap = new HashMap<Method, String>();

    @Override
    public void init(ServletConfig config) throws ServletException {
        basePackage = config.getInitParameter("base-package");
        try {
            //扫描基包得到全部的带包路径权限定名
            scanBasePackage(basePackage);
            //把带有@Controller/@Service/@Repository的类实例化放入map中,key为注解上的名字
            instance(packageNames);
            //Spring IOC注入
            springIOC();
            //完成URL地址与方法的映射关系
            handleUrlMethodMap();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }

    /**
     * 扫描基包
     * @param basePackage
     */
    private void scanBasePackage(String basePackage){
        //注意为了得到基包下面的URL路径,需要对basePackage包转换,将.替换为/
        URL url = this.getClass().getClassLoader().getResource(basePackage.replaceAll("\\.", "/"));
        File basePackageFile = new File(url.getPath());
        System.out.println("scan:" + basePackageFile);
        File[] childFiles = basePackageFile.listFiles();
        for (File file : childFiles){
            if (file.isDirectory()){
                scanBasePackage(basePackage + "." + file.getName());
            }else if (file.isFile()){
                packageNames.add(basePackage + "." + file.getName().split("\\.")[0]);
            }
        }
    }

    /**
     * 实例化
     * @param packageNames
     * @throws ClassNotFoundException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    private void  instance(List<String> packageNames) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        if (packageNames.size() < 1){
            return;
        }
        for (String string : packageNames){
            Class<?> c = Class.forName(string);
            if(c.isAnnotationPresent(Controller.class)){
                Controller controller = c.getAnnotation(Controller.class);
                String controllerName = controller.value();
                instanceMap.put(controllerName, c.newInstance());
                nameMap.put(string, controllerName);
                System.out.println("Controller:" + string + ", value:" + controller.value());
            }else if (c.isAnnotationPresent(Service.class)){
                Service service = c.getAnnotation(Service.class);
                String serviceName = service.value();
                instanceMap.put(serviceName, c.newInstance());
                nameMap.put(string, serviceName);
                System.out.println("Service:" + string + ",value:" + service.value());
            }else if (c.isAnnotationPresent(Repository.class)){
                Repository repository = c.getAnnotation(Repository.class);
                String repositoryName = repository.value();
                instanceMap.put(repositoryName, c.newInstance());
                nameMap.put(string, repositoryName);
                System.out.println("Repository:" + string + ",value:" + repository.value());
            }
        }
    }

    /**
     * 依赖注入
     * @throws IllegalAccessException
     */
    private void springIOC() throws IllegalAccessException {
        for (Map.Entry<String, Object> entry : instanceMap.entrySet()){
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for (Field field : fields){
                if (field.isAnnotationPresent(Qualifier.class)){
                    String name = field.getAnnotation(Qualifier.class).value();
                    field.setAccessible(true);
                    field.set(entry.getValue(), instanceMap.get(name));
                }
            }
        }
    }

    /**
     * url映射处理
     * @throws ClassNotFoundException
     */
    private void handleUrlMethodMap() throws ClassNotFoundException {
        if (packageNames.size() < 1){
            return;
        }
        for (String string : packageNames){
            Class<?> c = Class.forName(string);
            if (c.isAnnotationPresent(Controller.class)){
                Method[] methods = c.getMethods();
                StringBuffer baseUrl = new StringBuffer();
                if (c.isAnnotationPresent(RequestMapping.class)){
                    RequestMapping requestMapping = c.getAnnotation(RequestMapping.class);
                    baseUrl.append(requestMapping.value());
                }

                for (Method method : methods){
                    if (method.isAnnotationPresent(RequestMapping.class)){
                        RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                        baseUrl.append(requestMapping.value());

                        urlMethodMap.put(baseUrl.toString(), method);
                        methodPackageMap.put(method, string);
                    }
                }
            }
        }
    }

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

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String uri = req.getRequestURI();
        String contextPath = req.getContextPath();
        String path = uri.replaceAll(contextPath, "");
        Method method = urlMethodMap.get(path);
        if (method != null){
            //通过method拿到controller对象,准备反射执行
            String packageName = methodPackageMap.get(method);
            String controllerName = (String)nameMap.get(packageName);
            //拿到controller对象
            UserController userController = (UserController)instanceMap.get(controllerName);
            try {
                method.setAccessible(true);
                method.invoke(userController);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }
}

三、写个controller、service、dao

package com.ayo.controller;

import com.ayo.annotation.Controller;
import com.ayo.annotation.Qualifier;
import com.ayo.annotation.RequestMapping;
import com.ayo.service.UserService;

@Controller("userController")
@RequestMapping("/user")
public class UserController {

    @Qualifier("userServiceImpl")
    private UserService userService;

    @RequestMapping("/insert")
    public void insert(){
        userService.insert();
    }
}
package com.ayo.service;

public interface UserService {
    public void insert();
}
package com.ayo.dao;

public interface UserDao {
    public void insert();
}

参考自:微信公众号--java进阶架构师。

 

转载于:https://my.oschina.net/ayo123/blog/3018702

相关文章:

  • 你炒的肉丝为何又柴又老又难吃?
  • 刷新三观的几个网站,个个超厉害
  • 解决在V2.0中子组件使用v-model接收来自父组件的值异常
  • 商城系统 DBShop V1.3 Release 20190309 发布
  • HCL远程登陆(telnet)
  • 大数据会取代传统BI吗
  • 网络流基础
  • 不要自建Kubernetes
  • 容器镜像
  • C++智能指针与类模板
  • React 优秀插件记录
  • chrome浏览器的两个坑,以及其他
  • Visual Studio Community 2017 配置 OpenGL 环境 (NuGet)
  • 华为传输千兆5G光端机 PTN960
  • 持续集成-DevOps概念篇
  • 【108天】Java——《Head First Java》笔记(第1-4章)
  • 【391天】每日项目总结系列128(2018.03.03)
  • 【EOS】Cleos基础
  • CSS选择器——伪元素选择器之处理父元素高度及外边距溢出
  • iOS帅气加载动画、通知视图、红包助手、引导页、导航栏、朋友圈、小游戏等效果源码...
  • Python实现BT种子转化为磁力链接【实战】
  • Spring Boot快速入门(一):Hello Spring Boot
  • vue的全局变量和全局拦截请求器
  • windows下如何用phpstorm同步测试服务器
  • 笨办法学C 练习34:动态数组
  • 闭包--闭包作用之保存(一)
  • 大型网站性能监测、分析与优化常见问题QA
  • 第十八天-企业应用架构模式-基本模式
  • 工作踩坑系列——https访问遇到“已阻止载入混合活动内容”
  • 如何用vue打造一个移动端音乐播放器
  • 想晋级高级工程师只知道表面是不够的!Git内部原理介绍
  • 一起来学SpringBoot | 第三篇:SpringBoot日志配置
  • 怎么将电脑中的声音录制成WAV格式
  • ​【已解决】npm install​卡主不动的情况
  • # .NET Framework中使用命名管道进行进程间通信
  • $refs 、$nextTic、动态组件、name的使用
  • (附源码)springboot车辆管理系统 毕业设计 031034
  • (附源码)ssm高校志愿者服务系统 毕业设计 011648
  • (切换多语言)vantUI+vue-i18n进行国际化配置及新增没有的语言包
  • (删)Java线程同步实现一:synchronzied和wait()/notify()
  • (原創) 如何將struct塞進vector? (C/C++) (STL)
  • (原創) 系統分析和系統設計有什麼差別? (OO)
  • .net 4.0发布后不能正常显示图片问题
  • .NET 将混合了多个不同平台(Windows Mac Linux)的文件 目录的路径格式化成同一个平台下的路径
  • .NET 解决重复提交问题
  • .NET 应用启用与禁用自动生成绑定重定向 (bindingRedirect),解决不同版本 dll 的依赖问题
  • .netcore如何运行环境安装到Linux服务器
  • /etc/sudoer文件配置简析
  • @Not - Empty-Null-Blank
  • @private @protected @public
  • @staticmethod和@classmethod的作用与区别
  • [ 数据结构 - C++] AVL树原理及实现
  • [2016.7 test.5] T1
  • [ajaxupload] - 上传文件同时附件参数值
  • [DAU-FI Net开源 | Dual Attention UNet+特征融合+Sobel和Canny等算子解决语义分割痛点]