从Servlet到Spring MVC再到Spring Boot的进化历程

从Servlet到Spring MVC再到Spring Boot的进化历程

文章目录

  • 从Servlet到Spring MVC再到Spring Boot的进化历程
    • J2EE、Java EE和Servlet
      • 从Servlet开始说起
      • 一个简单的Servlet示例
        • Servlet文件
        • web.xml文件
    • 从Servlet进化到Spring MVC
      • 先谈MVC
      • Spring MVC项目示例
        • ***web*.xml配置**
        • ***dispatcher-servlet.xml配置***
    • 从Spring MVC到Spring Boot
      • Spring Boot项目示例
        • pom文件
        • application.properites文件
        • Controller的文件
      • Spring Boot是如何消去web.xml的
      • 没有dispacher-servlet.xml文件Spring Boot是如何将DataSource等Bean注入到Spring容器中的

J2EE、Java EE和Servlet

从Servlet开始说起

为了满足开发企业级应用程序的需求,Sun公司在早期J2SE的基础上,针对企业级应用的各种需求,提出了J2EE规范。自2005年J2EE 5.0版本推出后,Sun公司正式将J2EE的官方名称改为“Java EE(Java Enterprise edition)”。企业级应用具有大规模、多层、可伸缩、可靠以及网络安全等特点。目前Java EE规范中应用比较广泛的是其中Java Web相关的规范。

Servlet是Java EE规范中的定义的一个概念,它是处理用户请求的核心。

Servlet是一种独立于操作系统平台和网络传输协议的服务端的Java应用程序,他用来扩展服务器的功能,可以生成动态的Web页面。Servlet没有main()方法,它只能运行在Web服务器的Web容器中。Web容器负责管理Servlet,它装入并初始化Servlet,管理Servlet的多个实例;同时充当请求调度器,将客户端的请求传递到Servlet,并将Servlet的响应返回给客户端。

一个简单的Servlet示例

Servlet文件

