7-web应用

Spring boot为Spring mvc 提供的自动配置适用于大多数应用,并引入如下的配置信息:

  1. 引入 ContentNegotiatingViewResolver 和 BeanNameViewResolver beans。
  2. 对静态资源的支持,包括对WebJars的支持。
  3. 自动注册 Converter , GenericConverter , Formatter beans。
  4. 对 HttpMessageConverters 的支持。
  5. 自动注册 MessageCodeResolver 。
  6. 对静态 index.html 的支持。
  7. 对自定义 Favicon 的支持。
  8. 自动使用 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/myServlethttp://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视图。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容

  • 我曾因为 你就是那把 能够再次打开我上锁的心的钥匙 而欢喜雀跃 然而 我却意识到 我并不是你的专属钥匙 无法走进你...
    空酌阅读 170评论 0 1
  • Is already autumn now. Darkred fallen leaves covered ever...
    llclare阅读 246评论 0 0
  • Drupal page.tpl.php文件中的<?php print $messages; ?>的作用:动态地输出...
    米酒真香阅读 546评论 0 50
  • 第十章:创建计算字段 拼接字段拼接函数 Concat()SELECT Concat(name, '(', depa...
    JairusTse阅读 291评论 0 0
  • 你改变了,你便改变了整个游戏的规则,你是游戏的角色,你不精彩,游戏又如何? 让更高纬度的你来指引你,做好你本应是的...
    演金阅读 326评论 0 0