Spring专题之:用300行代码提炼spring核心原理一

一 目标

1,了解看源代码最有效的方式,先猜测后验证,不要一开始就去调试代码

2,用300行最简洁的代码提炼Spring的基础设计思想

3,结合设计模式,掌握Spring框架的基本脉络

二 Spring如何下手,从哪里开始看?

Spring的如何开始的,我们先从原理来了解一下。

Spring主要有3个阶段:

第一阶段:配置阶段

在web.xml中设定DispatchServle,设置Spring-*.xml相关的配置信息或者直接设置*.properties文件,包含@Controller @Service @Autowrited @RequestMapping 等等

第二阶段:初始化阶段

1,调用init()方法,加载配置文件

2,IOC容器初始化,根据spring-*.xml或者*.properties中的配置扫描相关的类。

3,根据扫描的类,创建实例化类并保存到IOC容器中,主要是通过反射机制将类实例化放入IOC容器中

4,进行DI操作,即依赖注入,通过扫描IOC容器中的实例,给没有赋值的属性自动赋值

5,初始化HandelerMappping,就是将URL和Method进行一对一的关联映射到Map

第三个阶段:运行阶段

1,掉用doPost()/doGet()请求,获取request/response。

2,匹配handlerMapping,从request对象中获得用户输入的url,并从hangdlerMapping中找到对应的Method方法。

3,反射调用method.invoker(),返回结果

4,response.getWrite().wirte(),将结果返回给浏览器

Spring专题之:用300行代码提炼spring核心原理一_第1张图片

 三 代码实现

第一步我们要新建一个maven web工程。

这个不会建的请移步度娘,自己去百度。

我的项目结构如下图:

Spring专题之:用300行代码提炼spring核心原理一_第2张图片

 第二步,新建我们自己的相关的注解以及配置web.xml,pom.xml以及application.properties文件

ChService注解代码如下:
package com.ch.framework.annotation;

import java.lang.annotation.*;

/**
 * Created by lijianfang on 2021/10/25.
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ChService {
    String value() default "";
}
ChRequestMapping注解代码如下:
package com.ch.framework.annotation;

import java.lang.annotation.*;

/**
 * Created by lijianfang on 2021/10/25.
 */
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ChRequestMapping {
    String name() default "";
    @ChAliasFor("path")
    String[] value() default {};
    @ChAliasFor("value")
    String[] path() default {};
    String[] params() default {};

    String[] headers() default {};

    String[] consumes() default {};

    String[] produces() default {};
}
ChController注解代码如下:
package com.ch.framework.annotation;

import java.lang.annotation.*;

/**
 * Created by lijianfang on 2021/10/25.
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ChController {
    String value() default "";
}

ChAutowired注解代码如下:

package com.ch.framework.annotation;

import java.lang.annotation.*;

/**
 * Created by lijianfang on 2021/10/25.
 */
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ChAutowired {
    boolean required() default true;
    String value() default "";
}
ChAliasFor注解代码如下:
package com.ch.framework.annotation;

import java.lang.annotation.*;

/**
 * Created by lijianfang on 2021/10/25.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface ChAliasFor {

//    @ChAliasFor("attribute");
    String value() default "";

//    @ChAutowired("value");
    String attribute() default "";

    Class annotation() default Annotation.class;
}

pom.xml文件中需要配置编译相关的组件,pom.xml具体如下:




  4.0.0

  com.ch.framework
  springTest
  1.0-SNAPSHOT
  war

  springTest Maven Webapp
  
  http://www.example.com

  
    UTF-8
    1.7
    1.7
  

  
    
      junit
      junit
      4.11
      test
    
    
      javax.servlet
      javax.servlet-api
      3.0.1
    
    
      org.glassfish
      bean-validator
      3.0-JBoss-4.0.2
    
  

  
    springTest
    
      
        
          maven-clean-plugin
          3.1.0
        
        
        
          maven-resources-plugin
          3.0.2
        
        
          maven-compiler-plugin
          3.8.0
        
        
          maven-surefire-plugin
          2.22.1
        
        
          maven-war-plugin
          3.2.2
        
        
          maven-install-plugin
          2.5.2
        
        
          maven-deploy-plugin
          2.8.2
        
      
    
    
      
        src/main/java
        
          **/*.xml
        
      
      
        resources
        
          **/*.yml
          **/*.properties
          **/*.xml
          **/*.jpg
          **/*.ttf
          **/*.xml
        
        
          **/*.yml
        
      
    
  

web.xml中设置我们自己定义的dispatchServlet类,详情如下:



  springTest
  
    znnmvc
    com.ch.framework.dispatcher.servlet.ChDispatchServlet
    
      contextConfigLocation
      application.properties
    
    1
  
  
    znnmvc
    /*
  
  
    index.jsp
  

application.properties文件中定义需要扫描的类包名。

scanPackage=com.ch.framework.demo

第三步,开发我们自己的dispatchSetvlet类,我新建的是ChDispatchServlet.java

具体代码如下:

package com.ch.framework.dispatcher.servlet;


import com.ch.framework.annotation.ChAutowired;
import com.ch.framework.annotation.ChController;
import com.ch.framework.annotation.ChRequestMapping;
import com.ch.framework.annotation.ChService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

/**
 * 手写自己的servlet
 * Created by lijianfang on 2021/10/25.
 */
