图1
图2
图2是传统模式下springmvc这样做的。
springboot和springmvc他们的结合点事那个,传统的springmvc 需要做的事情是初始化spring环境,读取xml,注册servlet,扫描controller,2json解析,3视图解析器,那么问题来了,springboot 没有web.xml 没有任何xml。没有办法做以上的事情,他是咋么做到的呢?他是如何内嵌tomcat呢.
图3
图4
图5
图6
图7
springboot如何把dispatchServlet放进spring 容器。 spring官方文档推荐用这种方式。我们需要用这种方式来完成spring容器的初始化,创建DispatcherServlet 并且注册到spring容器中。换句话说我们用这种方式来代替web.xml所做的工作。 web.xml在容器启动的时候回进行spring容器初始化,DispatcherServlet注册等工作。那问题来了,我们怎么确定容器启动的时候回调 WebApplictionInitializer类的onStartup方法呢。why会调用。
图8
我们可以发现 WebApplicationInitializer类型是有spring 开发的。 难道tomcat容器在启动的时候,循环遍历所有实现 WebApplicationInitializer 类的,然后依次调用WebApplicationInitializer 的onStartUp方法吗,那么这样这个tomcat不就严重依赖WebApplicationInitializer这个类了吗,显然是不可能的。那怎么办?
图9
图9借助于servlet的3.0规范,只要你在classpatch目录下,配置了META-INFO /services/这个目录下配置了一个类,这个类实现了ServletContainerInitializer类的onStartUp方法。tomcat(其他容器)启动的时候就会调用这个方法。
图10
我们自己的类是 WebApplicationInitializer的子类,而这个是调用SprinServletContainerInitializer的onStartup方法,两个类啊。
其实是这样 SprinServletContainerInitializer 的onStartup方法在被调用的时候,会把所有实现了WebApplicationInitializer的子类做为参数放在一个set中传进来。然后依次for循环遍历调用。 @通过HandlesTypes注解来获得WebApplicationInitializer 类。
图11
图12
这个属于tomcat的方法。
图13
spring 3.0后提供javaCofig方式。利用这种方式,springboot 完成了替代 扫描的功能。
图14
图14标绿的完成了spring 的初始化。新问题来了如何内嵌tomcat呢。当我们启动一个tomcat。需要运行一个exe文件->main 方法(tomcat)
tomcat来启动一个线程来跑我们的tomcat。 那么我们怎么在项目中执行tomcat的main方法呢,可以通过mave依赖把tomcat依赖到我们的项目中。
依赖这个jar包。我们就可以来执行tomcat的main方法。
图15
14行为什么要await().因为要等待服务连接,如果不写tomcat回一闪 而过。
图16
我们发现tomcat已经启动了。
图17
当我们加上打印记录的时候发现tomcat启动并没有打印这段代码,证明没有调用这个类,不是吧,上面不是说可以调用吗,其实并不是tomcat只有是web项目的时候他才会使用servlet3.0的新规范调用这个onStartup方法。 但是问题来了,springboot也不是web项目啊,难道你非要让我健一个web项目才可以吗
图18
此时正常打印了,证明可以调用onStartup方法了。
图19
修改成这样,刚才那个异常就解决了。但是又有新的异常了。jsp的异常。如果是web项目的话默认执行一个jsp引擎。
图20
这个问题也可以解决。只是缺少一个tomcat的jsp的jar包,加上就可以了。
图21
图22
打印controller了,证明访问到controller层了。但是页面是404 这个不影响,因为没有jsp所以这样。现在很少用jsp。所以正常的情况下我们已经完成了类似springBoot的功能了。这样的内嵌tomcat已经完成了。
图23
对controller进行修改。
图24
当我们访问map的时候报不能转化map的错误信息。
图25
xml 我们是这样配置 json转化的。
springboot 类型的咋玩?
添加fastjson 包依赖。
图26
实现WebConfigurer 类,重写configureMessageConverters方法。
图27
但是我们执行完发现并没有打印======== 为什么没有执行呢,
加上@EnableWebMvc 就可以正常显示了。
但是现在问题虽然解决了。springboot不需要jsp啊,我也不想添加jsp的jar 包,还有其他的方法吗。本来我springboot 不是web项目,为啥一定要添加jar包,一定要按web项目运行呢,?
图28
如果我把这个再改为addContext() 这样的话,onStartup的方法就不能调用。但是jsp那个异常就不报错了。
把这个onStartup中的方法赋值到图28中就能解决这个问题。
图29
图28改成这样。那么就可以正常访问,一切正常了。jsp那个异常也解决了。map也能正常返回了,也不是web 项目了。
图29
根据官网我们发现似乎少加ac.refresh();
图30
真正核心的代码ac.refresh() 这个要加上
图31
图31 refresh()是核心代码。所以要加上,但是遗憾的是加上以后 加上之后会报错。
图32
这是为什么呢,当我们执行ac.refresh()方法时候回解析Appconfig类的@EnableWebMvc注解 ,这个过程需要解析Servlet 但是这个时候DistpatechServlet还没有创建呢,所以报这个错误。
图33
图34
所以去掉就一切正常了。那问题又来了初始化,spring的核心逻辑不执行了吗,比如在ac.refresh()方法里的 解析注解,转化bd,bean初始化等功能,显然不是。
当代码执行到32行的时候回执行DispatcherServlet的init ()方法。
图18
我们发现在dispacherServlet的父类中有init()方法。
图19
图20
图21
图22
图21
图22
代码走到这里我们终于明白了,其实spring Mvc内部也调用了refresh()方法。因为底层都是调用spring的refresh()方法。那么问题又来了,最早不是执行了两遍的refresh() 方法吗,没有问题吗,没有问题啊,因为getBean的流程了,因为spring 在new bean的时候要先get一遍的。所以不会示例化两遍。