Spring boot为Spring mvc 提供的自动配置适用于大多数应用,并引入如下的配置信息:
- 引入 ContentNegotiatingViewResolver 和 BeanNameViewResolver beans。
- 对静态资源的支持,包括对WebJars的支持。
- 自动注册 Converter , GenericConverter , Formatter beans。
- 对 HttpMessageConverters 的支持。
- 自动注册 MessageCodeResolver 。
- 对静态 index.html 的支持。
- 对自定义 Favicon 的支持。
- 自动使用 ConfigurableWebBindingInitializer bean。
spring boot为FreeMarker,Groovy,tymeleaf,velocity,mustache提供自动配置,不建议使用jSP,不管使用那种模板引擎都会从/src/main/resources/templates下自动加载模板。
CORS支持
跨域资源请求,Spring mvc对cors提供开箱即用的支持,不用添加任何特殊配置,只需要在spring boot应用的controller类和方法上注解@CrossOrigin,并添加CORS配置。例如,允许controller中定义的所有方法实现跨域请求,如下:
@CrossOrigin(origins = "http://domain.example.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
@RequestMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}
也可以将@@CrossOrigin注解在controller中的方法上,Spring会结合类上的CrossOrigin和方法上的CrossOrigin配置形成完整的CrossOrigin配置,例如:
@CrossOrigin(maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin("http://domain1.example.com")
@RequestMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}
CorsConfiguration可以通过以下三种方式提供自定义设置,
1,AbstractHandlerMapping,通过setCorsConfigurations(Map<String,CorsConfiguration> corsConfigurations)方法,传入一个CorsConfiguration的Map,其中corsConfiguration基于URL pattern.
2,继承AbstractHandlerMapping,重写getCorsConfiguration方法,提供自定义的CorsConfiguration。
3,实现CorsConfigurationSource接口,实现getCorsConfiguration(HttpServletRequest request)方法,为每个请求创建CorsConfiguration实例。
cors 全局配置
使用Javaconfig配置:
@SuppressWarnings("deprecation")
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter{
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://domain2.example.com","http://domain3.example.com")//添加允许的域
.allowedMethods("PUT", "DELETE","POST","GET")//添加允许支持的http 方法
.allowedHeaders("header1", "header2", "header3")//添加允许的header
.exposedHeaders("header1", "header2") //添加暴漏的本地header
.allowCredentials(false) //配置是否允许使用证书
.maxAge(3600);
}
}
使用XML配置:
<mvc:cors>
<mvc:mapping path="/api/**"
allowed-origins="http://domain1.example.com, http://domain2.example.com"
allowed-methods="GET, PUT,POST,DELETE"
allowed-headers="header1, header2, header3"
exposed-headers="header1, header2" allow-credentials="false"
max-age="123" />
<!--可配置到多个域 -->
<mvc:mapping path="/resources/**"
allowed-origins="http://domain3.example.com" />
</mvc:cors>
使用Filter:
Spring 提供CorsFilter代替@CorsOrigin注解和WebMvcConfigurer#addCorsMappings(CorsRegistry)。例如:
public class MyCorsFilter extends CorsFilter{
public MyCorsFilter() {
super(getMyCorsConfiguration());
}
private static CorsConfigurationSource getMyCorsConfiguration(){
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("http://domain.example.com");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", config);
return source;
}
}
需要保证CorsFilter注册在所有filter之前。在spring boot中可以按照如下方法实现filter的顺序:
@Bean
public FilterRegistrationBean corsFilter() {
FilterRegistrationBean bean = new FilterRegistrationBean(new MyCorsFilter());
bean.setOrder(0);
return bean;
}
提供REST Api接口
利用@RestController可以创建基于rest api的函数接口,除此之外Spring提供Jersey 2.x的原生支持,并支持起步配置。添加jersey依赖,
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
继承ResouceConfig注册service端点,例如:
@Component
public class JerseyConfig extends ResourceConfig{
public JerseyConfig(){
register(MyEndPoint.class);
}
}
创建服务端点:
@Component
@Path("/user")
public class MyEndPoint {
@Path("/info")
@GET
public String getUserInfo(@QueryParam("uid")String uid){
return "success;user uid is "+uid;
}
@Path("/pwd")
@GET
public String getUserPwd(@QueryParam("uid")String uid){
return "success :user pwd is test";
}
}
运行spring boot应用访问:http://localhost:8080/user/info?uid=zhongzhong,查看返回信息:
在默认情况下jersey的servlet会映射到“/*”,使用@ApplicaitonPath可以更改映射,例如在JerseyConfig上添加如下注解:
@ApplicationPath("/jersey")
再次运行程序:
通过实现org.springframework.boot.autoconfigure.jersey.ResourceConfigCustomizer接口,自定义ResourceConfig。
@Component
public class JerseyCustom implements ResourceConfigCustomizer{
@Override
public void customize(ResourceConfig config) {
// TODO Auto-generated method stub
config.packages("com.zhongzhong.demo.jersey");
//config.register(MyEndPoint.class);
}
}
服务端点不变,在Spring boot启动类中添加ResourceConfig配置
@Bean
public ResourceConfig getJerseyCustom(){
return new ResourceConfig();
}
启动程序运行结果相同。在自定义resouceConfig中可以通过ResourceConfig.packages(...)和ResouceConfig.register(...)将服务端点所在包和服务端点注册到ResouceConfig中。但是jersey对扫描可执行archives的支持相当有限。例如, 在运行可执行 war 文件时, 它无法扫描在 WEB INF/classes中找到的包中的服务端点。为避免此限制, 不应使用package方法, 并且应使用register方法单独注册端点。
内嵌servlet支持
Spring boot 内嵌Tomcat,jetty和undertow服务器。在centos上使用内嵌tomcat时会用于程序在运行过程中会删除tmpwatch目录而倒是程序发生错误,所以要重新配置tmpwatch目录来避免。
在使用内嵌servlet时,可以通过使用Spring beans或则Servlet组件的方式注册serlvet,Filters及特定Servlet相关的所有listeners。
如果上下文中只包含一个Servlet,他将被映射到“/”;如果存在多个Servlet,每个Servlet的名称将为被用作路径的前缀,过滤映射到“/*”。还可以使用ServletRegistrationBean、FilterRegistrationBean、ServeltListenerRegistrationBean实现完全的控制。
注册servlet
创建测试Servlet实现javax.servlet.Servlet接口,如下:
public class MyServletTest implements Servlet{
.....//其他方法省略
@Override
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException {
// TODO Auto-generated method stub
HttpServletResponse response = (HttpServletResponse) arg1;
response.getWriter().write("this is MyServlet test");
response.getWriter().flush();
}
}
public class MyServlet implements Servlet{
// 省略其他方法....
@Override
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException {
// TODO Auto-generated method stub
HttpServletResponse response = (HttpServletResponse) arg1;
response.getWriter().write("this is MyServlet");
response.getWriter().flush();
}
}
在启动类中注入两个org.springframework.boot.web.servlet.ServletRegistrationBean实例,如下:
@Bean
public ServletRegistrationBean<MyServlet> getServlet(){
ServletRegistrationBean bean = new ServletRegistrationBean();
bean.addUrlMappings("/myServlet");
bean.setServlet(new MyServlet());
bean.setOrder(4);
return bean;
}
@Bean
public ServletRegistrationBean<MyServletTest> getServletRegistrationBean(){
ServletRegistrationBean bean = new ServletRegistrationBean();
bean.addUrlMappings("/myServletTest");
bean.setServlet(new MyServletTest());
bean.setOrder(2);
return bean;
}
启动应用程序,分别访问:http://localhost:8080/myServlet,http://localhost:8080/myServletTest。
Filter的定义和使用与Servlet相似。定义两个Filter均继承javax.servlet.Filter,例如:
public class MyFilterTest implements Filter{
//另外两个方法省略
@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1,
FilterChain arg2) throws IOException, ServletException {
// TODO Auto-generated method stub
HttpServletRequest request = (HttpServletRequest)arg0;
if(request.getRequestURI().endsWith("Test")){
System.out.println("in "+this.getClass().getName());
arg2.doFilter(arg0, arg1);
}else{
System.out.println("request not in my filter");
}
}
}
public class MyFilter implements Filter{
//另外两个方法省略
@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1,
FilterChain arg2) throws IOException, ServletException {
// TODO Auto-generated method stub
HttpServletRequest request = (HttpServletRequest)arg0;
if(request.getRequestURI().contains("myServlet")){
System.out.println("in "+this.getClass().getName());
arg2.doFilter(arg0, arg1);
}else{
System.out.println("filter not configured in main");
}
}
}
在启动类中添加两个org.springframework.boot.web.servlet.FilterRegistrationBean实例,如下:
@Bean
public FilterRegistrationBean<MyFilter> getMyFilter(){
FilterRegistrationBean<MyFilter> myFilter = new FilterRegistrationBean<MyFilter>();
Collection<String> url = new LinkedList<String>();
url.add("/myServlet");
myFilter.setFilter(new MyFilter());
myFilter.addServletNames("myServlet");
return myFilter;
}
@Bean
public FilterRegistrationBean<MyFilterTest> getMyFilterTest(){
FilterRegistrationBean<MyFilterTest> myFilter = new FilterRegistrationBean<MyFilterTest>();
Collection<String> url = new LinkedList<String>();
url.add("/myServletTest");
myFilter.setFilter(new MyFilterTest());
myFilter.addServletNames("myServletTest");
return myFilter;
}
继续访问http://localhost:8080/myServletTest,控制台输出:
访问http://localhost:8080/myServlet
自定义内嵌Servlet
在application.properties中设置关于内嵌servlet的配置:
监听地址设置:server.address=8090;
session是否持久化设置:server.session.persistence=true/false;
session超时时间:server.session.timeout=3600;
session数据存放位置:server.session.store-dir=/usr/db/sessiondb
session-cookie设置等(server.session.cookie.*)
编程实现--实现WebServerFactoryCustomizer接口
@Component
public class MyServletCustom implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>{
@Override
public void customize(ConfigurableServletWebServerFactory factory) {
// TODO Auto-generated method stub
factory.setPort(8090);
}
}
启动应用程序,端口已更改为8090:
直接初始化ConfigurableServletWebServerFactory,在启动类中添加如下内容:
@Bean
public ConfigurableServletWebServerFactory webServerFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.setPort(9000);
factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/notfound.html"));
return factory;
}
启动应用程序,端口被更改为9000:
在使用内嵌servlet时,容器对jsp的支持会有一些限制:
1,tomcat只支持war的打包方式,不支持jar;
2,jetty只支持war的打包方式;
3,undertow不支持JSP;
4,创建自定义的error.jsp页面不会覆盖error handling视图。