手写--Spring核心原理(版本一)

一、实现思路

手写--Spring核心原理(版本一)_第1张图片

二、代码实现

1、配置application.properties文件

scanPackage=com.ckw.demo

2、配置web.xml




  Archetype Created Web Application

  
    ckwmvc
    com.ckw.mvcframework.v1.CKWDispatcherServlet
    
      contextConfigLocation
      application.properties
    

    1

  
  
    ckwmvc
    /*
  




CKWDispatcherServlet 是有自己模拟 SpringMVC 实现的核心功能类

3、自定义注解

a、CKWController

package com.ckw.mvcframework.annotation;

import java.lang.annotation.*;

/**
 * @author ckw
 * @version 1.0
 * @date 2020/6/11 20:50
 * @description: 自定义controller
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CKWController {

    String value() default "";
}

b、CKWService

package com.ckw.mvcframework.annotation;

import java.lang.annotation.*;

/**
 * @author ckw
 * @version 1.0
 * @date 2020/6/11 20:52
 * @description: 自定义Service
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CKWService {

    String value() default "";

}

c、CKWRequestMapping

package com.ckw.mvcframework.annotation;

import java.lang.annotation.*;

/**
 * @author ckw
 * @version 1.0
 * @date 2020/6/11 20:51
 * @description: 自定义映射路径
 */
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CKWRequestMapping {

    String value() default "";

}

d、CKWAutowired

package com.ckw.mvcframework.annotation;

import java.lang.annotation.*;

/**
 * @author ckw
 * @version 1.0
 * @date 2020/6/11 20:54
 * @description: 自定义注入
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CKWAutowired {

    String value() default "";

}

e、CKWRequestParam

package com.ckw.mvcframework.annotation;

import java.lang.annotation.*;

/**
 * @author ckw
 * @version 1.0
 * @date 2020/6/11 20:53
 * @description: 自定义请求接收参数
 */
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CKWRequestParam {

    String value() default "";

}

4、编写controller、service层接口及service实现类(使用自定义注解)

package com.ckw.demo.controller;

import com.ckw.demo.service.DemoService;
import com.ckw.mvcframework.annotation.CKWAutowired;
import com.ckw.mvcframework.annotation.CKWController;
import com.ckw.mvcframework.annotation.CKWRequestMapping;
import com.ckw.mvcframework.annotation.CKWRequestParam;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author ckw
 * @version 1.0
 * @date 2020/6/11 20:56
 * @description:
 */
@CKWController
public class DemoController {

    @CKWAutowired("demoService")
    private DemoService demoService;