public class ChDispatchServlet extends HttpServlet{
    private static final Logger logger = LoggerFactory.getLogger(ChDispatchServlet.class);
    private Properties contextConfig = new Properties();
    private List classNames = new ArrayList();
    private Map ioc = new HashMap();
    private Map handlerMapping = new HashMap();
    @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){
            logger.error("调用报错,错误信息",e);
        }
    }
    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception{
        if(this.handlerMapping.isEmpty()){
            return;
        }
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replaceAll(contextPath, "").replaceAll("/", "/");
        //用户访问的路径是不是在hangdlerMapping中
        if(!this.handlerMapping.containsKey(url)){
            resp.getWriter().write("没有找到相关的路径,请确认路径是否正确!");
            return;
        }
        Method method = (Method) this.handlerMapping.get(url);
        //获取方法的参数列表
        Class[] paramterTypes = method.getParameterTypes();
        //获取请求的参数
        Map parameterMap = req.getParameterMap();
        Object [] parameValues = new Object[paramterTypes.length];
        //方法的参数列表
        for(int i = 0;i param : parameterMap.entrySet()) {
                    System.out.println("param.getValue()==="+param.getValue()[0]);
                    String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]","").
                            replaceAll(",\\s",",");
                    parameValues[i] = value;
                    i++;
                }
            }
        }
        String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
        //反射调用方法
        method.invoke(this.ioc.get(beanName), parameValues);
    }

    /**
     * 初始化相关参数
     * @param config
     * @throws ServletException
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        //1、加载配置文件
        doLoadConfig(config.getInitParameter("contextConfigLocation"));
        //2、扫描相关的类
        doScanner(contextConfig.getProperty("scanPackage"));
        //3、初始化扫描的类,并将他们放到IOC容器中
        doInstance();
        //4、完成依赖注入
        doAutowired();
        //5、初始化handerMapping
        initHandlerMapping();
        //6、调用
    }
    /**
     * 初始化url跟method的一一对应关系
     */
    private void initHandlerMapping() {
        if (ioc.isEmpty()) {
            return;
        }
        for (Map.Entry entry : ioc.entrySet()) {
            Class clazz = entry.getValue().getClass();
            //当前类的注解是不是controller,不是直接跳过
            if (!clazz.isAnnotationPresent(ChController.class)) {
                continue;
            }
            String baseUrl = "";
            //是controller注解,获取注解上的路径名称
            if (clazz.isAnnotationPresent(ChRequestMapping.class)) {
                ChRequestMapping requestMapping = clazz.getAnnotation(ChRequestMapping.class);
                baseUrl = requestMapping.value()[0];
            }
            // 默认获取所有的public,获取class类的方法(公共方法)
            for (Method method : clazz.getMethods()) {
                //判断方法上的注解是不是requestMap注解,不是就直接跳过
                if (!method.isAnnotationPresent(ChRequestMapping.class)) {
                    continue;
                }
                //获取注解上的路径
                ChRequestMapping requestMapping = method.getAnnotation(ChRequestMapping.class);
                //合成最终访问的路径
                String url = ("/"+baseUrl + "/" + requestMapping.value()[0]).replaceAll("/", "/");
                // 将访问的路径放到handlerMapping中
                handlerMapping.put(url, method);
                logger.info("Mapped" + url + method);
            }
        }
    }
    /**
     * 完成依赖注入
     */
    private void doAutowired() {
        if(ioc.isEmpty()){
            return;
        }
        //循环ioc中的类,给对应的类中的依赖的属性注入相应的bean
        for(Map.Entry entry:ioc.entrySet()){
            //获取对应类的私有属性
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            //循环私有属性,看是否是autowired注解
            for(Field field:fields){
                //看是不是service注解
                if(field.isAnnotationPresent(ChService.class)){
                    continue;
                }
                //查看是不是依赖注入的注解
                ChAutowired autowired = field.getAnnotation(ChAutowired.class);
                //获取对应注解的名字,先查看是否注解上是不是有写了默认的值,没有值时,使用类型注入
                String beanName = autowired.value().trim();
                if("".equals(beanName)){
                    beanName=field.getType().getName();
                }
                //设置注解类的可用属性
                field.setAccessible(true);
                try {
                    field.set(entry.getValue(),ioc.get(beanName));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 初始化扫描的类
     */
    private void doInstance(){
        if(classNames.isEmpty()){
            return;
        }
        try {
            for(String className:classNames){
                Class clazz = Class.forName(className);
                // 什么样的类才需要初始化,添加了注解的类才初始化
                //controller,service
                if (clazz.isAnnotationPresent(ChController.class)){
                    Object instance = clazz.newInstance();
                    String beanName = toLowerFirstCase(clazz.getSimpleName());
                    ioc.put(beanName, instance);
                }else if(clazz.isAnnotationPresent(ChService.class)){
                    ChService service = clazz.getAnnotation(ChService.class);
                    // 1、默认首字母小写
                    String beanName = service.value();
                    // 2、自定义bean名字
                    if ("".equals(beanName.trim())) {
                        beanName = toLowerFirstCase(clazz.getSimpleName());
                    }
                    Object instance = clazz.newInstance();
                    ioc.put(beanName, instance);
                    // 3、根据类自动赋值
                    for (Class i : clazz.getInterfaces()) {
                        if (ioc.containsKey(i.getName())) {
                            try {
                                throw new Exception("这个" + i.getName() + " 已经存在!");
                            } catch (Exception e) {
                                logger.error("这个" + i.getName() + " 已经存在!");
                            }
                        }
                        ioc.put(i.getName(), instance);
                    }
                }else {
                    continue;
                }

            }
        } catch (ClassNotFoundException e) {
            logger.error("报错信息:",e);
        } catch (InstantiationException e) {
            logger.error("报错信息:",e);
        } catch (IllegalAccessException e) {
            logger.error("报错信息:",e);
        }
    }
    /**
     * 

* 首字母小写 *

* * @param simpleName * @return */ private String toLowerFirstCase(String simpleName) { char[] chars = simpleName.toCharArray(); chars[0] += 32; return String.valueOf(chars); } /** *

* 扫描配置文件配置的类路径 *

* * @param scanPackage */ private void doScanner(String scanPackage) { 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{ if (!file.getName().endsWith(".class")) { continue; } String className = (scanPackage + "." + file.getName().replace(".class", "")); classNames.add(className); } } } /** *

* 加载配置文件,直接从类路径下找到spring主配置文件夹所在的路径并且将其读取出来放到Properties对象中
* 将application.properties文件中的scanPackage=com.ch.framework.demo 放到properties对象中 *

* @param contextConfigLocation */ private void doLoadConfig(String contextConfigLocation){ InputStream fis = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation); try{ contextConfig.load(fis); }catch (Exception e){ logger.error("加载配置失败,错误原因:", e); }finally{ if(null!=fis){ try { fis.close(); } catch (IOException e) { logger.error("关闭流失败,错误原因:", e); } } } } }

第四步,编写测试类

我新建了一个demo包,

TestController.java文件代码如下:
package com.ch.framework.demo.controller;

import com.ch.framework.annotation.ChAutowired;
import com.ch.framework.annotation.ChController;
import com.ch.framework.annotation.ChRequestMapping;
import com.ch.framework.demo.service.DomeService;

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

/**
 * Created by lijianfang on 2021/11/15.
 */
@ChController
@ChRequestMapping("test")
public class TestController {


    @ChAutowired
    private DomeService domeService;

    @ChRequestMapping("getData")
    public void getData(HttpServletRequest request, HttpServletResponse response){
        String name = request.getParameter("name");
        System.out.println("name=="+name);
        try {
            String result = domeService.getData(name);
            response.getWriter().write(result);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @ChRequestMapping("addData")
    public void addData(HttpServletRequest request, HttpServletResponse response){
        int a = Integer.parseInt(request.getParameter("a"));
        int b = Integer.parseInt(request.getParameter("b"));
        System.out.println("name=="+a);
        try {
            int result = domeService.addData(a,b);
            response.getWriter().write("result:a+b="+result);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

DomeService.java文件代码如下: 

package com.ch.framework.demo.service;

/**
 * Created by lijianfang on 2021/11/15.
 */
public interface DomeService {
    public String getData(String name);
    public int addData(int a,int b);
}
DomeServiceImpl.java文件代码如下: 
package com.ch.framework.demo.service.impl;

import com.ch.framework.annotation.ChService;
import com.ch.framework.demo.service.DomeService;

/**
 * Created by lijianfang on 2021/11/15.
 */
@ChService
public class DomeServiceImpl implements DomeService {
    @Override
    public String getData(String name) {
        return "My name is "+name;
    }

    @Override
    public int addData(int a, int b) {
        return a+b;
    }
}

第五步,测试结果

Spring专题之:用300行代码提炼spring核心原理一_第3张图片

Spring专题之:用300行代码提炼spring核心原理一_第4张图片

你可能感兴趣的