springboot(2)

个人积累,请勿私自转载,转载前请联系
代码及文章资源https://github.com/jedyang/DayDayUp/tree/master/java/springboot
基于SpringBootCookBook的读书笔记,重在个人理解和实践,而非翻译。

WEB工程

创建一个基本的RESTful应用

目前的软件一般都是围绕web服务,或者数据服务等来构建的,下面我们继续前面的代码,构建一个基本的Restful应用。

  1. 首先增加web依赖。

     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
     </dependency>
    
  2. 创建一个spring controller 负责处理http request。先创建一个controllers包以存放代码。

  3. 增加一个controller:BookController。

     @RestController
     @RequestMapping("/books")
     public class BookController {
         @Autowired
         private BookRepository bookRepository;
     
         @RequestMapping(value = "", method = RequestMethod.GET)
         public Iterable<Book> getAllBooks(){
             return bookRepository.findAll();
         }
     
         @RequestMapping(value = "/{isbn}", method = RequestMethod.GET)
         public Book getBook(@PathVariable String isbn){
             return bookRepository.findByIsbn(isbn);
         }
     }
    
  4. mvn启动工程.mvn spring-boot:run

  5. 浏览器http://localhost:8080/books。可以看到结果[]

原理

关注@RestController注解。
这个注解组合了@Controller@ResponseBody。所以用这两个注解替换页完全没有问题,只是更方便了。
剩下的就是spring mvc的东西了。

创建一个spring data rest应用

在上面这个应用中,我们提供一个restful服务,用来获取数据。这种服务非常简单,应用也非常广泛。但是如果要提供很多数据服务接口,也是一项繁重无聊的工作。为了减少模版式工作,spring提供了一个spring-boot-starter-data-rest,使我们可以简单的通过在repository接口上添加注解达到暴露数据服务的目的。

  1. 添加依赖

     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-data-rest</artifactId>
     </dependency>
    
  2. 新建一个repository接口
    新建一个接口AuthorRepository,并使用@RepositoryRestResource注解

     /**
      * 使用注解直接暴露rest服务
      * @author yunsheng
      */
     @RepositoryRestResource
     public interface AuthorRepository extends PagingAndSortingRepository<Author, Long> {
     }
    

同样的方式,也给publisher和reviewer提供服务

    @RepositoryRestResource
    public interface PublisherRepository extends
            PagingAndSortingRepository<Publisher, Long> {
    }


    @RepositoryRestResource
    public interface ReviewerRepository extends
            PagingAndSortingRepository<Publisher.Reviewer, Long> {
    }
  1. Run
    访问http://localhost:8080/authors会看到响应

     {
       "_embedded" : {
         "authors" : [ ]
       },
       "_links" : {
         "self" : {
           "href" : "http://localhost:8080/authors{?page,size,sort}",
           "templated" : true
         },
         "profile" : {
           "href" : "http://localhost:8080/profile/authors"
         }
       },
       "page" : {
         "size" : 20,
         "totalElements" : 0,
         "totalPages" : 0,
         "number" : 0
       }
     }
    

原理

  1. 我们看到响应比之前的BookRepository多了很多额外信息,因为我们的接口是继承自PagingAndSortingRepositoryPagingAndSortingRepository又是继承自CrudRepository,多了分页和排序功能。可以改成CrudRepository看一下效果。

  2. RepositoryRestResource也提供了一些参数。比如可以修改访问的url。

     @RepositoryRestResource(collectionResourceRel = "writers", path = "writers")
    
  3. 我们依赖了spring-boot-starter-data-rest。这同时会引入spring-hateoas库,这会给我们提供ALPS支持。(没有太深入研究,是一种数据格式,用来描述应用级别的api接口)
    比如可以这样使用:

     $ curl -l localhost:8080
       % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                      Dload  Upload   Total   Spent    Left  Speed
     100   537    0   537    0     0   1721      0 --:--:-- --:--:-- --:--:--  1721{
       "_links" : {
         "writers" : {
           "href" : "http://localhost:8080/writers{?page,size,sort}",
           "templated" : true
         },
         "books" : {
           "href" : "http://localhost:8080/books"
         },
         "publishers" : {
           "href" : "http://localhost:8080/publishers{?page,size,sort}",
           "templated" : true
         },
         "reviewers" : {
           "href" : "http://localhost:8080/reviewers{?page,size,sort}",
           "templated" : true
         },
         "profile" : {
           "href" : "http://localhost:8080/profile"
         }
       }
     }
    

    查看所有服务。

     $ curl -l localhost:8080/writers
       % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                      Dload  Upload   Total   Spent    Left  Speed
     100   374    0   374    0     0   3431      0 --:--:-- --:--:-- --:--:--  3978{
       "_embedded" : {
         "writers" : [ ]
       },
       "_links" : {
         "self" : {
           "href" : "http://localhost:8080/writers{?page,size,sort}",
           "templated" : true
         },
         "profile" : {
           "href" : "http://localhost:8080/profile/writers"
         }
       },
       "page" : {
         "size" : 20,
         "totalElements" : 0,
         "totalPages" : 0,
         "number" : 0
       }
     }
    

    查看writers服务。

定制servlet filter

仔细看springboot的启动日志