    @CKWRequestMapping("/test/demo")
    public void testDemo(HttpServletRequest request, HttpServletResponse response,
                         @CKWRequestParam("name")String name){

        String s = demoService.get(name);

        try {
            response.getWriter().write("

"+s+"

"); } catch (IOException e) { e.printStackTrace(); } } }

package com.ckw.demo.service;

/**
 * @author ckw
 * @version 1.0
 * @date 2020/6/11 20:49
 * @description: service
 */
public interface DemoService {

    String get(String name);

}

package com.ckw.demo.service.impl;

import com.ckw.demo.service.DemoService;
import com.ckw.mvcframework.annotation.CKWService;

/**
 * @author ckw
 * @version 1.0
 * @date 2020/6/11 20:49
 * @description:
 */
@CKWService
public class DemoServiceImpl implements DemoService {

    @Override
    public String get(String name) {
        return name + " handwriting spring_v1 ok!!!!!!!!";
    }

}

5、此时基本配置已经完成,编写最主要核心功能类CKWDispatcherServlet(v1版本)

package com.ckw.mvcframework.v1;

import com.ckw.mvcframework.annotation.*;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
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.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

/**
 * @author ckw
 * @version 1.0
 * @date 2020/6/11 21:01
 * @description: 自定义DispatchServlet
 */
public class CKWDispatcherServlet extends HttpServlet {

    //保存application.properties配置文件中的内容
    private Properties contextConfig = new Properties();

    //保存扫描的所有的类名
    private List classNames = new ArrayList();

    //传说中的IOC容器
    //简化程序,暂时不使用ConcurrentHashMap
    private Map ioc = new HashMap();

    //保存url和Method的对应关系
    private Map handlerMapping = new HashMap();


    @Override
    public void init(ServletConfig config) throws ServletException {

        //1、加载配置文件
        doLoadConfig(config.getInitParameter("contextConfigLocation"));

        //2、扫描相关类
        doScanner(contextConfig.getProperty("scanPackage"));

        //3、初始化所有相关类的实例,并且放入Spring容器中
        doInstance();

        //4、完成依赖注入
        doAutowired();

        //5、初始化handlerMapping
        initHandlerMapping();

        System.out.println("handwriting spring init ok");

    }

    @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 {

        try {
            //6、调用,运行阶段
            doDispatch(req,resp);

        } catch (Exception e) {
            e.printStackTrace();
            resp.getWriter().write("500 Exection,Detail : " + Arrays.toString(e.getStackTrace()));
        }

    }

    //6、调用,运行阶段
    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        //绝对路径
        //   /项目名/调用路径
        String url = req.getRequestURI();
        //处理成相对路径
        // /项目名
        String contextPath = req.getContextPath();
        //调用路径
        url = url.replaceAll(contextPath,"").replaceAll("/+","/");

        if(!this.handlerMapping.containsKey(url)){
            resp.getWriter().write("404 Not Found!!!");
            return;
        }

        Method method = handlerMapping.get(url);

        //拿到所有请求参数
        Map params = req.getParameterMap();

        //获取方法中的形参列表
        Class[] parameterTypes = method.getParameterTypes();

        Object[] paramValues = new Object[parameterTypes.length];

        for (int i = 0; i < parameterTypes.length; i ++) {
            Class parameterType = parameterTypes[i];
            //不能用instanceof,parameterType它不是实参,而是形参
            if(parameterType == HttpServletRequest.class){
                paramValues[i] = req;
                continue;
            }else if(parameterType == HttpServletResponse.class){
                paramValues[i] = resp;
                continue;
             //这里简单只判断String类型
            }else if(parameterType == String.class){
                //获取方法参数的所有注解
                Annotation[][] pa = method.getParameterAnnotations();
                for (int j = 0; j < pa.length; j ++){
                    for (Annotation a : pa[i]) {
                        //判断是否有RequestParam注解
                        if(a instanceof CKWRequestParam){
                            String paramName = ((CKWRequestParam) a).value();
                            if(!"".equals(paramName.trim())){
                                String value = Arrays.toString(params.get(paramName))
                                        .replaceAll("\\[|\\]","")
                                        .replaceAll("\\s","");
                                paramValues[i] = value;
                            }
                        }
                    }
                }
            }
        }

        //通过反射拿到method所在class,拿到class之后还是拿到class的名称
        //再调用toLowerFirstCase获得beanName
        String beanName  = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
        method.invoke(ioc.get(beanName),paramValues);


    }

    //5、初始化handlerMapping
    private void initHandlerMapping() {

        if(ioc.isEmpty()){ return; }

        //把controller中对应的url---method做对应
        for (Map.Entry entry : ioc.entrySet()) {
            Class clazz = entry.getValue().getClass();

            //拿到有controller注解的类
            if(!clazz.isAnnotationPresent(CKWController.class))continue;

            //保存写在类上面的@GPRequestMapping("/demo")
            String baseUrl = "";
            if(clazz.isAnnotationPresent(CKWRequestMapping.class)){
                CKWRequestMapping requestMapping = clazz.getAnnotation(CKWRequestMapping.class);
                baseUrl = requestMapping.value();
            }

            //拿到有RequestMapping注解的方法
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                if(!method.isAnnotationPresent(CKWRequestMapping.class))continue;
                CKWRequestMapping requestMapping = method.getAnnotation(CKWRequestMapping.class);
                //优化
                // //demo///query
                String url = ("/" + baseUrl + "/" + requestMapping.value())
                        .replaceAll("/+","/");

                handlerMapping.put(url, method);

                System.out.println(url + "-------------" + method);            }
        }
    }

    //4、完成依赖注入
    private void doAutowired() {

        if(ioc.isEmpty()) return;

        for (Map.Entry entry : ioc.entrySet()) {
            //Declared 所有的,特定的 字段,包括private/protected/default
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for (Field field : fields) {
                if(!field.isAnnotationPresent(CKWAutowired.class)){continue;}
                CKWAutowired annotation = field.getAnnotation(CKWAutowired.class);

                //如果用户没有自定义beanName,默认就根据类型注入
                //这个地方省去了对类名首字母小写的情况的判断
                String beanName = annotation.value().trim();
                if("".equals(beanName)){
                    beanName = field.getType().getName();
                }

                //如果是public以外的修饰符,只要加了@Autowired注解,都要强制赋值
                //反射中叫做暴力访问
                field.setAccessible(true);
                try {
                    //用反射机制,动态给字段赋值
                    field.set(entry.getValue(),ioc.get(beanName));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }

        }

    }

    //3、初始化所有相关类的实例,并且放入Spring容器中
    private void doInstance() {

        if(classNames.isEmpty()) return;

        try {
            for (String className : classNames) {
                Class clazz = Class.forName(className);

                //什么样的类才需要初始化?
                //加了注解的类,才初始化,怎么判断?
                //为了简化代码逻辑,只举例 @Controller和@Service
                if(clazz.isAnnotationPresent(CKWController.class)){
                    Object instance = clazz.newInstance();
                    //Spring默认类名首字母小写
                    String beanName = toLowerFirstCase(clazz.getSimpleName());
                    ioc.put(beanName,instance);

                }else if(clazz.isAnnotationPresent(CKWService.class)){
                    //拿到注解中设置的beanName的值
                    CKWService annotation = clazz.getAnnotation(CKWService.class);
                    String beanName = annotation.value();
                    //如果注解中没有设置beanName的值,则默认类名首字母小写作为beanName
                    if("".equals(beanName.trim())){
                        beanName = toLowerFirstCase(clazz.getSimpleName());
                    }
                    Object instance = clazz.newInstance();
                    //存入IOC中
                    ioc.put(beanName, instance);

                    //还需把当前类实现的所有接口遍历出来  接口名---实现类的类型(不考虑多个实例类)
                    for (Class anInterface : clazz.getInterfaces()) {
                        if(ioc.containsKey(anInterface.getName())){
                            throw new Exception("The “" + anInterface.getName() + "” is exists!!");
                        }
                        ioc.put(toLowerFirstCase(anInterface.getSimpleName()),instance);
                    }
                }else{
                    continue;
                }

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

    }

    //如果类名本身是小写字母,确实会出问题
    //但这个方法是我自己用,private的
    //传值也是自己传,类也都遵循了驼峰命名法
    //默认传入的值,存在首字母小写的情况,也不可能出现非字母的情况

    //为了简化程序逻辑,不做其他判断
    private String toLowerFirstCase(String simpleName) {
        char [] chars = simpleName.toCharArray();
        //之所以加,是因为大小写字母的ASCII码相差32,
        // 而且大写字母的ASCII码要小于小写字母的ASCII码
        //在Java中,对char做算学运算,实际上就是对ASCII码做算学运算
        chars[0] += 32;
        return String.valueOf(chars);
    }


    //2、扫描相关类
    private void doScanner(String scanPackage) {
        //scanPackage = com.ckw.demo ,存储的是包路径
        //转换为文件路径,实际上就是把.替换为/就OK了
        //classpath
        URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.","/"));

        File classPath = new File(url.getFile());
        //遍历扫描包下的所有文件
        for (File file : classPath.listFiles()) {
            //如果是文件夹  递归调用
            if(file.isDirectory()){
                doScanner(scanPackage + "." + file.getName());
            }else{
                //只加载以.class文件结尾
                if(!file.getName().endsWith(".class")){ continue;}

                String className = (scanPackage + "." + file.getName().replace(".class",""));
                classNames.add(className);
            }
        }

    }

    //1、加载配置文件
    private void doLoadConfig(String contextConfigLocation) {

        //直接从类路径下找到Spring主配置文件所在的路径
        //并且将其读取出来放到Properties对象中
        //相对于scanPackage=com.ckw.demo 从文件中保存到了内存中
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);

        try {
            contextConfig.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

接下来几篇会对核心功能进行拆分、更加细致的划分

你可能感兴趣的:(java,spring,servlet)