手撸代码实现还原Spring基本功能,实现IOC,DI,AOP

致谢:首先感谢Tom老师,看了Tom老师的视频后,对Spring的实现思路有了更清晰的认识。

首先先总结一下实现Spring功能的一个整体思路。
实现Spring基本需要经过一下步骤:
配置阶段–>初始化阶段–>运行阶段
手撸代码实现还原Spring基本功能,实现IOC,DI,AOP_第1张图片
大致流程了解之后我们再细化每个阶段。
1、配置阶段:
我们需要在web.xml中配置Servlet,init-param,url-pattern,以及新建自定义注解(实现Spring过程中,不需要引入Spring依赖,注解也需要自己自定义)
2、初始化阶段:
配置阶段完成之后,我们需要在初始画阶段做以下处理:

  • 读取配置文件
  • 初始化IOC容器
  • 扫描相关的类
  • 实例化相关的类,并保存到IOC容器中
  • 实现依赖注入
  • 初始化HandlerMapping

3、运行阶段
用户请求后调用doGet/doPost方法,
拿到请求地址,取出requestMapping路径,
拿resquestMapping路径与HandlerMapping匹配,
反射调用method.invoke(),
返回前端

手撸代码实现还原Spring基本功能,实现IOC,DI,AOP_第2张图片
代码区:

配置阶段:

1、配置application.properties

#要扫描的包#
scanPackage=com.nxw.demo

2、配置web.xml


        MySpring
        com.nxw.spring.webmvc.NDispatcherServlet
        
            contextConfigLocation
            classpath:application.properties
        
        1
    
    
        MySpring
        /*
    

3、自定义注解

/**
 * Cntroller注解
 * @author Silence
 */

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

}

/**
 * 业务逻辑注入注解
 * @author Silence
 */

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

/**
 * 请求url
 * @author Silence
 */

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

/**
 *  请求参数映射
 * @author Silence
 *
 */

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

/**
 * 自动注入注解
 * @author Silence
 */

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

初始化阶段:

1、读取配置文件

private void doLoadConfig(String contextConfigLocation) {
        //classpath下找到application.properties配置文件,并且读取出来
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation.replaceAll("classpath:",""));
        try {
            contextConfig.load(is);
        } catch (IOException e) {
            System.err.println("没有找到配置文件");
            e.printStackTrace();

        }finally {
            if(null != is){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

2、初始化IOC容器 就是创建一个map

private Map ioc = new HashMap();

3、扫描相关的类

 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 {
                //判断是不是以.class结尾的文件
                if(!file.getName().endsWith(".class")) {continue;}
                //拼接全类名,可以通过Class.forName()
                String className = (scanPackage + "." + file.getName().replace(".class", ""));
                //存入list
                classNames.add(className);
            }
        }
    }

4、实例化相关的类,并且缓存到IOC容器中

 private void doInstance() {
        //扫描刚刚扫描的类
        if(classNames.isEmpty()){return;}
        for (String className : classNames) {
            try {
                Class clazz = Class.forName(className);

                if(clazz.isAnnotationPresent(NController.class)) {
                    String beanName = toLowerFirstCase(clazz.getSimpleName());
                    Object instance = clazz.newInstance();
                    ioc.put(beanName, instance);
                }else if(clazz.isAnnotationPresent(NService.class)){
                    String beanName = toLowerFirstCase(clazz.getSimpleName());
                    //自定义beanName,不同包下相同类名
                    NService service = (NService) clazz.getAnnotation(NService.class);
                    if(!"".equals(service.value())){
                        beanName = service.value();
                    }

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

                    //如果是接口,用它的实现类赋值
                    for (Class i : clazz.getInterfaces()) {
                        if (ioc.containsKey(i.getName())){
                            throw new Exception("This beanName already exists!");
                        }
                        //匹配接口类型
                        ioc.put(i.getName(),instance);
                    }

                }else{
                    continue;
                }

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

5、完成依赖注入

private void doAutowrited() {
        if(ioc.isEmpty()){return;}
        for (Map.Entry entry : ioc.entrySet()) {
            //把所有的private,public,default,prodected都拿到
            Field fields [] = entry.getValue().getClass().getDeclaredFields();

            for (Field field : fields) {
                if(!field.isAnnotationPresent(NAutowired.class)){
                    continue;
                }
                NAutowired autowired = field.getAnnotation(NAutowired.class);
                String beanName = autowired.value().trim();
                if("".equals(beanName.trim())){
                    beanName = field.getType().getName();
                }

                //暴力访问,可以通过反射拿到所有的private,public,default,prodected
                field.setAccessible(true);

                try {
                    field.set(entry.getValue(),ioc.get(beanName));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
   }

6、初始化HandlerMapping

private void doHandlerMapper() {
        if(ioc.isEmpty()){return;}
        for (Map.Entry entry : ioc.entrySet()) {
            Class clazz = entry.getValue().getClass();

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

            String baseUrl = null;
            if(clazz.isAnnotationPresent(NRequestMapping.class)){
                NRequestMapping requestMapping = clazz.getAnnotation(NRequestMapping.class);
                baseUrl = requestMapping.value();

            }

            for (Method method : clazz.getMethods()) {
                if(!method.isAnnotationPresent(NRequestMapping.class)){continue;}
                NRequestMapping requestMapping = method.getAnnotation(NRequestMapping.class);
                String url = ("/" + baseUrl + "/" + requestMapping.value()).replaceAll("/+","/");
                handleMapping.put(url,method);
                System.out.println("Mapping : " + url + " , "+method);
            }
        }
    }

运行阶段:
doPost()

 //7、调用具体的方法
        String url = req.getRequestURL().toString();
        String contextPath = req.getContextPath();
        String url1 = url.replaceAll("http://localhost:8080","");

        System.out.println("URL : "+url);
        if(!this.handleMapping.containsKey(url1)){
            resp.getWriter().write("404 Not Found!");
            return;
        }

        Map params = req.getParameterMap();
        Method method = this.handleMapping.get(url1);
        String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
        method.invoke(ioc.get(beanName),new Object[] {req,resp,params.get("name")[0],params.get("addr")[0]});

至此Spring基本功能已经实现。

源码请到 https://download.csdn.net/download/nxw_tsp/12646030 下载

你可能感兴趣的:(手撸代码实现还原Spring基本功能,实现IOC,DI,AOP)