大体步骤
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.在 CustomMvcServletContainerInitializer
的 onStartup
方法中,可以模拟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
- 先从bean的容器中获得每个bean的所有属性
- 查看属性上是否有@Autowired注解
- 在bean容器中查找是否有其属性的类型的bean
- 找到一个Bean则直接返回
- 找到数量大于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
文件中显式配置。可以在代码中以程序化的方式注册这些组件