为了提升自己的技术所以没事自己通过百度和找资料来学习了一下spring的思想所以就记录一下
这个是比较简单的一种,有点乱。我按照自己的理解写的有注释,但是不知道是不是都是正确,如果有错误希望批评指正谢谢。
总结:
1:首先要了解spring的整体:
主要作用就是ioc di mvc 和 aop ,所以要从这几个方面去入手。
想要指导思想就要从头开始,所以就总结了一下整个流程,如下:
一:首先要配置spring
1:配置web.xml中的DispatcherServlet
2:设定 init-param 配置扫描的路径:contextConfigLocation = classpath:application.xml (我写的是application.properties)
3:配置url-pattern :/*
4:配置Annotation :@Controller @RequestMapping @Service @Autowrited @RequestParam
二:继承HttpServlet并重写 init()方法开始初始化ioc
1:首先加载配置文件
(1):创建读取配置文件对象Properties
(2):以流的形式获取配置文件名称
(3):加载配置文件
(4):通过配置文件对象获取对应键值对键的值(获取之后是一个字符串:路径)
(5):开始通过路径扫描所有此路径下的文件
-1:先处理路径中把符号 “.”转换成“/”
-2:通过文件类对象File 在指定路径下获取所有的文件的Filed对象
-3:遍历所有对象,并通过判断是否是文件夹之后进行递归此方法已达到扫描到所有文件
然后在循环里面进行判断当文件是以.class结尾的就开始组合这个对象名称(通过全路径+对象名称 并去掉.class后缀)
-4:把这个对象全称保存到一个Hashmap集合里面(Map<Striing,Object>)
(6):遍历这个集合并开始ioc容器的真正初始化和di的注入
-1:去除所有kay,并判断是否是以全路径保存的(contains("."))
-2:获取该类class对象,并判断是哪种注解进行的修饰(分为两种Controller Requestmapping)
-3:分别把这三种对象和对象名称保存到集合里面
-4:获取集合里面所有的value值(map.values()),并遍历所有对象
--1:判断对象是否为空,是 跳过
--2:判断此class对象是否被@Controller注解修饰,是继续进行
--3:获取此class对象所有的属性
--4:遍历所有属性
--5:判断是否有DI注解@Autowrited,有继续进行
--6:获取此注解对象field.getAnnotation(Autowrited.class)
--7:判断注解是否有自定义
--8:没有自定义就按照类型名称进行DI注入
(7):重写dopost()he doGet()方法
(8):重写doDispatch()自定义方法(此方法实现了mvc的路径和方法的映射)
上个面是我个人的流程总结,下面是写的代码,通过网络课程 以及 百度搜索 写了一遍最简单的spring中的ioc和di的以及mvc的代码
/** * @Description: 第一个版本的spring手写 * @Author: com.syn.v1 * @Create: 2020/4/2 0002 10:28 *///1首先需要配置servlet的入口(web.xml:主要配置DispatcherServlet的入口和初始化的配置文件等)//2编写Annotation(@Controller @RequestMapping @Service @Autowritde @RequestParam) /*2在编写主类 DispatcherServlet 主要有一些几步: 1:继承 DispatcherServlet 初始化init方法:ioc主要就在这里实现 2:重写dopst和doget方法 并 完成 mvc的功能(另外编写一个方法专门实现) */public class SYNDispatcherServlet extends HttpServlet{ //创建容器 private Map<String,Object> mapping = new HashMap<>(); @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 { try { doDispatch(req,resp); } catch (Exception e) { resp.getWriter().write("500 Exception "+ Arrays.toString(e.getStackTrace())); } } //这个方法是实现mvc的 private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception{ //获取除去host(域名或者ip)部分的路径 String url = req.getRequestURI(); //返回工程名部分,如果工程映射为/,此处返回则为空 /*request.getRequestURL() http://localhost:8080/jqueryLearn/resources/request.jsp request.getRequestURI() /jqueryLearn/resources/request.jsp request.getContextPath()/jqueryLearn*/ String contextPath = req.getContextPath(); //拼接路径,去掉多余的项目根路径和多余的斜杠 url=url.replace(contextPath,"").replaceAll("/+","/"); //判断map容器中是否有指定的路径 if(!this.mapping.containsKey(url)){resp.getWriter().write("404 Not Found!");return;} //获取方法 Method method = (Method)this.mapping.get(url); //获取所有参数 Map<String, String[]> parames = req.getParameterMap(); //初始化方法 method.invoke(this.mapping.get(method.getDeclaringClass().getName()),new Object[]{req,resp,parames.get("name")[0]}); } //这个方法是实现初始化ioc容器和di的注入 @Override public void init(ServletConfig config) throws ServletException { InputStream is=null; try { //一:加载配置文件 //1读取配置文件 //Properties 该类主要用于读取Java的配置文件 Properties configContext=new Properties(); //以流的形式获取配置文件的名称 is=this.getClass().getClassLoader().getResourceAsStream(config.getInitParameter("contextConfigLocation")); //加载指定配置文件名称(以键值对形式) configContext.load(is); //获取指定键的值 String scanPackage = configContext.getProperty("scanPackage"); //扫描此路径下的所有文件并保存文件名称 doScanner(scanPackage); //2:初始化容器IOC //遍历所有的mapping,通过 className 开始创建 需要初始化的 对象 for (String className:mapping.keySet()){ //先判断是否是全路径的文件名称 if(!className.contains(".")){continue;} //通过Class.forName()方法 获取指定名称的class对象 Class<?> Clazz = Class.forName(className); //判断该类对象是否有注解SYNController if(Clazz.isAnnotationPresent(SYNController.class)){ //通过相对应的名称 创建对象并放入到 map 中 mapping.put(className,Clazz.newInstance()); //这个是在类上面requestMapping映射的路径,默认为空 String baseUrl=""; //判断类上面是否有RequestMaping注解 if(Clazz.isAnnotationPresent(SYNRequestMapping.class)){ //获取此类上面的RequestMaping对象 SYNRequestMapping RequestMaping = Clazz.getAnnotation(SYNRequestMapping.class); //赋值给baseUrl baseUrl=RequestMaping.value(); } //获取此类多有方法 Method[] methods = Clazz.getMethods(); //遍历方法 for (Method method:methods){ //方法上是否有SYNRequestMapping 注解,没有跳过 if(!method.isAnnotationPresent(SYNRequestMapping.class)){continue;} //获取方法上的SYNRequestMapping对象 SYNRequestMapping RequestMapping = method.getAnnotation(SYNRequestMapping.class); //组合mvc映射的路径,并通过replaceAll去掉多余的斜杠 String url =(baseUrl+"/"+RequestMapping.value()).replaceAll("/+","/"); //把映射路径放入到mapping(map) mapping.put(url,method); System.out.println("Mapped"+url+","+method); } //判断是否有SYNService注解 }else if(Clazz.isAnnotationPresent(SYNService.class)){ //获取此类上面的SYNService注解对象 SYNService synService = Clazz.getAnnotation(SYNService.class); //获取注解上面的自定义的内容(可以为空) String beanName = synService.value(); //判断如果为空则说明需要用到类型注入 if("".equals(beanName)){beanName=Clazz.getName();} //初始化接口 Object instance = Clazz.newInstance(); //放入到map中 mapping.put(beanName,instance); //遍历所有 for (Class<?> i:Clazz.getInterfaces()){ mapping.put(i.getName(),instance); } }else {continue;} } //遍历所有map中的对象 for (Object object:mapping.values()){ //如果对象为空跳过 if(object==null){continue;} //不为空获取对象 Class<?> clazz = object.getClass(); if(clazz.isAnnotationPresent(SYNController.class)){ //获得类里面声明的所有字段 Field[] fields = clazz.getDeclaredFields(); //遍历这些 for (Field field:fields){ //判断是否被SYNAutowired注解修饰 if(!field.isAnnotationPresent(SYNAutowired.class)){continue;} //获取注解对象 SYNAutowired SYNautowired = field.getAnnotation(SYNAutowired.class); //获取注解上的内容 String beanName=SYNautowired.value(); if("".equals(beanName)){beanName=field.getType().getName();} field.setAccessible(true); //3:注入DI field.set(mapping.get(clazz.getName()),mapping.get(beanName)); } } } }catch (Exception e){ e.printStackTrace(); }finally { if(is !=null){ try { is.close(); }catch (IOException e){ e.printStackTrace(); } } } System.out.println("GP MVC Framework is init"); } //通过传入的路径扫描所有的文件并放入到map集合里面 private void doScanner(String scanPackage) { //先把获取的键值对的值转换一下(把com.syn.demo 转换成 com/syn/demo),变成项目的的文件路径 URL resource = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/")); //通过文件对象获取此路径下所有的文件 //resource.getFile() 返回指定目录下该文件的file对象 File classDir =new File(resource.getFile()); for (File file:classDir.listFiles()){ //判断file是否是目录 if(file.isDirectory()){ doScanner(scanPackage+"."+file.getName()); }else { //判断文件是否是以 .class 结尾的,不是直接跳过 if(!file.getName().endsWith(".class")){continue;} //把获取的文件对象整理成以全路径开头的文件名称(路径+去掉 .class 的组合) String clazzName=(scanPackage+"."+file.getName().replace(".class","")); mapping.put(clazzName,null); } } }} web.xml里面的配置如下:(主要就是servlet,init-param,servlet-mapping)
<web-app> <display-name>Archetype Created Web Application</display-name> <servlet> <servlet-name>synmvc</servlet-name> <servlet-class>com.syn.v1.SYNDispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>application.properties</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>synmvc</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping></web-app>
至于Annoattion我就不写了,百度应该都有,有什么问题可以给我留言。
原文地址:https://www.cnblogs.com/songyinan/p/12623340.html