美文网首页
MINI版Sping-300行代码手写Sping

MINI版Sping-300行代码手写Sping

作者: 有梦想的虫子_2018 | 来源:发表于2020-09-25 18:27 被阅读0次

转载
作者:程序员技术圈
链接:https://www.jianshu.com/p/2c30f3e6b664

用不到 300 行代码来描述 Spring IOC、DI、MVC 的精华设计思想,并保证基本功能完整。

Spring 的三个阶段,配置阶段、初始化阶段和运行阶段

image.png

配置阶段:主要是完成 application.xml 配置和 Annotation 配置。

初始化阶段:主要是加载并解析配置信息,然后,初始化 IOC 容器,完成容器的 DI 操作,已经完成 HandlerMapping 的初始化。

运行阶段:主要是完成 Spring 容器启动以后,完成用户请求的内部调度,并返回响应结果。

项目结构

image.png

一、配置阶段

1、pom.xml配置

我采用的是 maven 管理项目。先来看 pom.xml 文件中的配置,我只引用了 servlet-api 的依赖。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>sz-spring</artifactId>
        <groupId>com.suzao.spring</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>spring-demo-01</artifactId>
    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>
    </dependencies>
    <packaging>war</packaging>
    <build>
        <finalName>webapp</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.2.2</version>
                <configuration>
                    <webResources>
                        <resource>
                            <!-- 原配置文件的目录,相对于pom.xml文件的路径 -->
                            <directory>src/main/webapp/WEB-INF</directory>
                            <!-- 目标路径 -->
                            <targetPath>WEB-INF</targetPath>
                        </resource>
                    </webResources>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2、SZDispatcherServlet 类

然后,创建 SZDispatcherServlet 类并继承 HttpServlet,重写 init()、doGet() 和 doPost() 方法。

package com.suzao.mvcframework.servlet.v2;

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.IOException;

/**
 * @ClassName SZDispatchServlet
 * @Description: TODO
 * @Author mc
 * @Date 2020
 * @Version V1.0
 **/
public class SZDispatchServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
    }

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

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
    }
}

3、web.xml配置

在 web.xml 文件中配置以下信息:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:javaee="http://java.sun.com/xml/ns/javaee"
         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
         version="2.4">
    <display-name>SuZao Web Application</display-name>
    <servlet>
        <servlet-name>szmvc</servlet-name>
        <servlet-class>com.suzao.mvcframework.servlet.v2.SZDispatchServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>application.properties</param-value>
        </init-param>

        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>szmvc</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

4、application.properties配置

我们配置了一个初始化加载的 Spring 主配置文件路径,在原生框架中,我们应该配置的是 classpath:application.xml。在这里,我们为了简化操作,用 properties 文件代替 xml 文件。以下是 properties 文件中的内容:

scanPackage=com.suzao.demo

5、创建SZController 注解:

package com.suzao.mvcframework.annotation;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SZController {
    String value() default "";
}

6、创建SZService 注解

package com.suzao.mvcframework.annotation;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SZService {
    String value() default "";
}

7、创建SZRequestMapping注解

package com.suzao.mvcframework.annotation;

import java.lang.annotation.*;

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SZRequestMapping {
    String value() default "";
}

8、创建SZRequestParam注解

package com.suzao.mvcframework.annotation;

import java.lang.annotation.*;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SZRequestParam {
    String value() default "";
}

9、创建SZAutowired 注解

package com.suzao.mvcframework.annotation;

import java.lang.annotation.*;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SZAutowired {
    String value() default "";
}

10、使用自定义注解进行配置DemoAction

package com.suzao.demo.action;

import com.suzao.demo.service.IDemoService;
import com.suzao.mvcframework.annotation.SZAutowired;
import com.suzao.mvcframework.annotation.SZController;
import com.suzao.mvcframework.annotation.SZRequestMapping;
import com.suzao.mvcframework.annotation.SZRequestParam;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @ClassName DemoAction
 * @Description: TODO
 * @Author mc
 * @Date 2020
 * @Version V1.0
 **/
@SZController
@SZRequestMapping("/demo")
public class DemoAction {
    @SZAutowired
    private IDemoService demoService;