public class MyFirstServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    public static final String HTML_START="";
    public static final String HTML_END="";

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException {
        PrintWriter out = response.getWriter();
        Date date = new Date();
        out.println(HTML_START + "

Hi There!


Date="+date +"

"+HTML_END); } }

Servlet要对外提供服务,就需要将它配置在web.xml上,web.xml也是Java EE规范的一部分,示例配置如下。

web.xml文件




    
        /my-first-servlet
    

    
        MyFirstServlet
        com.ncepu.yun.MyFirstServlet
    
    
        MyFirstServlet
        /my-first-servlet
    


web.xml中的“”和“”配置块指定了Servlet类及其响应的路径,配置完后。用户在浏览器中访问/my-first-servlet时,用户请求就会被转发到MyFirstServlet中进行处理。

要想运行上面的代码,我们需要按照一定的目录结构放置我们的文件,将文件编译之后打成war包,再把war包放入到Servlet容器如tomcat中。

maven推荐的项目目录结构

目录 说明
src/main/resources 资源文件目录。例如application.xml、struts.xml
src/main/java Java源代码目录
src/test/java 测试用例代码目录
src/test/resources 测试用例相关资源目录
src/main/webapp Web项目根目录
target 编译构建输出目录

前述的web.xml文件放在webapp/WEB-INF目录下,java文件放入src/main/java目录下。

从Servlet进化到Spring MVC

上文提到了一个简单的web.xml配置加上一个Sevlet实现就能够对外提供服务了,但是这种做法有一个问题。随着时间的推移,当你需要处理的业务变得越来越复杂,当你展现给用户的界面变得越来越复杂,你的Sevlet中的代码实现就会变得越来越难以维护。如果我们能够让整个系统的有一个非常清晰的层次结构,而不是所有代码都在Servlet中,代码的可读性、可维护性就能够有显著提高,使用Spring MVC就能够达到这个目标。

先谈MVC

MVC(Model-View—Controller)是一种典型的软件设计模式,它将软件的结构分为三层,从而使得设计大型应用程序变得容易。
使用MVC有如下优点:

  1. 软件更加容易维护。系统拆分之后,各个部分实现的功能范围都很清晰,需要改什么功能直接到相应的部分修改即可。
  2. 代码复用率提高。系统拆分之后,功能实现会更加内聚,一个类的一个方法只负责某个固定的功能,所有用到这个功能的位置直接调用这个方法就行。
  3. 协同开发更加容易。系统拆分之后,视图的开发可以和模型、控制器的开发同时进行,提升软件交付速度。

Spring MVC项目示例

Spring MVC是利用Spring容器的和Servlet实现的一个MVC框架。下面会用一个简单的Spring MVC的例子来讲解Spring MVC是如何利用Spring容器和Servlet的来实现MVC框架的。

下面只截取了项中目的web.xml、dispatcher-servlet.xml两个配置文件来进行说明,完整的项目可见 https://github.com/muou0213/cloudyispringmvc

web.xml配置



  Archetype Created Web Application

  
    
      org.springframework.web.context.ContextLoaderListener
    
  

  
    DispatcherServlet
    
      org.springframework.web.servlet.DispatcherServlet
    
    
      contextConfigLocation
      /WEB-INF/dispatcher-servlet.xml
    
    1
  

  
    DispatcherServlet
    /
  

  
  
    contextConfigLocation
    /WEB-INF/dispatcher-servlet.xml
  


简要的说明下这个配置文件中各个元素的作用。


    
      org.springframework.web.context.ContextLoaderListener
    
  

这个linstener配置启动了Spring的容器,只有容器启动了Spring才能管理各个Bean。

 
      org.springframework.web.servlet.DispatcherServlet
    

DispatcherServlet是Spring MVC实现MVC功能的关键Servlet


      contextConfigLocation
      /WEB-INF/dispatcher-servlet.xml

这个配置指向了一个xml配置文件,这个配置文件配置了实现MVC功能所需的一些示例。后面对dispatcher-servlet.xml文件说明时会详细说到这一块。

 
    contextConfigLocation
    /WEB-INF/dispatcher-servlet.xml
  

这个配置是告诉Spring要将哪些示例放入到容器中。这个配置跟上面的配置的功能是相似的,大多数应用场景下,这两个配置填写同一个xml文件就可以了。

1

根据Servlet的生命周期,Servlet的实例化是需要等到第一次有用户请求这个Servlet的时候才会进行。加了这个配置,DispatcherServlet就会在Servlet容器启动后就实例化,不需要用户去请求它。

dispatcher-servlet.xml配置





    
    

    

    

    
        
        
    

    
        
        
        
        
    


    
        
        
    

dispatcher-servlet.xml各个配置元素的说明。


这个配置告诉Spring容器应该去扫描哪个包来进行Bean管理,你的Controller、Service和Dao都放在这个包中。


这个配置告诉MyBatis放SQL语句的接口所在的包的包名。


这个配置向Spring容器中注册了RequestMappingHandlerMapping, RequestMappingHandlerAdapter, ExceptionHandlerExceptionResolver等类的实例来处理Controller中的注解,例如@RequestMapping , @ExceptionHandler等注解,这些示例是完成MVC功能的重要依仗。

实际上,这个配置也可以省略掉,Dispatcher使用了一些默认的实现类来处理这些注解,这些默认实现配置在Dispatcher.properties文件中,而使用可以修改这些默认实现为其他自定义的实现。想要进一步了解
可以参考 https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-servlet-config


这个配置项给出了xml可以读取配置的配置文件的位置。通过这个配置项目可以将一些属性值与xml文件分开存放。


        
        
    

这个bena实际上是一个jsp的视图解析器,也就是解析MVC中V的相关部分。除了InternalResourceViewResolver外,Spring MVC还提供了多种视图解析器的实现。

总结一下,Spring MVC的就是在Servlet的基础上,通过各种注解、注解的解析类以及支持jsp视图等各种类型视图的类来完成MVC功能的一个MVC框架。

完整的示例项目可以在https://github.com/muou0213/cloudyispringmvc下载

从Spring MVC到Spring Boot

通过上一节对Spring MVC的配置文件的分析,我们能够发现使用Spring MVC进行业务开发的准备工作基本上就是写配置文件。而这些配置文件中的很大一部分都是固定不变的,理论上讲我们可以让框架去代替我们完成这些不变的部分,我们只需要给出变化部分的值就可以了。

使用Spring Boot就能够达到这个效果。

使用Spring Boot之后,我们就可以省去web.xml和dispacher-servlet.xml的配置,仅仅给出像数据库url这种每个项目都不一样的参数的值就可以运行整个项目了。下面是一个简单的在Spring Boot基础上使用Spring MVC的一个示例

Spring Boot项目示例

pom文件




    4.0.0

    com.ncepu.yun
    cloudyispringboot
    1.0-SNAPSHOT
    war

    cloudyispringboot Maven Webapp


    
        UTF-8
        1.8
        1.8
    


    
        org.springframework.boot
        spring-boot-starter-parent
        2.2.2.RELEASE
         
    

    

        
        
            org.mybatis.spring.boot
            mybatis-spring-boot-starter
            2.1.2
        

        
        
            org.mybatis
            mybatis-spring
            2.0.4
        

        
        
            org.apache.commons
            commons-dbcp2
            2.7.0
        

        
        
            mysql
            mysql-connector-java
            5.1.49
        

        
        
            org.apache.tomcat.embed
            tomcat-embed-jasper
            9.0.35
        


        
            org.springframework.boot
            spring-boot-starter-web
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
            
                
                    org.junit.vintage
                    junit-vintage-engine
                
            
        

        
            junit
            junit
            4.11
            test
        
        
            org.projectlombok
            lombok
            1.18.6
        
    

可以看到pom文件中添加了spring-boot-starter-parent,mybatis-spring-boot-starter,spring-boot-starter-web这三个spring boot相关的依赖,使用Spring Boot开发web项目这三个依赖是必须添加的。

特别需要说明的一点是,如果你需要在Spring Boot内嵌的tomcat中使用JSP,你就需要在pom中引入tomcat-embed-jasper依赖,这个依赖用于编译JSP文件。

application.properites文件

###mybatis###
mybatis.mapper-locations=mappers/*.xml

###datasource###
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/spring_demo?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
spring.datasource.password=passwd
spring.datasource.username=root

###mvc###
spring.mvc.view.prefix=WEB-INF/jsp/
spring.mvc.view.suffix=.jsp

前面单独使用Spring MVC时候web.xml、dispatcher-servlet.xml两个文件中的繁杂的配置都被上面这个简单的properties文件替代了。

Controller的文件

@Controller
public class HelloController {
    @Autowired
    private UserMapper userMapper;

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public ModelAndView sayHello() {
        ModelAndView modelAndView = new ModelAndView("hello");
        String username = userMapper.selectAllUser().get(0).getUsername();
        modelAndView.addObject("name", username);
        return modelAndView;
    }
}

Controller层、Service层、Dao层代码跟之前是一样的没有变化。就是经过这么简单的配置,整个项目就可以运行了。

完整的示例项目可以在https://github.com/muou0213/cloudyispringboot下载。

看到这里我们可能会有两个疑问:

  1. Spring Boot是如何消去web.xml的
  2. 没有dispacher-servlet.xml文件Spring Boot是如何把使用Mybati所必须的DataSource等Bean注入到Spring容器中的

下面我们就来解答这两个疑问。

Spring Boot是如何消去web.xml的

Spring Boot是通过使用Servlet3.0引入的新接口ServletContainerInitializer来消去web.xml配置文件的。实现了ServletContainerInitializer接口的类中的onStartup()方法会在Servlet容器启动的时候被调用。

在使用Spring Boot内嵌的tomcat时,Spring Boot中的类TomcatStarter的onStartup()方法会调用DispatcherServletRegistrationBean类的onStartup()方法。从类名我们就可以看出,DispatcherServletRegistrationBean这个类会把DispatcherServlet添加到Servlet容器中。

@Override
	protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
		String name = getServletName();
		return servletContext.addServlet(name, this.servlet);
	}

把DispatcherServlet添加到Servlet容器中的关键代码是servletContext.addServlet(name, this.servlet),这个addServlet方法也是Servlet3.0引入的,专门用来把servlet添加到Servlet容器中的。

通过使用Servlet3.0引入的ServletContainerInitializer接口和addServlet()方法,Spring Boot就完成了之前通过web.xml完成的DispatcherServlet配置过程。

没有dispacher-servlet.xml文件Spring Boot是如何将DataSource等Bean注入到Spring容器中的

Spring Boot是通过配合使用@EnableAutoConfiguration注解和SpringFactoriesLoader Bean注入机制来完成DataSource等Bean的注入的。

我们通常会在SpringBoot的启动类上使用注解@SpringBootApplication,如下面代码

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

而使用@SpringBootApplication注解等同于使用了@SpringBootConfiguration、@EnableAutoConfiguration等注解,这一点打开@SpringBootApplication注解的源码就能看到。
@EnableAutoConfiguration注解就是Spring Boot能够把DataSource等相关的Bean注入到Spring容器中的关键。

我们打开spring-boot-autoconfigure的源码,能找到一个spring.factories的properties文件,这个文件中有如下内容:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\

以EnableAutoConfiguration为key,以逗号分隔的Configuration类为value的一个映射关系。

在启动类上配置了@EnableAutoConfiguration后,Spring Boot的代码会利用SpringFactoriesLoader加载配置在spring.factories文件中EnableAutoConfiguration key后面所有的Configuration类,其中有个Configuration类会将DataSource注入到Spring容器中。

下面截取了DataSourceConfiguration的部分代码,从这部分代码就可以看出,这个Configuration提供了将tomcat提供的DataSource实现注入到Spring容器中的功能。

abstract class DataSourceConfiguration {

	@SuppressWarnings("unchecked")
	protected static  T createDataSource(DataSourceProperties properties, Class type) {
		return (T) properties.initializeDataSourceBuilder().type(type).build();
	}

	/**
	 * Tomcat Pool DataSource configuration.
	 */
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)
	@ConditionalOnMissingBean(DataSource.class)
	@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "org.apache.tomcat.jdbc.pool.DataSource",
			matchIfMissing = true)
	static class Tomcat {

		@Bean
		@ConfigurationProperties(prefix = "spring.datasource.tomcat")
		org.apache.tomcat.jdbc.pool.DataSource dataSource(DataSourceProperties properties) {
			org.apache.tomcat.jdbc.pool.DataSource dataSource = createDataSource(properties,
					org.apache.tomcat.jdbc.pool.DataSource.class);
			DatabaseDriver databaseDriver = DatabaseDriver.fromJdbcUrl(properties.determineUrl());
			String validationQuery = databaseDriver.getValidationQuery();
			if (validationQuery != null) {
				dataSource.setTestOnBorrow(true);
				dataSource.setValidationQuery(validationQuery);
			}
			return dataSource;
		}

	}

你可能感兴趣的:(源码讲解,框架梳理)