简单模拟SpringMVC

大体步骤

1.在 resources/META-INF/services 目录下创建一个文件,文件名为 javax.servlet.ServletContainerInitializer,内容为实现了该接口的初始化类的全限定类名。在这个示例中,我们创建了一个名为 com.it.mvc.util.xxxServletContainerInitializer 的类

2.创建一个实现了 javax.servlet.ServletContainerInitializer 接口的类,例如 com.it.mvc.util.CustomMvcServletContainerInitializer。在这个类中,可以模拟Spring MVC初始化的一些基本步骤,如注册DispatcherServlet、创建Spring容器等

3.在 CustomMvcServletContainerInitializeronStartup 方法中,可以模拟Spring MVC框架的初始化步骤。这包括创建Spring应用上下文、配置DispatcherServlet、扫描并注册Controller类、配置视图解析器等

//模拟springmvc
public class CustomMvcServletContainerInitializer implements ServletContainerInitializer {
    //存储所有的controller key=类名首字母小写 value是controller对象
   static   Map<String,Object> mapControllers =new HashMap<>();

   static   Map<String,Object> mapAutoWried =new HashMap<>();

   //存储所有的方法和他对应的访问路径
   static   Map<String, Method> mapMethods=new HashMap<>();

    @Override
    public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {


        //扫描所有的controller
        try {
            doScan(AppConfig.class);
            autoWried();
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        //实例化一个DispatcherServlet,配置
        doInit(servletContext);
        //解析handler
        parseHandler();
    }

    private void parseHandler() {
        for (String s : mapControllers.keySet()) {
            Object controller = mapControllers.get(s);
            //获取里面所有方法,判断是否加了@RequestHapping
            Method[] methods = controller.getClass().getDeclaredMethods();
            for (Method method:methods) {
                if (method.isAnnotationPresent(RequestMapping.class)){
                    //加了RequestMapping注解。缓存值和方法
                    //获取到方法上的 RequestMapping 注解对象
                    RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                   //requestMapping.value()
                   mapMethods.put(requestMapping.value(), method);//requestMapping.value()  例:"login.do"


                }
            }
           //测试打印
            System.out.println("mapControllers.keySet()"+mapControllers.get(s));


        }
    }

    //实例化DispatcherServlet
    //并且拦截所有请求
    private void doInit(ServletContext servletContext) {
        MyDispatcherServlet myDispatcherServlet = new MyDispatcherServlet();
        ServletRegistration.Dynamic myServlet = servletContext.addServlet("myServlet", myDispatcherServlet);
        myServlet.addMapping("*.do");

    }






    //让类名首字母小写的方法
    public  static String postClassName(String className){
        char[] chars = className.toCharArray();
       chars[0]= (char) (chars[0]+32);
        return  String.valueOf(chars);
    }


}

    模拟@Autowired

  1. 先从bean的容器中获得每个bean的所有属性
  2. 查看属性上是否有@Autowired注解
  3. 在bean容器中查找是否有其属性的类型的bean
  4. 找到一个Bean则直接返回
  5. 找到数量大于1个的话,将多个Bean遍历,与属性名称进行对比,一致则直接返回。
    @SneakyThrows
    public void autoWried(){
        /*@Autowried */
        for (String classname: mapControllers.keySet()){
            //获取每个bean
            Object bean = mapControllers.get(classname);
            Class<?> beanClass = bean.getClass();
            //获取bean的所有属性
            Field[] declaredFields = beanClass.getDeclaredFields();
            for (Field field:declaredFields) {
                int numbers=0;
                System.out.println("*********");
                System.out.println("field"+field.getName()+"      "+field);
                System.out.println("*********");
                //判断哪些属性上加了@Autowired
                MyAutowired annotation = field.getAnnotation(MyAutowired.class);
                if (annotation!=null){
                    field.setAccessible(true);
                    //获取被加注解@Autowried的属性的类型
                    Class<?> type = field.getType();
                    for (String classnames:mapControllers.keySet()){
                        //获得bean中是否有属于加了注解的属性的类型
//                        System.out.println("(mapControllers.get(classnames))"+mapControllers.get(classnames));
//                        System.out.println("type.isInstance(mapControllers.get(classnames))"+type.isInstance(mapControllers.get(classnames)));
                        if (type.isInstance(mapControllers.get(classnames))){
                            //将其放入到mapAutoWried中
                            mapAutoWried.put(classnames,mapControllers.get(classnames));
                            numbers++;
                        }

                    }
                    //*********************
                    if (numbers==0){
                        System.out.println(field+"没有找到对象属性");
                    }else if (numbers==1){
                        for (String names:mapAutoWried.keySet()){
                            if (type.isInstance(mapAutoWried.get(names))){
                          /*      System.out.println("beanClass"+beanClass);
                                System.out.println("mapAutoWried.get(names)"+mapAutoWried.get(names));
                                System.out.println("field"+field);*/
                                field.set(bean,(mapAutoWried.get(names)));
                            }
                        }
                    }else if (numbers>1){
                        //遍历map集合
                        for (String names:mapAutoWried.keySet()){
//                            System.out.println("field.getName()"+field.getName());
//                            System.out.println("names"+names);
                            if (field.getName().equals(names)){

                                field.set(bean,mapAutoWried.get(names));
                            }else{

                                System.out.println("在bean中没有匹配的类型名字");
                            }

                        }
                    }
                    //*********************



                }

            }



        }
        /*@Autowried */
    }

模拟@ComponentScan

private void doScan(Class clazz) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
    if (clazz.isAnnotationPresent(ControllerScan.class)){
        //返回一个表示该注解的实例对象。这个实例对象是 ControllerScan 注解的一个实例,包含了该注解所定义的属性值和方法。
        ControllerScan scan = (ControllerScan) clazz.getAnnotation(ControllerScan.class);

        //scan.value() 方法获取了注解中指定的要扫描的包路径
        String packageName = scan.value();

        //把packageName换成路径
        //replaceAll("\\.", "-")基于正则表达式 替换      replace('.', '-')基于字符替换
        String path = packageName.replace(".", "\\");

        scan(path);

    }else{
        System.out.println("没有扫描的注解");
        return;
    }
}


