Hand-coded to realize the basic functions of Spring, realize IOC, DI, AOP

Acknowledgements: First of all, I would like to thank Mrs. Tom. After watching Mrs. Tom’s video, I have a clearer understanding of Spring's implementation ideas.

First, summarize an overall idea of ​​implementing Spring functions.
The implementation of Spring basically needs to go through the following steps:
configuration phase -> initialization phase -> running
Insert picture description here
phase. After understanding the general process, we will refine each phase.
1. Configuration phase:
we need to configure Servlet, init-param, url-pattern, and create custom annotations in web.xml (in the process of implementing Spring, there is no need to introduce Spring dependencies and annotations need to be customized)
2. Initialization Stage: After the
configuration stage is completed, we need to do the following in the initial painting stage:

  • Read configuration file
  • Initialize the IOC container
  • Scan related classes
  • Instantiate related classes and save them in the IOC container
  • Implement dependency injection
  • Initialize HandlerMapping

3. Running phase After the
user requests, call the doGet/doPost method,
get the request address, take out the requestMapping path,
take the requestMapping path to match the HandlerMapping,
call method.invoke() in reflection, and
return to the front end

Insert picture description here
Code area:

Configuration phase:

1. Configure application.properties

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

2. Configure web.xml

<servlet>
        <servlet-name>MySpring</servlet-name>
        <servlet-class>com.nxw.spring.webmvc.NDispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:application.properties</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>MySpring</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

3. Custom annotations

/**
 * 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 "";
}

Initialization phase:

1. Read the configuration file

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. Initializing the IOC container is to create a map

private Map<String,Object> ioc = new HashMap<String, Object>();

3. Scan related classes

 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. Instantiate related classes and cache them in the IOC container

 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. Complete dependency injection

private void doAutowrited() {
        if(ioc.isEmpty()){return;}
        for (Map.Entry<String, Object> 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. Initialize HandlerMapping

private void doHandlerMapper() {
        if(ioc.isEmpty()){return;}
        for (Map.Entry<String, Object> 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);
            }
        }
    }

Operating phase:
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<String,String []> 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]});

So far the basic functions of Spring have been realized.

For the source code, please go to https://download.csdn.net/download/nxw_tsp/12646030 to download

Guess you like

Origin blog.csdn.net/nxw_tsp/article/details/107483054