JavaWeb(4):Spring简介
参考书籍:《Pro Java for Web Apps》
Maven
依赖
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.4.RELEASE</version>
<scope>compile</scope>
</dependency>
Spring Framework
是一个Java
应用程序容器,它提供了许多有用的特性,如反转控制(IoC
)、依赖注入(DI
)、抽象数据访问、事务管理等。
IoC
是一个软件设计模式,即组装器(Spring Framework
)在运行时才会绑定对象,也就是运行时才会实例化依赖的组件。这使得开发者可以针对接口进行编程,无需重新编译代码。DI
是IoC
的一种实现方式,通过DI
,一个类可以通过声明其依赖的接口类型,然后利用组装器在运行时注入其依赖的实例。
Spring Framework
提供了一个MVC
(模型-视图-控制)模式框架,利用它可以简化创建Web
应用程序的过程,不用手动处理复杂的Servlet
、HttpServletRequest
等。控制器类的每个方法都被映射到了一个不同的请求URL
、方法或请求的其他属性上,模型可以通过 $Map$<$String, Object$>的形式从控制器传递到视图,控制器返回的视图或视图名称将把模型转发到合适的JSP
视图。通过这些特性,Spring Framework
极大地简化了Servlet
容器的工作内容。
使用MVC
框架时,控制器类中的每个方法都可以拥有一个指向特定URL
、请求方法、参数存在性、头的值、内容类型或期望响应类型的唯一映射。
Spring Framework
容器以一个或多个应用上下文的形式存在,由 $org.springframework.context.ApplicationContext$ 接口表示。一个应用上下文管理着一组bean
、执行业务逻辑的Java
对象、执行任务等。由Spring
管理的bean
可以自动进行DI
、消息通知、bean
验证等服务。一个Spring
程序至少需要一个应用上下文,也可以使用多个应用上下文组成层次结构。在层次结构中应用上下文间有着类似于树的父子兄弟关系,一个子应用上下文的bean
可以访问父应用上下文的bean
,但无法访问兄弟应用上下文的bean
。许多类都继承了 $ApplicationContext$ ,也有许多实现了它:
类 | 描述 |
---|---|
$ConfigurableApplication$ | 可配置的应用上下文 |
$WebApplicationContext$ | 用于Java EE Web 应用程序,提供了对 $ServletContext$ 和 $ServletConfig$ 的访问 |
$ConfigurableWebApplicationContext$ | 可配置的 $WebApplicationContext$ |
$XmlApplicationContext$ | 用于在Java EE Web 应用程序中从XML 文件加载Spring 配置 |
$AnnotationConfigWebApplicationContext$ | 用于在Java EE Web 应用程序中以编程方式配置Spring |
Spring
使用 $DispatcherServlet$ 处理Web
请求,该Servlet
将请求委托给合适的控制器,并按需求对请求和响应实体进行转换。在Web
应用程序中,我们可以使用任意数量的 $DispatcherServlet$ 。每个 $DispatcherServlet$ 类都有自己的应用上下文,包含了对Web
应用程序的 $ServletContext$ 和自己的 $ServletConfig$ 的引用。
启动Spring Framework
配置和启动Spring framework
是不同的,且相互独立,都可以用多种方式实现。在Java SE
中只能通过在应用程序的 $main$ 方法中以编程方式启动。而在Java EE
中,可以使用XML
创建部署描述符( $web.xml$ )或者在 $javax.servlet.ServletContainerInitializer$ 中通过编程方式启动。
部署描述符方式
传统的Spring Framework
应用程序总是通过部署描述符启动。以部署描述符方式启动要求在配置文件中创建 $DispatcherServlet$ 实例,然后以 $contextConfigLocation$ 启动参数的形式为它提供配置文件,并指示Spring
在启动时加载。
<context_param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/rootContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>springDispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/servletContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springDispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
$ContextLoaderListener$ 将在Web
应用程序启动时初始化,然后从 $contextConfigLocation$ 中指定的 $rootContext.xml$ 中加载根应用上下文并启动。同样的,$springDispatcher$ 也从指定的 $servletContext.xml$ 中加载应用上下文。
编程方式
Java EE 6
提供了一个新接口 $ServletContainerInitializer$ ,实现其的类将在应用程序开始时启动,并在所有监听器启动之前调用它们的 $onStartup$ 方法。要使用该接口,需要在 $/META-INF/services/javax.servlet.ServletContainerInitializer$ 文件中列出实现它的类。Spring Framework
提供了一个桥接口,简化了这一步:$org.springframework.web.SpringServletContainerInitializer$ 类实现了 $ServletContainerInitializer$ 接口,然后该类会扫描程序并寻找 $org.framework.web.WebApplicationInitializer$ 接口的实现,调用其 $onStartup$ 方法。因此我们可以在 $WebApplicationInitializer$ 实现类中以编程方式配置监听器、Servlet
和过滤器。
public class Bootstrap implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext container) {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(RootContextConfiguration.class);
container.addListener(new ContextLoaderListener(rootContext));
AnnotationConfigWebApplicationContext servletContext = new AnnotationConfigWebApplicationContext();
servletContext.register(ServletContextConfiguration.class);
ServletRegistration.Dynamic dispatcher = container.addServlet("springDispatcher", new DispatcherServlet(servletContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
上面的代码实现了与之前的XML
文件一样的配置,其中 $RootContextConfiguration$ 和 $ServletContextConfiguration$ 是自定义的配置应用上下文的类。在此要注意的是,不要把 $DispatcherServlet$ 映射到 $/\star$ ,因为映射到 $/\star$ 也会把内部JSP
请求发送到该Servlet
。
我们也不必只使用一种方式配置,可以混合使用,如下所示:
public class Bootstrap implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext container) {
XmlWebApplicationContext rootContext = new XmlWebApplicationContext();
rootContext.setConfigLocation("/WEB-INF/rootContext.xml");
container.addListener(new ContextLoaderListener(rootContext));
XmlWebApplicationContext servletContext = new XmlWebApplicationContext();
servletContext.setConfigLocation("/WEB-INF/servletContext.xml");
ServletRegistration.Dynamic dispatcher = container.addServlet("springDispatcher", new DispatcherServlet(servletContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
配置Spring Framework
与启动相同,配置Spring Framework
同样可以通过XML
和编程方式。配置Spring Framework
的主要步骤就是配置bean
。Spring Framework
本身提供了默认bean
,在此基础上,我们需要自己再编写一些bean
。
XML
配置方式
通过在上面的引用的 $servletContext.xml$ 文件中添加如下内容进行配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<mvc:annotation-driven />
<bean name="fooImpl" class="FooImpl"/>
<bean name="barController" class="BarController">
<property name="foo" ref="fooImpl"/>
</bean>
</beans>
我们使用 <$beans$> 命名空间配置bean
。上述XML
文件将实例化 $FooImpl$ 和 $BarController$ ,并将 $FooImpl$ 注入 $BarController$ 的 $foo$ 属性中。<$mvc:annotation-driven$> 指示Spring
使用 $@RequestsMapping$, $@RequestBody$, $@RequestParam$, $@PathParam$, $@ResponseBody$ 这样的注解将请求映射到控制器方法上。
混合配置方式
在XML
的基础上,我们可以通过使用组件扫描和注解进行配置。组件扫描将扫描所有标注了 $org.springframework.stereotype.Component$ 的类,并将他们变成bean
交由Spring
进行管理。任何标注了 $@Component$ 的注解都将变成组件注解,因此 $@Controller$, $@Repository$, $@Service$ 也都是组件注解。当然,也可以创建自己的组件注解。$@org.springframework.beans.factory.annotation.Autowired$ 注解,以及它的同义词 $@javax.inject.Inject$ 和 $@javax.annotation.Resource$ 用于声明Spring
应该注入的依赖,可以用于标注字段、方法、方法参数或者构造器。通常bean
应该具有无参构造器,但对于一个标注了 $@Autowired$ 的构造器的类,Spring
将注入所有构造器参数并使用该构造器构造实例。当存在多个匹配的bean
时,可以通过使用 $@org.springframework.beans.factory.annotation.Qualifier$ 或者 $@javax.inject.Named$ 指定使用的bean
的名字,或者使用 $@org.springframework.context.annotation.Primary$ 标记组件指定优先使用的bean
的名字。除了上述注解之外,还有两个特殊的用于bean
的注解,$@javax.annotation.PostConstruct$ 注解和 $@javax.annotation.PreDestroy$ 注解,用于标注一个 $public\ \ void$ 的无参方法,前者会在bean
构造完成后调用,后者会在bean
关闭前调用。
通过使用注解,我们可以简化我们的Spring
配置。在 $rootContext.xml$ 中可以添加如下配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<context:annotation-config/>
<context:component-scan base-package="org.example">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
<$context:annotation-config$> 启用注解配置,<$context:component-scan$> 启用组件扫描,$base-package$ 特性指定要扫描的包,在其命名空间内,我们使用了 <$context:exclude-filter$> 标签排除了对 $@Controller$ 组件的扫描。因为在之前的 $servletContext.xml$ 中我们已经配置了 $barController$ ,如果不排除的话,它将会被实例化两次。或者,我们可以修改 $servletContext.xml$ 配置为如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<mvc:annotation-driven/>
<context:annotation-config/>
<context:component-scan base-package="org.example" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
使用 <$context:include-filter$> 设置应该扫描的包,注意这里使用了 $use-default-filters$ 该属性表示是否使用标准扫描模式,默认为 $true$ 。
编程配置方式
使用编程方式配置时,我们要创建自定义的配置类,然后为这些类标上 $@org.springframework.context.annotation.Configuration$ 注解。在自定义的配置类内,我们还可以通过使用 $@org.springframework.context.annotation.Bean$ 注解标注无参方法注册bean
。不仅如此,我们在混合配置中所提到的所有注解都能在配置类内使用,而且由于 $@Configuration$ 是标注了 $@Component$ 的注解,因此也能被组件扫描所扫描到,但这可能产生意料之外的行为,如bean
被实例化两次。使用配置类时,可以通过结合使用 $@org.springframework.context.annotation.Import$ 注解导入其他配置类中的配置,或者使用 $@org.springframework.context.annotation.ImportResource$ 注解导入XML
配置来实现将配置分布在多个地方。
还记得我们在混合配置中保留的命名空间吗?现在我们可以使用注解来代替它们了。
注解 | 命名空间 | 作用 |
---|---|---|
$@ComponentScan$ | <$context:component-scan$> | 指定启用组件扫描的包 |
$@EnableAsync$ | <$task:*$> | 启用Spring 的 $@Aysnc$ 异步方法执行 |
$@EnableScheduling$ | <$task:*$> | 启用Spring 的 $@Scheduled$ 计划方法的执行 |
$@EnableCaching$ | <$cache:*$> | 启用Spring 的注解驱动缓存管理特性 |
$@EnableTransactionManagement$ | <$tx:annotation-driven$> | 为 $@Transactional$ 方法启用事务管理 |
$@EnableWebMvc$ | <$mvc:annotation-driven$> | 通常需要对该注解进行自定义配置,通过继承 $WebMvcConfigurerAdapter$ 来自定义需要的部分 |
@Configuration
@EnableWebMvc
@ComponentScan(
basePackage = "org.example",
useDefaultFilters = false,
includeFilters = @ComponentScan.Filter(Controller.class)
)
public class ServletContextConfiguration {}
上例是一个简单的配置类,它没有包含任何bean
,一切都通过组件扫描和 $@EnableWebMvc$ 进行配置。