public void scan(String path) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
    //this.getClass().getClassLoader().getResource("") ==> D:\.....    path:com.it.mvc.config.AppConfig
  //通过 getPath() 获取该 URL 的路径部分,即得到当前目录的绝对路径。
    String path1 = this.getClass().getClassLoader().getResource("").getPath();
    String decode = null;
    try {
        decode = URLDecoder.decode(path1, "UTF-8");
    } catch (UnsupportedEncodingException e) {
        throw new RuntimeException(e);
    }
    //  String root = this.getClass().getClassLoader().getResource("").getPath()+path;
    String root=   decode+path;
    System.out.println("root"+root);
    //通过 File 类创建一个表示指定文件路径的 File 对象,其中 filepath 是所需目录的路径
    File file = new File(root);
    //使用 listFiles() 方法获取指定目录 file 中的所有文件和子目录
    File[] files = file.listFiles();

    for (File temp:files) {
        if (temp.isDirectory()){
            //(temp.getName()获取当前文件名字
       String  pathDirectory=path+"\\"+temp.getName();
            scan(pathDirectory);
        }else {

            //得到类名
            String classname = temp.getName().replaceAll(".class", "");

            //实例化这个对象 packageName是包名
            String packageName = path.replaceAll("\\\\", ".");
            Class<?> aClass = Class.forName(packageName + "." + classname);

            //判断是否加了@Controller
            if (aClass.isAnnotationPresent(Controller.class)) {
                //模拟单例池,实例化对象
                Object controller = aClass.newInstance();
                
                //@Controller 没有赋值
                String s = postClassName(classname);
                mapControllers.put(s, controller);
                System.out.println(" mapControllers"+ mapControllers);
            }
        }
    }

}

实现Servlet,获取请求,发送响应信息

public class MyDispatcherServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     //获取请求的路径
        //req.getServletPath() 返回的是客户端请求中与 Servlet 相关的路径,用于在 Servlet 中处理和分发请求  也返回一个 例:"/login.do"
        String servletPath = req.getServletPath();
        Method method = CustomMvcServletContainerInitializer.mapMethods.get(servletPath);//requestMapping.value()  例:"login.do"

        //获得类名
        String simpleName = method.getDeclaringClass().getSimpleName();

        //获得首字符小写的类名
        String className = CustomMvcServletContainerInitializer.postClassName(simpleName);


        //返回对应的类
        Object controller = CustomMvcServletContainerInitializer.mapControllers.get(className);
        //返回参数的类型
        Class<?>[] parameterTypes = method.getParameterTypes();
     if (parameterTypes.length==0){
         try {
             method.invoke(controller);
         } catch (IllegalAccessException e) {
             throw new RuntimeException(e);
         } catch (InvocationTargetException e) {
             throw new RuntimeException(e);
         }
     }else if (parameterTypes.length==1){
         try {
             method.invoke(controller,req);
         } catch (IllegalAccessException e) {
             throw new RuntimeException(e);
         } catch (InvocationTargetException e) {
             throw new RuntimeException(e);
         }

     }


   //然后根据请求的路径找打对应的方法(存在controller)
     // 方法--调用---缓存这个方法
    //不能在这里通过controller去找,而是一开始找好
    //缓存----map
    //应该会有一个map来缓存方法和对应的请求

    }
}

配置一个内置的Tomcat

public class SpringApplication {

    public static void main(String[] args) throws LifecycleException, UnsupportedEncodingException {
        //记录以后静态文件存储的位置
        String baseDir = Thread.currentThread().getContextClassLoader().getResource("public").getPath();
        baseDir = URLDecoder.decode(baseDir, "UTF-8");
        //实例化tomcat对象
        Tomcat tomcat = new Tomcat();
        //指定项目访问的路径项目名
        //指定静态资源位置
        System.out.println("baseDir"+baseDir);
        tomcat.addWebapp("/x",baseDir);
        //设置端口号
        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
        connector.setPort(8080);
        connector.setThrowOnFailure(true);


        //应用端口号
        tomcat.getService().addConnector(connector);
        tomcat.setConnector(connector);


        //启动tomcat
        tomcat.start();
        //阻塞等待
        tomcat.getServer().await();
    }
}

Controller类

@Controller
public class IndexController {

    @MyAutowired
   public HelloController helloController;

    @MyAutowired
    public Person teacher;

    @RequestMapping("/login.do")
    public String login(HttpServletRequest request){
        String name = request.getParameter("name");
        System.out.println(name);
        System.out.println("login.do");
      return "";
    }

    @Override
    public String toString() {
        return "IndexController{" +
                "helloController=" + helloController +
                ", person=" + teacher +
                '}';
    }
}
@Controller
public class HelloController {


}

模拟注解

//扫描包的注解
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
    String value() default "";
}
@Retention(RetentionPolicy.RUNTIME)
public @interface ControllerScan {
    String value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)//自定义注解只能被应用到类的字段
public @interface MyAutowired {
}
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
    String value();
}

最后

在resourcesMETA-INF/services下,写入

com.it.mvc.util.CustomMvcServletContainerInitializer

这样了指定了 Servlet 3.0+ 容器初始化时要执行的类。。它允许库开发人员在容器启动时注册Servlet、Filter、Listener等组件,而无需在 web.xml 文件中显式配置。可以在代码中以程序化的方式注册这些组件

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值