    @SZRequestMapping("query")
    public void query(HttpServletRequest req, HttpServletResponse resp,
                      @SZRequestParam("name") String name){
        String result = demoService.get(name);
        try {
            resp.getWriter().write(result);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @SZRequestMapping("add")
    public void add(HttpServletRequest request , HttpServletResponse resp,
                    @SZRequestParam("a") Integer a, @SZRequestParam("b") Integer b){
        try {
            resp.getWriter().write(a+"+" +b +"=" +(a+b));
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @SZRequestMapping("remove")
    public void remove(HttpServletRequest req , HttpServletResponse resp,
                       @SZRequestParam("id") Integer id){

    }

}

11、使用自定义注解进行配置DemoService

package com.suzao.demo.service;

import com.suzao.mvcframework.annotation.SZService;

/**
 * @ClassName DemoService
 * @Description: TODO
 * @Author mc
 * @Date 2020
 * @Version V1.0
 **/
@SZService
public class DemoService implements IDemoService {
    @Override
    public String get(String name) {
        return "My name is "+ name;
    }
}

12、IDemoService

package com.suzao.demo.service;

/**
 * @ClassName IDemoService
 * @Description: TODO
 * @Author mc
 * @Date 2020
 * @Version V1.0
 **/
public interface IDemoService {
    String get(String name);
}

二、初始化阶段

1、先在 SZDispatcherServlet 中声明几个成员变量

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

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

    //IOC容器
    private Map<String,Object> ioc = new HashMap<>();

    //保存url和Method的对应关系
    private Map<String,Method> handleMapping = new HashMap<>();

2、SZDispatcherServlet 的init()方法

当 Servlet 容器启动时,会调用 SZDispatcherServlet 的 init()方法,从 init 方法的参数中,我们可以拿到主配置文件的路径,从能够读取到配置文件中的信息。前面我们已经介绍了 Spring 的三个阶段,现在来完成初始化阶段的代码。在 init() 方法中,定义好执行步骤,如下:

@Override
    public void init(ServletConfig config) throws ServletException {
        //1.加载配置文件
        doLoadConfig(config.getInitParameter("contextConfigLocation"));

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

        //3.初始化扫描到的类,并且放入到IOC容器中
        doInstance();

        //4.完成自动化的依赖注入
        doAutowired();

        //5.初始化HandlerMapping
        doInitHandlerMapping();

        System.out.println("SZ Spring framework is init.");
    }

3、doLoadConfig() 方法

doLoadConfig() 方法的实现,将文件读取到 Properties 对象中

//加载配置文件
    private void doLoadConfig(String contextConfigLocation) {
        //直接从类路径下找到Spring主配置文件所在的路径
        //并且将其读取出来放到Properties对象中
        //相对于scanPackage=com.suzao.demo从文件中保存到内存中
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
            contextConfig.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

4、doScanner() 方法

doScanner() 方法,递归扫描出所有的 Class 文件

   //扫描出相关的类
    private void doScanner(String scanPackage) {
        URL url = this.getClass().getClassLoader().getResource("/"+scanPackage.replaceAll("\\.","/" ));
        //scanPackage= com.suzao.demo 存储的包路径
        //转换为文件路径,实际上就是把 .替换成/
        //classpath下不仅有.class 文件, .xml文件 .properties文件
        File classPath = new File(url.getFile());

        for (File file :classPath.listFiles()){
            if(file.isDirectory()){
                doScanner(scanPackage + "." + file.getName());
            }else {
                //变成包名.类名
                //Class.forname()
                if(!file.getName().endsWith(".class")){
                    continue;
                }
                classNames.add(scanPackage + "." + file.getName().replace(".class","" ));
            }
        }
    }

5、doInstance() 方法

doInstance() 方法,初始化所有相关的类,并放入到 IOC 容器之中。IOC 容器的 key 默认是类名首字母小写,如果是自己设置类名,则优先使用自定义的。因此,要先写一个针对类名首字母处理的工具方法

private String toLowerFirstCase(String simpleName) {
        char[] chars = simpleName.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }
private void doInstance() {
        if(classNames.isEmpty()){
            return;
        }
        try {
            for(String className : classNames){
                Class<?> clazz = Class.forName(className);

                //什么样的类才需要初始化呢
                //加了注解的类,才初始化,怎么判断
                //为了简化代码逻辑,主要体会设计思想,只举例@Controller @Service
                if(clazz.isAnnotationPresent(SZController.class)){
                    Object instance = clazz.newInstance();
                    String beanName = toLowerFirstCase(clazz.getSimpleName());
                    ioc.put(beanName,instance );
                } else if (clazz.isAnnotationPresent(SZService.class)) {

                    //1.默认根据beanName类名首字母小写
                    String beanName = toLowerFirstCase(clazz.getSimpleName());

                    //2.使用自定义的beanName
                    SZService service = clazz.getAnnotation(SZService.class);
                    if(!"".equals(service.value())){
                        beanName = service.value();
                    }

                    Object instance = clazz.newInstance();
                    ioc.put(beanName, instance);

                    //3.根据包名.类名作为beanName
                    for (Class<?> i : clazz.getInterfaces()){
                        if(ioc.containsKey(i.getName())){
                            throw new Exception("The beanName is exists!!");
                        }
                        //把接口的类型直接当成key了
                        ioc.put(i.getName(),instance );
                    }

                }else {
                    continue;
                }
            }

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

doAutowired() 方法

doAutowired() 方法,将初始化到 IOC 容器中的类,需要赋值的字段进行赋值

    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(SZAutowired.class)){
                    continue;
                }
                SZAutowired autowired = field.getAnnotation(SZAutowired.class);

                //如果用户没有自定义beanname ,默认就根据类型注入
                String beanName = autowired.value().trim();
                if("".equals(beanName)){
                    beanName = field.getType().getName();
                }

                //如果是public以外的修饰符,只要加了@Autowired注解,都要强制赋值
                //暴力访问
                field.setAccessible(true);

                //反射调用
                //给entry.getValue()这个对象的field字段,赋ioc.get(beanName)的值
                try {
                    field.set(entry.getValue(),ioc.get(beanName) );
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                    continue;
                }
            }
        }
    }

6、doInitHandlerMapping() 方法

doInitHandlerMapping() 方法,将 SZRequestMapping 中配置的信息和 Method 进行关联,并保存这些关系。

//初始化url和method的一对一对应关系
    private void doInitHandlerMapping() {
        if(ioc.isEmpty()){
            return;
        }

        for (Map.Entry<String,Object> entry : ioc.entrySet()){
            Class<?> clazz = entry.getValue().getClass();

            if(!clazz.isAnnotationPresent(SZController.class)){
                continue;
            }


            //保存写在类上面的@GPRequestMapping("/demo")
            String baseUrl = "";
            if(clazz.isAnnotationPresent(SZRequestMapping.class)){
                SZRequestMapping requestMapping = clazz.getAnnotation(SZRequestMapping.class);
                baseUrl = requestMapping.value();
            }
            //默认获取所有的public方法
            for(Method method : clazz.getMethods()){
                if(!method.isAnnotationPresent(SZRequestMapping.class)){
                    continue;
                }
                SZRequestMapping requestMapping = method.getAnnotation(SZRequestMapping.class);

                String url = ("/" + baseUrl + "/" + requestMapping.value()).replaceAll("/+","/" );
                handleMapping.put(url,method );

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

到此,初始化阶段的所有代码全部写完。

三、运行阶段

1、doPost()方法

来到运行阶段,当用户发送请求被 Servlet 接受时,都会统一调用 doPost 方法,我先在 doPost 方法中再调用 doDispach() 方法

@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 {
            doDispatch(req,resp);
        }catch (Exception e){
            e.printStackTrace();
            resp.getWriter().write("500 Exception " + Arrays.toString(e.getStackTrace()));
        }
    }

2、doDispatch() 方法

 private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception{
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath,"" ).replaceAll("/+","/" );
        if(!this.handleMapping.containsKey(url)){
            resp.getWriter().write("404 Not Found!");
            return;
        }

        Method method = this.handleMapping.get(url);
        Map<String,String[]> paramsMap = req.getParameterMap();
        //实参列表要根据形参列表才能决定,首先得拿到形参列表
        Class<?>[] parameterTypes = method.getParameterTypes();
        Object[] parameValus = new Object[parameterTypes.length];
        for (int i = 0 ;i< parameterTypes.length ; i++){
            Class paramterType = parameterTypes[i];
            if(paramterType == HttpServletRequest.class){
                parameValus[i] = req;
                continue;
            }else if(paramterType ==  HttpServletResponse.class){
                parameValus[i] = resp;
                continue;
            }else if(paramterType == String.class){
                Annotation[][] pa = method.getParameterAnnotations();
                for (Annotation a : pa[i]){
                    if(a instanceof SZRequestParam){
                        String paramName = ((SZRequestParam) a).value();
                        if(!"".equals(paramName.trim())){
                            String value = Arrays.toString(paramsMap.get(paramName))
                                    .replaceAll("\\[|\\]", "")
                                    .replaceAll("\\s","," );
                            parameValus[i] = value;
                        }
                    }
                }
                /*for(int j =0 ;j < pa.length ;j++){
                }*/
            }
        }
        String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
        method.invoke(ioc.get(beanName),parameValus);
    }

我们把服务发布到 web 容器中,然后,在浏览器输入:http://localhost:8080/demo/query?name=MC
结果:

image.png
当然,真正的 Spring 要复杂很多,但核心设计思路基本如此。

4、SZDispatchServlet完整代码

package com.suzao.mvcframework.servlet.v2;

import com.suzao.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.*;

/**
 * @ClassName SZDispatchServlet
 * @Description: TODO
 * @Author mc
 * @Date 2020
 * @Version V1.0
 **/
public class SZDispatchServlet extends HttpServlet {

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

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

    //IOC容器
    private Map<String,Object> ioc = new HashMap<>();

    //保存url和Method的对应关系
    private Map<String,Method> handleMapping = 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 {
            doDispatch(req,resp);
        }catch (Exception e){
            e.printStackTrace();
            resp.getWriter().write("500 Exception " + Arrays.toString(e.getStackTrace()));
        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception{
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath,"" ).replaceAll("/+","/" );
        if(!this.handleMapping.containsKey(url)){
            resp.getWriter().write("404 Not Found!");
            return;
        }

        Method method = this.handleMapping.get(url);
        Map<String,String[]> paramsMap = req.getParameterMap();
        //实参列表要根据形参列表才能决定,首先得拿到形参列表
        Class<?>[] parameterTypes = method.getParameterTypes();
        Object[] parameValus = new Object[parameterTypes.length];
        for (int i = 0 ;i< parameterTypes.length ; i++){
            Class paramterType = parameterTypes[i];
            if(paramterType == HttpServletRequest.class){
                parameValus[i] = req;
                continue;
            }else if(paramterType ==  HttpServletResponse.class){
                parameValus[i] = resp;
                continue;
            }else if(paramterType == String.class){
                Annotation[][] pa = method.getParameterAnnotations();
                for (Annotation a : pa[i]){
                    if(a instanceof SZRequestParam){
                        String paramName = ((SZRequestParam) a).value();
                        if(!"".equals(paramName.trim())){
                            String value = Arrays.toString(paramsMap.get(paramName))
                                    .replaceAll("\\[|\\]", "")
                                    .replaceAll("\\s","," );
                            parameValus[i] = value;
                        }
                    }
                }
                /*for(int j =0 ;j < pa.length ;j++){
                }*/
            }
        }


        String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
        method.invoke(ioc.get(beanName),parameValus);
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        //1.加载配置文件
        doLoadConfig(config.getInitParameter("contextConfigLocation"));

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

        //3.初始化扫描到的类,并且放入到IOC容器中
        doInstance();

        //4.完成自动化的依赖注入
        doAutowired();

        //5.初始化HandlerMapping
        doInitHandlerMapping();

        System.out.println("SZ Spring framework is init.");
    }

    //初始化url和method的一对一对应关系
    private void doInitHandlerMapping() {
        if(ioc.isEmpty()){
            return;
        }

        for (Map.Entry<String,Object> entry : ioc.entrySet()){
            Class<?> clazz = entry.getValue().getClass();

            if(!clazz.isAnnotationPresent(SZController.class)){
                continue;
            }


            //保存写在类上面的@GPRequestMapping("/demo")
            String baseUrl = "";
            if(clazz.isAnnotationPresent(SZRequestMapping.class)){
                SZRequestMapping requestMapping = clazz.getAnnotation(SZRequestMapping.class);
                baseUrl = requestMapping.value();
            }
            //默认获取所有的public方法
            for(Method method : clazz.getMethods()){
                if(!method.isAnnotationPresent(SZRequestMapping.class)){
                    continue;
                }
                SZRequestMapping requestMapping = method.getAnnotation(SZRequestMapping.class);

                String url = ("/" + baseUrl + "/" + requestMapping.value()).replaceAll("/+","/" );
                handleMapping.put(url,method );

                System.out.println("Mapped " + url + "," + 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(SZAutowired.class)){
                    continue;
                }
                SZAutowired autowired = field.getAnnotation(SZAutowired.class);

                //如果用户没有自定义beanname ,默认就根据类型注入
                String beanName = autowired.value().trim();
                if("".equals(beanName)){
                    beanName = field.getType().getName();
                }

                //如果是public以外的修饰符,只要加了@Autowired注解,都要强制赋值
                //暴力访问
                field.setAccessible(true);

                //反射调用
                //给entry.getValue()这个对象的field字段,赋ioc.get(beanName)的值
                try {
                    field.set(entry.getValue(),ioc.get(beanName) );
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                    continue;
                }
            }


        }
    }

    private void doInstance() {
        if(classNames.isEmpty()){
            return;
        }
        try {
            for(String className : classNames){
                Class<?> clazz = Class.forName(className);

                //什么样的类才需要初始化呢
                //加了注解的类,才初始化,怎么判断
                //为了简化代码逻辑,主要体会设计思想,只举例@Controller @Service
                if(clazz.isAnnotationPresent(SZController.class)){
                    Object instance = clazz.newInstance();
                    String beanName = toLowerFirstCase(clazz.getSimpleName());
                    ioc.put(beanName,instance );
                } else if (clazz.isAnnotationPresent(SZService.class)) {

                    //1.默认根据beanName类名首字母小写
                    String beanName = toLowerFirstCase(clazz.getSimpleName());

                    //2.使用自定义的beanName
                    SZService service = clazz.getAnnotation(SZService.class);
                    if(!"".equals(service.value())){
                        beanName = service.value();
                    }

                    Object instance = clazz.newInstance();
                    ioc.put(beanName, instance);

                    //3.根据包名.类名作为beanName
                    for (Class<?> i : clazz.getInterfaces()){
                        if(ioc.containsKey(i.getName())){
                            throw new Exception("The beanName is exists!!");
                        }
                        //把接口的类型直接当成key了
                        ioc.put(i.getName(),instance );
                    }

                }else {
                    continue;
                }
            }

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

    }

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

    //扫描出相关的类
    private void doScanner(String scanPackage) {
        URL url = this.getClass().getClassLoader().getResource("/"+scanPackage.replaceAll("\\.","/" ));
        //scanPackage= com.suzao.demo 存储的包路径
        //转换为文件路径,实际上就是把 .替换成/
        //classpath下不仅有.class 文件, .xml文件 .properties文件
        File classPath = new File(url.getFile());

        for (File file :classPath.listFiles()){
            if(file.isDirectory()){
                doScanner(scanPackage + "." + file.getName());
            }else {
                //变成包名.类名
                //Class.forname()
                if(!file.getName().endsWith(".class")){
                    continue;
                }
                classNames.add(scanPackage + "." + file.getName().replace(".class","" ));
            }

        }

    }

    //加载配置文件
    private void doLoadConfig(String contextConfigLocation) {
        //直接从类路径下找到Spring主配置文件所在的路径
        //并且将其读取出来放到Properties对象中
        //相对于scanPackage=com.suzao.demo从文件中保存到内存中
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
            contextConfig.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

转载
作者:程序员技术圈
链接:https://www.jianshu.com/p/2c30f3e6b664

相关文章

网友评论

      本文标题:MINI版Sping-300行代码手写Sping

      本文链接:https://www.haomeiwen.com/subject/wywfuktx.html