手写 spring ioc

 一、实现自己的servlet,模拟DispatcherServlet

package com.sl;

import com.sl.mvcframework.annotation.SLAutowired;
import com.sl.mvcframework.annotation.SLController;
import com.sl.mvcframework.annotation.SLRequestMapping;
import com.sl.mvcframework.annotation.SLService;

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

/**
 * Author: sl
 * Date: 2020/8/21-20:31
 * Description:描述信息
 */

public class HandSpringServlet  extends HttpServlet {

    private static final String location="contextConfigLocation";

    private Properties properties=new Properties();

    private List<String> classNames=new ArrayList<String>();

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

    private Map<String,Method> handerMapping=new HashMap<String,Method>();


    @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 {
        doDispach(req,resp);
    }

    private void doDispach(HttpServletRequest req, HttpServletResponse resp) {
        if(this.handerMapping.isEmpty()){
            return;
        }
        String url=req.getRequestURI();
        String contextPath=req.getContextPath();
        url=url.replace(contextPath,"").replaceAll("/+","/");
        if(!this.handerMapping.containsKey(url)){
            try {
                resp.getWriter().write("404 页面找不到了");
                return;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        Map<String,String[]> parameterMap=req.getParameterMap();
        Method method=this.handerMapping.get(url);
        Class<?> []methodParameterTypes=method.getParameterTypes();

        Object []paramValue=new Object[methodParameterTypes.length];
        for(int i=0;i<methodParameterTypes.length;i++){
            Class type=methodParameterTypes[i];
            if(type==HttpServletRequest.class){
                paramValue[i]=req;
                continue;
            }else if(type==HttpServletResponse.class){
                paramValue[i]=resp;
                continue;
            }else if(type==String.class){
                for(Map.Entry<String,String[]> entry:parameterMap.entrySet()){
                    String value=Arrays.toString(entry.getValue()).replaceAll("\[|\]","").replaceAll("\s",",");
                    paramValue[i]=value;
                }
            }else if(type==Integer.class){
                for(Map.Entry<String,String[]> entry:parameterMap.entrySet()){
                    Integer value=Integer.parseInt(Arrays.toString(entry.getValue()).replaceAll("\[|\]","").replaceAll("\s",","));;
                    paramValue[i]=value;
                }
            }
        }

        String beanName=method.getDeclaringClass().getSimpleName();
        try {
            method.invoke(this.ioc.get(beanName),paramValue);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

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

        //2.扫描所有相关的类
        doScanner(properties.getProperty("package"));

        //3.初始化所有类相关的实例,并保存到ioc容器中
        doInstance();

        //4.依赖注入
        doAurowaired();

        //5.构造handerMapping
        doHanderMapping();

        System.out.println("SL mvcframework 初始化完成");
    }

    private void doHanderMapping() {
        if(ioc.isEmpty()){
            return;
        }
        for(Map.Entry<String,Object> entry:ioc.entrySet()){
            Class<?> clazz=entry.getValue().getClass();
            if(!clazz.isAnnotationPresent(SLController.class)){
                continue;
            }
            String baseUrl="";
            if(clazz.isAnnotationPresent(SLRequestMapping.class)){
                SLRequestMapping requestMapping=clazz.getAnnotation(SLRequestMapping.class);
                baseUrl=requestMapping.value();
            }
            Method []methods=clazz.getMethods();
            for(Method method:methods){
                if(!method.isAnnotationPresent(SLRequestMapping.class)){
                    continue;
                }
                SLRequestMapping requestMappingMethod=method.getAnnotation(SLRequestMapping.class);
                String url=("/"+baseUrl+"/"+requestMappingMethod.value()).replaceAll("/+","/");
                handerMapping.put(url,method);
                System.out.println(url+":"+method);
            }
        }
    }


    private void doAurowaired() {

        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(SLAutowired.class)){
                    return;
                }
                SLAutowired autowired=field.getAnnotation(SLAutowired.class);
                String beanName=autowired.value();
                if("".equals(beanName)){
                    beanName=field.getType().getSimpleName();
                }
                field.setAccessible(true);
                try {
                    field.set(entry.getValue(),ioc.get(beanName));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }

        }
    }

    private void doInstance() {
        if(classNames.size()==0){
            return;
        }
        try {
            for(String className:classNames){
                Class<?> clazz=Class.forName(className);
                if(clazz.isAnnotationPresent(SLController.class)){
                    String beanName=clazz.getSimpleName();
                    ioc.put(beanName,clazz.newInstance());
                }else if(clazz.isAnnotationPresent(SLService.class)){
                    SLService service=clazz.getAnnotation(SLService.class);
                    String beanName=service.value();
                    beanName=clazz.getSimpleName();
                    if(!"".equals(beanName.trim())){
                        ioc.put(beanName,clazz.newInstance());
                        continue;
                    }
                    /*Class<?> [] interfaces=clazz.getInterfaces();
                    for(Class<?> cz:interfaces){
                        ioc.put(beanName,cz.newInstance());
                    }*/
                }else{
                    continue;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void doScanner(String aPackage) {
       URL url= this.getClass().getClassLoader().getResource("/"+aPackage.replaceAll("\.","/"));
        File dir=new File(url.getFile());
        for(File file:dir.listFiles()){
            if(file.isDirectory()){
                doScanner(aPackage+"."+file.getName());
            }else{
                classNames.add(aPackage+"."+file.getName().replaceAll(".class",""));
            }
        }
    }

    private void doLoadConfig(String initParameter) {
        InputStream ins=this.getClass().getClassLoader().getResourceAsStream(initParameter);
        try {
            properties.load(ins);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(ins !=null){
                try {
                    ins.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public void destroy() {
        super.destroy();
    }


}

二、实现自己的注解类

@Target({ElementType.FIELD})//注解用于什么地方
@Retention(RetentionPolicy.RUNTIME) //定义注解的生命周期,始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。
@Documented
public @interface SLAutowired {
    String value() default "";
}


@Target({ElementType.TYPE})//注解用于什么地方
@Retention(RetentionPolicy.RUNTIME) //定义注解的生命周期,始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。
@Documented
public @interface SLController {

    String value() default "";
}


@Target({ElementType.TYPE,ElementType.METHOD})//注解用于什么地方
@Retention(RetentionPolicy.RUNTIME) //定义注解的生命周期,始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。
@Documented
public @interface SLRequestMapping {
    String value() default "";
}


@Target({ElementType.PARAMETER})//注解用于什么地方
@Retention(RetentionPolicy.RUNTIME) //定义注解的生命周期,始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。
@Documented
public @interface SLRequestParam {
    String value() default "";
}

@Target({ElementType.TYPE})//注解用于什么地方
@Retention(RetentionPolicy.RUNTIME) //定义注解的生命周期,始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。
@Documented
public @interface SLService {
    String value() default "";
}

三、实现测试crontoller 和service

@SLController
@SLRequestMapping("/demo")
public class DemoController {

    @SLAutowired
    DemoService demoService;

    @SLRequestMapping("/query")
    public void query(HttpServletRequest request, HttpServletResponse response,@SLRequestParam("name") String name) throws IOException {
        String result=demoService.getName(name);
        response.getWriter().write(result);
    }

    @SLRequestMapping("/add")
    public void add(HttpServletRequest request, HttpServletResponse response,@SLRequestParam("a") Integer a,@SLRequestParam("b")Integer b) throws IOException {
        response.getWriter().write("a+b="+(a+b));
    }

    @SLRequestMapping("/remove")
    public void remove(HttpServletRequest request, HttpServletResponse response,@SLRequestParam("name") String name) throws IOException {
        response.getWriter().write("已经删除了,success");
    }

}



@SLService
public class TestService {
    public String getName(String name){
        return "您的名字是"+name;
    }
}

四、总结

1.实现spring iot 其核心其实就是servlet 的实现,在web.xml中配置了servlet 的启动参数后,程序按照servlet 中的init 方法加载ioc过程

2、当servlet 启动后,首先会去加载web.xml init-parpameter 中配置的配置文件,按照路径扫描包下的class 文件,并按照反射实现类的初始化并将其存入map中

3、上一步其实就是初始化bean 容器,当bean 完成初始化后,紧接着就是完成依赖注入,就是将类中标注了@Autowaire 注解的累变量给其赋值,相当于new对象,换言之就是给cronller中的service 变量赋值

4、当完成对象注入后,接着就是url 映射,也就是handermapping,意思就是给crontroller 中加了requestMapping 注解的方法增加地址映射,如可以通过request 请求中的url /test/getName 找到目标方法

5、以上就完成了spring iot 最基本的bean 初始化、依赖注入、地址映射,最后就是实现doget dopost ,按照request 中的请求地址以及参数,通过反射调用目标方法

6、需要注意的地方:实现注解一定要加runtime 也就是运行时有效,不然无法确定是否加了注解 ,其次就是用到了大量的反射及代理知识,需要同学们巩固巩固。

原文地址:https://www.cnblogs.com/lufei33180/p/13620162.html