2017-09-05 13:16:56.528  INFO 14936 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2017-09-05 13:16:56.529  INFO 14936 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.15
2017-09-05 13:16:56.951  INFO 14936 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2017-09-05 13:16:56.951  INFO 14936 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 2713 ms
2017-09-05 13:16:57.273  INFO 14936 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Mapping servlet: 'dispatcherServlet' to [/]
2017-09-05 13:16:57.276  INFO 14936 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]
2017-09-05 13:16:57.277  INFO 14936 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2017-09-05 13:16:57.277  INFO 14936 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2017-09-05 13:16:57.277  INFO 14936 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]

可以看出,springboot内置使用tomcat做web容器,内置了servlet。
默认也加载了几个filter。
同时,我们也可以自定义自己的filter。
目前,spring已经内置了大量的filter,首先我们看一下如何使用他们。比如我们希望得到的请求的用户真是url,而不是代理的url,可以使用这个filter。RemoteIpFilter

  1. 建一个配置类
    在跟路径下新建一个配置类。

     @Configuration
     public class WebConfiguration {
         @Bean
         public RemoteIpFilter remoteIpFilter() {
             return new RemoteIpFilter();
         }
     } 
    
  2. run
    可以看到RemoteIpFilter已经加载进来。

     2017-09-05 13:41:56.814  INFO 13792 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'remoteIpFilter' to: [/*]
    

原理

在启动类BookPubApplication@SpringBootApplication注解中,有包扫描注解。
所以,在启动时,spring扫描到@Configuration注解的类,将其中的bean注入到上下文中。
然后对其中实现了Filter接口的类,加载到filter chain中。

所以,我们可以自己实现Filter,然后按照上面的方式,加载到springboot的启动过程中。

配置拦截器

filter过滤器是servlet中的概念,其实和spring没什么关系(除了加到filter chain中)。spring mvc提供了另一种方式处理请求HandlerInterceptorHandlerInterceptor允许我们在不同节点处理request,如请求被处理前,请求被处理后,view被渲染前,请求完全处理结束等。拦截器不会改变request本身,是根据逻辑中止请求的执行,可以抛出异常或返回false。

spring mvc已经预置打很多拦截器,常用的有LocaleChangeInterceptor 和
ThemeChangeInterceptor。已LocaleChangeInterceptor(用于修改国际化参数)为例演示。

添加拦截器并不像前面添加过滤器一样,通过bean注解就可以了。稍微有点复杂。

  1. 继承WebMvcConfigurerAdapter
    在前面WebConfiguration基础上修改。增加继承WebMvcConfigurerAdapter。

  2. 通过添加@bean声明一个拦截器。

  3. 只是声明还不行,需要覆写手动添加一个拦截器。
    代码最终如下:

     @Configuration
     public class WebConfiguration extends WebMvcConfigurerAdapter {
         @Bean
         public RemoteIpFilter remoteIpFilter() {
             return new RemoteIpFilter();
         }
     
         @Bean
         public LocaleChangeInterceptor localeChangeInterceptor() {
             return new LocaleChangeInterceptor();
         }
     
         @Override
         public void addInterceptors(InterceptorRegistry registry) {
             registry.addInterceptor(localeChangeInterceptor());
         }
     }
    
  4. run
    浏览器访问http://localhost:8080/books?locale=foo
    (为什么用参数locale?因为默认的修改本地化的参数名就是这个,可以看源码)
    会看到报错:

     java.lang.UnsupportedOperationException: Cannot change HTTP accept header - use a different locale resolution strategy
     at org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver.setLocale(AcceptHeaderLocaleResolver.java:127) ~[spring-webmvc-4.3.9.RELEASE.jar:4.3.9.RELEASE]
     at org.springframework.web.servlet.i18n.LocaleChangeInterceptor.preHandle(LocaleChangeInterceptor.java:148) ~[spring-webmvc-4.3.9.RELEASE.jar:4.3.9.RELEASE]
     at org.springframework.web.servlet.HandlerExecutionChain.applyPreHandle(HandlerExecutionChain.java:133) ~[spring-webmvc-4.3.9.RELEASE.jar:4.3.9.RELEASE]
    

可以看到异常时来自LocaleChangeInterceptor,证明我们的拦截器已经工作。这个报错的原因是头信息不允许来自浏览器的修改。

原理

你也应该猜到了,很简单。springboot对实现了WebMvcConfigurer的类(WebMvcConfigurerAdapter继承自WebMvcConfigurer),回调其addInterceptors方法。达到添加拦截器的目的。

还没结束

通常其他的文章,到这里已经结束了,但是本篇不是。
你有没有发现,访问http://localhost:8080/books?locale=foo会报错,但是访问http://localhost:8080/writer?locale=foo是正常的呢?

为什么呢?
原来,/books是我们在controller层通过@RestController,@RequestMapping("/books")暴露的。
/writers这些事我们在数据层通过@RepositoryRestResource直接暴露的。
看来两种方式还是有区别的。@RepositoryRestResource直接暴露的根本不走拦截器。

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,733评论 6 342
  • 1. 开发第一个SpringBoot应用 环境要求:Java 1.8 + mvn3.3.9 创建Maven pom...
    仅此而已004阅读 1,628评论 0 0
  • 2017年8月21日 我原本只想简单记录一下springboot中应用Jpa的简单操作。不想由于hibernate...
    行者N阅读 6,476评论 0 23
  • 上周,微信朋友圈一度被一条“温暖”的信息刷屏。往常看到爱心接力,内心都充满了“人间自有真情在”的感动,可这次的这份...
    晚安晴天阅读 228评论 0 0