zoukankan      html  css  js  c++  java
  • 手写Spring实现1.0

    总览

    1. 引入servlet-api 依赖
    2. 在web.xml中的配置servlet,将对应请求都转发到自写的DispatcherServlet类(并在init-param参数中设置配置文件参数)
    3. DispatcherServlet类继承HttpServlet接口,并实现对应的方法。(get,post,init)
      - init():完成IOC,DI,handlerMapping等的初始化
      - get/post():根据用户的请求uri,从handlerMapping中获取到对应的调用方法和参数,使用反射实现调用

    DispatcherServlet

    field:

     Properties contextConfig;      //配置信息
     List<String> classNames = new ArrayList<>();            // 扫描根目录下的所有的类文件名
     Map<String,Object> ioc = new HashMap<>();               // ioc容器,存储已经实例化和依赖注入的对象(控制反转、依赖注入,单例)
     Map<String,Method> handlerMapping = new HashMap<>();    // url映射表,每个url对应一个method方法
    

    init(ServletConfig config):

    1. doLoadContextConfig()从servlet参数中获取配置文件路径 - 并将配置读取到contextConfig中
    2. doScanner()从config中获取到扫描根目录,扫描其下的所有.class文件,保存到classNames数组中
    3. doInstance()遍历扫描到的所有类,如果类存在相关的注解,则初始化bean,并注册到ioc容器
    4. doAutowired()扫描ioc容器中的所有实例的属性,如果存在@Autowired注解,则从ioc中找到对应的实例进行注入(私有属性需要开启访问权限)
    5. doInitHandlerMapping()对ioc中所有具有@Controller标签的类,读取其方法,解析出对应的请求uri,以<uri,method>的方式存放到map中

    doDipatch(req,resp) <- doGet(),doPost()

    1. 获取用户请求uri
    2. 组装请求实参列表
    3. method.invoke(instance,params)反射调用

    代码详解

    init(ServletConfig config)

        @Override
        public void init(ServletConfig config){
            // 1. 从servlet参数中获取配置文件路径 - 并将配置读取到contextConfig中
            doLoadContextConfig(config.getInitParameter("contextConfigLocation"));
    
            // 2. 从config中获取到扫描根目录,扫描其下的所有.class文件,保存到classNames数组中
            doScanner(contextConfig.getProperty("scan-package"));
    
            // 3. 遍历扫描到的所有类,如果类存在相关的注解,则初始化bean,并注册到ioc容器
            doInstance();
    
            // 4. 扫描ioc容器中的所有实例的属性,如果存在@Autowired注解,则从ioc中找到对应的实例进行注入
            doAutowired();
    
            // 5. 对ioc中所有具有@Controller标签的类,读取其方法,解析出对应的请求uri,以<uri,method>的方式存放到map中
            doInitHandlerMapping();
    
            logger.info("哦豁,初始化完成了!");
        }
    

    配置加载和读取

    doLoadContextConfig(String contextConfigLocation)

        private void doLoadContextConfig(String contextConfigLocation) {
            InputStream fis = null;
    
            contextConfig = new Properties();
            try {
                fis = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
                contextConfig.load(fis);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if(fis != null) {
                    try {
                        fis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    

    IOC

    doScanner(String packageName)

        private void doScanner(String packageName) {
            URL url = this.getClass().getClassLoader().getResource("/" + packageName.replace('.','/'));
            File fileDir = new File(url.getFile());
            for(File file : fileDir.listFiles()){
                if(file.isDirectory()){
                    doScanner(packageName + "." + file.getName());
                    continue;
                }
    
                if(file.getName().endsWith(".class")){
                    classNames.add(packageName + "." + file.getName().replace(".class","").trim());
                }
            }
        }
    

    doInstance()

        private void doInstance() {
            if(classNames.isEmpty()){return;}
    
            for(String className : classNames){
                try {
                    Class<?> clazz = Class.forName(className);
    
                    if (clazz.isAnnotationPresent(GPController.class)) {
                        String beanName = toLowerFirstCase(clazz.getSimpleName());
                        Object instance = clazz.newInstance();
                        ioc.put(beanName, instance);
                    } else if (clazz.isAnnotationPresent(GPService.class)) {
                        Object instance = clazz.newInstance();
    
                        // 1. 根据对象名或Autowired参数作为beanName注册到ioc容器
                        GPService service = clazz.getAnnotation(GPService.class);
                        String beanName = service.value().trim();
                        if("".equals(service.value())){
                            beanName = toLowerFirstCase(clazz.getSimpleName());
                        }
    
                        ioc.put(beanName,instance);
    
                        // 2. 将对象的所有实现接口,以接口的全类名作为beanName注册到ioc容器
                        for(Class<?> interfaceClass : clazz.getInterfaces() ) {
                            beanName = interfaceClass.getName();
                            if(ioc.containsKey(beanName)){
                                logger.info("The bean " + interfaceClass.getName() + " is already exists!");
                                continue;
                            }
    
                            ioc.put(beanName,instance);
                        }
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    

    DI

    doAutowired()

        /**
         * 反射中Filed对象,Method等对象,均只与来源类有关,与来源类的实例无关联,
         * 当需要使用对应的filed对象或method对象去调用来源类的具体实例时,需要将具体的实例作为参数传递给filed对象或method对象
         */
        private void doAutowired() {
            if(ioc.isEmpty()){return;}
    
            for(Map.Entry<String,Object> entry : ioc.entrySet()){
                Field[] fields = entry.getValue().getClass().getDeclaredFields();
                for(Field field : fields){
                    if(!field.isAnnotationPresent(GPAutowired.class)){continue;}
    
                    GPAutowired autowired = field.getAnnotation(GPAutowired.class);
                    String beanName = autowired.value().trim();
                    if("".equals(beanName)){
                        beanName = field.getType().getName();
                    }
    
                    field.setAccessible(true);
    
                    //field 相当于@GPAutowired private IDemoService demoService;
                    //entry.getValue() 相当于DemoAction的实例
                    //ioc.get(beanName)相当于 ioc.get("com.gupaoedu.demo.service.IDemoService");
                    try {
                        field.set(entry.getValue(),ioc.get(beanName));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    

    MVC

    doInitHandlerMapping()

        private void doInitHandlerMapping() {
            if(ioc.isEmpty()){return;}
    
            for(Map.Entry<String,Object> entry : ioc.entrySet()){
                Class<?> clazz = entry.getValue().getClass();
    
                if(!clazz.isAnnotationPresent(GPController.class)){continue;}
    
                // 获取controller上的base路径
                String baseUrl = "";
                if(clazz.isAnnotationPresent(GPRequestMapping.class)){
                    GPRequestMapping requestMapping = clazz.getAnnotation(GPRequestMapping.class);
                    baseUrl = requestMapping.value().trim();
                }
    
                // 依次扫描所有的方法,对带有GPRequestMapping注解的方法解析相对路径拼接上base路径,保存到HandlerMapping
                for(Method method : clazz.getMethods()){
                    if(!method.isAnnotationPresent(GPRequestMapping.class)){continue;}
    
                    GPRequestMapping requestMapping = method.getAnnotation(GPRequestMapping.class);
                    String handlerUrl = ("/" + baseUrl + "/" + requestMapping.value()).replaceAll("/+","/");
                    handlerMapping.put(handlerUrl,method);
                    logger.info("handler mapping init:" + handlerUrl);
                }
            }
        }
    

    dispatch

    doDispatch(req,resp)

        private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
            String uri = req.getRequestURI();
            String contextPath = req.getContextPath();
            uri = uri.replace(contextPath,"/").replaceAll("/+","/");
    
            Method method = handlerMapping.get(uri);
            if(method == null){
                resp.getWriter().write("404 Not Found!");
                return;
            }
    
            // 方法的形参列表
            Class<?>[] parameterTypes = method.getParameterTypes();
    
            // 方法的实参列表
            Object[] parameters = new Object[parameterTypes.length];
    
            for(int i = 0; i < parameterTypes.length; i++){
                if(parameterTypes[i] == HttpServletRequest.class){
                    parameters[i] = req;
                }else if(parameterTypes[i] == HttpServletResponse.class){
                    parameters[i] = resp;
                }else{
                    Annotation[][] annotations = method.getParameterAnnotations();
                    for(Annotation annotation : annotations[i]){
                        if(annotation instanceof GPRequestParam){
                            String parameterName = ((GPRequestParam) annotation).value();
                            parameters[i] = req.getParameter(parameterName);
                        }
                    }
                }
            }
    
            String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
            method.invoke(ioc.get(beanName),parameters);
        }
    

    欢迎疑问、期待评论、感谢指点 -- kiqi,愿同您为友

    -- 星河有灿灿,愿与之辉

  • 相关阅读:
    [NSURL initFileURLWithPath:]: nil string parameter 错误的解决方案
    Parser Error Message: Could not load type 错误原因
    C# DropDownList做友情链接打开新窗口
    前两天去A公司面试,面试管问的题目一下子闷了。很郁闷。重新答题。在这里分享一下
    获取枚举描述信息(Description)
    生成实体层的界面(webForm1.aspx)代码
    java中Filter 技术
    hdu_1332&poj_1102_LCDisplay
    hdu_1997_汉诺塔VII
    hdu_1134_Game of Connections_卡特兰数列
  • 原文地址:https://www.cnblogs.com/kiqi/p/14346683.html
Copyright © 2011-2022 走看看