zoukankan      html  css  js  c++  java
  • 手写 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 也就是运行时有效,不然无法确定是否加了注解 ,其次就是用到了大量的反射及代理知识,需要同学们巩固巩固。

  • 相关阅读:
    央企国管公积金提取。本人实际经历 2013年6月5日
    一台机器开启多个tomcat7 绿色版
    给Repeater、Datalist和Datagrid增加自动编号列
    有关比较分析的MDX
    01[转Cognos8第三讲]Cognos8的安装与配置
    BI前端工具对比
    转摘cognos学习笔记
    04[转Cognos8第四讲]权限配置(2)
    如何更改oracle字符集
    IBM Cognos BI 最佳实践: 定制 IBM Cognos 8 UI
  • 原文地址:https://www.cnblogs.com/lufei33180/p/13620162.html
Copyright © 2011-2022 走看看