zoukankan      html  css  js  c++  java
  • 仿真手写Spring核心原理v1.0

    一、实现思路

    二、工程结构

    三、自定义配置

    1、配置application.properties文件

    为了解析方便,用application.properties来代替Spring的核心配置文件application.xml,具体配置内容如下:

    2、配置web.xml文件

    所有依赖于Web容器的项目,都是从读取web.xml文件开始的。先配置好web.xml中的内容:

    其中MyDispatcherServlet是自己模拟Spring实现的核心功能类。

    四、自定义注解

    @MyController注解:

     

    @MyRequestMapping注解:

     @MyRequestParam注解:

     @MyService注解:

     @MyAutowired注解:

     另外,这里再补充些Java中元注解相关的知识:

    五、在业务代码上加上我们自定义的注解

    定义一个DemoService接口:

    定义一个DemoServiceImpl实现类:

    定义一个DemoController类:

    至此,配置阶段就已经完成。

    六、容器初始化和运行阶段

    定义一个MyDispatcherServlet类(仿造SpringMVC的前端控制器):

    (1) 实现v1版本

    初始化阶段:

    采用了常用的设计模式(工厂模式、单例模式、委派模式、策略模式),将init()方法中的代码进行封装。按照之前的实现思路,先搭基础框架,再填补具体代码:

    声明全局的成员变量,其中IOC容器就是注册式单例的具体案例:

     实现doLoadConfig()方法:

     实现doScanner()方法:

    实现doInstance()方法,该方法就是工厂模式的具体体现:

    为了处理方便,自己实现了toLowerFirstCase()方法,用来实现类名首字母小写,具体代码如下:

    实现doAutowired()方法:

    实现initHandlerMapping()方法,handlerMapping就是策略模式的应用案例(省去大量if...else if...):

    运行阶段:

    doPost()方法中用了委派模式,委派模式的具体逻辑在doDispatch()方法中:

    (2)实现v2版本:

    在v1版本中,基本功能已经完全实现,但代码的优雅程度还不如人意。比如url参数还不支持强制类型转换,在运行阶段还需要通过反射获取Method方法对应的Controller类名、以及加了@RequestParam注解的方法参数,消耗系统性能。在v2版本中继续优化,把运行阶段的反射调用前移到初始化阶段。

    首先修改web.xml文件,将v1版本的MyDispatcherServlet改成v2版本:

    接着改造HandlerMapping,在真实的Spring源码中,HandlerMapping其实是一个List而非Map,List中的元素是一个自定义的类型。现在我们来仿真写一段代码,先定义一个内部类HandlerMapping:

    然后,优化HandlerMapping容器的结构,代码如下:

    修改initHandlerMapping()方法:

    修改doDispatch()方法:

    定义一个convert()方法,主要负责url参数的强制类型转换:

    定义一个getHandler()方法,主要负责匹配handlerMapping容器中是否有对应的url:

    至此,手写Mini版SpringMVC框架就已全部完成。

    运行结果演示:

    测试query()方法:

    测试add()方法:

    测试remove()方法: 

    当然,真正的Spring要复杂很多,这里主要通过手写的形式,了解Spring的基本设计思路以及设计模式如何应用,后续会继续手写更加高仿真的Spring v2.0版本。

    七、面试题

    1、提问:Spring中的Bean是线程安全的吗?

    思考,Spring中的Bean是哪里来的?通过包扫描对应的类,利用反射实例化出来的,并且缓存在IOC容器中。Spring并没有对你的Bean做任何处理,Bean是不是线程安全取决于用户编写的Bean本身

    2、提问:Spring中的Bean是如何被回收的?

    其实就是Spring中的Bean的生命周期问题,Bean设置不同的作用域对应不同的生命周期,不同的生命周期对应不同的回收时间。作用域包括singleton、prototype、request、session、globalSession。可以在xml中配置,例如:

    如果设置为prototype,即多例模式。每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行new XXBean(),用完了就被GC回收了。

    如果设置为request,那对应的Bean就会存在于一次请求里面。

    如果设置为session,那对应的Bean就会存在于一次会话里面。

    如果设置为globalsession,那对应的Bean就会存在于整个Web应用中。

    Spring中的Bean默认是singleton,即单例模式。GC回收原则:当Bean没有被任何地方引用的时候。所以,当Spring本身不消失,自然而然IOC容器也不会消失,那么IOC容器中对应的Bean实例也不会消失。所以,此时的Bean是全局存在的,或者说随着Spring的存亡而存亡。

  • 相关阅读:
    git pull fatal: refusing to merge unrelated histories
    Git报错:Your branch is ahead of 'origin/master' by 1 commit
    java读取大文件内容到Elasticsearch分析(手把手教你java处理超大csv文件)
    Java try catch语句块中try()的括号中代码作用
    Redis这15个“雷坑”,别问我咋知道的……
    MySql死锁
    MQ限流应用
    什么是 JWT -- JSON WEB TOKEN
    mybatis-plus查询指定字段
    数据库炸了----我就重启了一下啊(Communications link failure)
  • 原文地址:https://www.cnblogs.com/ZekiChen/p/12782443.html
Copyright © 2011-2022 走看看