Spring框架(Spring Boot)的技巧

总的来说

这是关于Spring框架(Spring Boot)的技巧。我们将随时进行更新。
一些较长的内容将作为单独的文章发布,并在此处提供链接。
另外,请注意,最初发布的技巧是在2015年使用的旧版本,可能不再适用,敬请留意。

建议

将属性文件的值设置为字段的默认值。

    環境: Spring Boot 1.3.0.M5, Java 1.8.0_60

价值注解

您可以使用Value注解将属性文件的值作为字段的默认值。

    Annotation Type Value
myapp:
  string:
    property: Some text
    propertyDefault:
#    propertyUnresolvable:
  int:
    property: 123
  boolean:
    property: true
@Value("${myapp.string.property}")
private String stringProperty;
@Value("${myapp.int.property}")
private int intProperty;
@Value("${myapp.boolean.property}")
private boolean boolProperty;
logger.info("String:{} int:{} bool:{}", stringProperty, intProperty, boolProperty);
// ⇒ String:Some text int:123 bool:true

未被定义的属性

当引用未定义的属性时会引发异常。

@Value("${myapp.string.propertyUnresolvable}")
private String stringPropertyUnresolvable;
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'myapp.string.propertyUnresolvable' in string value "${myapp.string.propertyUnresolvable}"

为了解决这个问题,我们将自定义PropertySourcesPlaceholderConfigurer。我们将使用setIgnoreUnresolvablePlaceholders方法来忽略无法解决的属性。

@Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
  PropertySourcesPlaceholderConfigurer config = new PropertySourcesPlaceholderConfigurer();
  config.setIgnoreUnresolvablePlaceholders(true);
  return config;
}

接下来,我们将设置默认值。通过使用冒号进行分隔来指定默认值。

@Value("${myapp.string.propertyUnresolvable:defaultValue}")
private String stringPropertyUnresolvable;

结果如下所示。

logger.info("String Unresolvable:{}", stringPropertyUnresolvable);
// ⇒ String Unresolvable:defaultValue

默认值

如果属性的值未被定义,那么将不会反映Value注解指定的默认值。

myapp:
  string:
    propertyDefault:
@Value("${myapp.string.propertyDefault:defaultValue}")
private String stringPropertyDefault;

在字段中,未反映默认值的Value注释将输出为空字符串。

logger.info("String Default:{}", stringPropertyDefault);
// ⇒ String Default:

为了解决这个问题,我们可以定制PropertySourcesPlaceholderConfigurer。我们可以使用setNullValue方法将空字符串转换为null。

@Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
  PropertySourcesPlaceholderConfigurer config = new PropertySourcesPlaceholderConfigurer();
  config.setIgnoreUnresolvablePlaceholders(true);
  config.setNullValue("");
  return config;
}

然而,默认值未被设置,将变为null。

logger.info("String Default:{}", stringPropertyDefault);
// ⇒ String Default:null

因此,在此处用Value注释不指定默认值,而是使用Optional。

@Value("${myapp.string.propertyDefault}")
private Optional<String> stringPropertyDefault;

结果如下所示。

logger.info("String Default:{}", stringPropertyDefault.orElse("defaultValue"));
// ⇒ String Default:defaultValue

收集

    Class DefaultFormattingConversionService

用yaml定义数组的方式如下,但是spring似乎不支持这种形式的数组定义。(这个问题已经被列为@Value注解应该能够从YAML属性中注入List的问题。)

myapp:
  intList:
    property:
     - 1
     - 2
     - 3
     - 4
     - 5

目前,我们使用逗号来分隔并列举数值,以表示数组。

myapp:
  intList:
    property: 1,2,3,4,5
  stringArray:
    property: a,b,c,d,e
@Value("${myapp.intList.property}")
private List<Integer> intList;
@Value("${myapp.stringArray.property}")
private List<String> stringArray;

然而,继续这样下去将会导致异常产生。

Caused by: java.lang.NumberFormatException: For input string: "1,2,3,4,5"

为了解决这个问题,使用ConversionService将DefaultFormattingConversionService进行转换。

@Bean
public static ConversionService conversionService() {
  ConversionService conversionService = new DefaultFormattingConversionService();
  return conversionService;
}

结果如下。

logger.info("intList:{}", intList.toString());
// ⇒ intList:[1, 2, 3, 4, 5]
logger.info("stringArray:{}", stringArray.toString());
// ⇒ stringArray:[a, b, c, d, e]

本地日期

要将日期时间设为字段的默认值,除了使用Value注释外,还需使用DateTimeFormat注释。

myapp:
  localDate:
    property: 2015-09-28
  localDateTime:
    property: 2015-09-28 10:05:00

使用DateTimeFormat注解声明模式。

@Value("${myapp.localDate.property}")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate localDate;
@Value("${myapp.localDateTime.property}")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime localDateTime;

结果如下所示。

logger.info("localDate:{}", localDate.toString());
// ⇒ localDate:2015-09-28
logger.info("localDateTime:{}", localDateTime.toString());
// ⇒ localDateTime:2015-09-28T10:05

thymeleaf 是一个用于在 Java 网页应用中创建动态模板的开源模板引擎。

您可以从Thymeleaf模板中引用属性文件的值。

myapp:
  version: 1.0.1
<p th:text="${@environment.getProperty('myapp.version')}"></p>

结果如下。

<p>1.0.1</p>

对ConversionService进行自定义操作

    環境: Spring Boot 1.3.0.M5, Java 1.8.0_60

定制化型轉換服務,將日期字串轉換為Date或LocalDate類別。

    • Interface ConversionService

 

    Class FormattingConversionServiceFactoryBean
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.support.FormattingConversionServiceFactoryBean;

@Configuration
public class ConversionConfig {

  @Bean
  public static ConversionService conversionService() {

    FormattingConversionServiceFactoryBean factoryBean = new FormattingConversionServiceFactoryBean();
    factoryBean.setConverters(getConverters());
    factoryBean.afterPropertiesSet();
    ConversionService conversionService = factoryBean.getObject();

    return conversionService;
  }

  private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
  private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

  private static Set<Converter<String, ?>> getConverters() {
    Set<Converter<String, ?>> converters = new HashSet<Converter<String, ?>>();

    converters.add(
      new Converter<String, Date>() {
        @Override
        public Date convert(String str) {
          SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
          try {
            return sdf.parse(str);
          } catch (ParseException e) {
            return null;
          }
        }
    });

    converters.add(
      new Converter<String, LocalDate>() {
        @Override
        public LocalDate convert(String str) {
          return LocalDate.parse(str, DATE_FORMATTER);
        }
    });

    converters.add(
      new Converter<String, LocalDateTime>() {
        @Override
        public LocalDateTime convert(String str) {
          return LocalDateTime.parse(str, DATETIME_FORMATTER);
        }
    });

    return converters;
  }
}

确认动作代码

@Autowired
ConversionService conversionService;

public void confirm() {

  Integer i = conversionService.convert("2015", Integer.class);
  Long l = conversionService.convert("2015", Long.class);
  BigDecimal b = conversionService.convert("124.45", BigDecimal.class);

  logger.info("Integer:{} Long:{} BigDecimal:{}", i, l, b);
  // ⇒ Integer:2015 Long:2015 BigDecimal:124.45

  Date d = conversionService.convert("2015-10-03", Date.class);
  logger.info("Date:{}", d.toString());
  // ⇒ Date:Sat Oct 03 00:00:00 JST 2015

  LocalDate ld = conversionService.convert("2015-10-03", LocalDate.class);
  logger.info("localDate:{}", ld.toString());
  // ⇒ localDate:2015-10-03

}

3. 使用EventListener注解来实现应用程序事件。

    環境: Spring Boot 1.3.0.M5, Java 1.8.0_60

从Spring 4.2开始,您可以使用EventListener注解来实现应用程序事件的实现。

    • Annotation Type EventListener

 

    • Class ApplicationEvent

 

    • Interface ApplicationListener

 

    Interface ApplicationEventPublisher

不使用注释的实现方法

import java.io.Serializable;
import java.util.Date;

public class Blog implements Serializable {

  private static final long serialVersionUID = -5011335950988850583L;

  private Long id;
  private String title;
  private Date published;

  public Blog(Long id, String title) {
    this.id = id;
    this.title = title;
    this.published = null;
  }

  ... getter/setter省略 ...

  @Override
  public String toString() {
    return "Blog [id=" + id + ", title=" + title + ", published=" + published
        + "]";
  }

}

事件类

import org.springframework.context.ApplicationEvent;

public class BlogEvent extends ApplicationEvent {

  private static final long serialVersionUID = 6029200135885343599L;

  private Blog blog;

  public BlogEvent(Object source, Blog blog) {
    super(source);
    this.blog = blog;
  }

  public Blog getBlog() {
    return blog;
  }

  public void setBlog(Blog blog) {
    this.blog = blog;
  }

}

听众类

import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class BlogListener implements ApplicationListener<BlogEvent> {
  final static Logger logger = LoggerFactory.getLogger(BlogListener.class);

  @Override
  public void onApplicationEvent(BlogEvent event) {
    logger.info("Blog Event is occured!!");
    logger.info(event.getBlog().toString());
    event.getBlog().setPublished(new Date());
  }

}

实现ApplicationEventPublisherAware

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;

@Service
public class BlogService implements ApplicationEventPublisherAware {
  final static Logger logger = LoggerFactory.getLogger(BlogService.class);

  private ApplicationEventPublisher publisher;

  @Override
  public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
    this.publisher = publisher;
  }

  public void createBlog(Blog blog) {
    logger.info("Publishing Blog Create Event");
    BlogEvent event = new BlogEvent(this, blog);
    publisher.publishEvent(event);
  }

}

动作确认代码


@Autowired
BlogService blogService;

public void confirm() {

  Blog blog = new Blog(1L, "浜省だらけの野球対決を観賞する");
  logger.info(blog.toString());
  // ⇒ Blog [id=1, title=浜省だらけの野球対決を観賞する, published=null]

  blogService.createBlog(blog);
  logger.info(blog.toString());
  // ⇒ Blog [id=1, title=浜省だらけの野球対決を観賞する, published=Sun Oct 04 20:25:33 JST 2015]

}

使用EventListener注解的实现方法

import java.io.Serializable;
import java.util.Date;

public class Product implements Serializable {

  private static final long serialVersionUID = 886343041195980987L;

  private Long id;
  private String productName;

  private Date createdAt;
  private Date completedAt;

  public Product(Long id, String productName) {
    this.id = id;
    this.productName = productName;
    this.createdAt = null;
    this.completedAt = null;
  }

  ... getter/setter省略 ...

  @Override
  public String toString() {
    return "Product [id=" + id + ", productName=" + productName
        + ", createdAt=" + createdAt + ", completedAt=" + completedAt + "]";
  }

}

事件类

不需要继承ApplicationEvent。

public class ProductEvent {

  private Product product;
  private String status;

  public ProductEvent(Product product) {
    this.product = product;
    this.status = "CREATE";
  }

  public Product getProduct() {
    return product;
  }
  public void setProduct(Product product) {
    this.product = product;
  }
  public String getStatus() {
    return status;
  }
  public void setStatus(String status) {
    this.status = status;
  }

}

听众类

不需要实现ApplicationListener。 相反,可以在方法上添加EventListener注解来注册事件侦听器。

import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class ProductListener {
  final static Logger logger = LoggerFactory.getLogger(ProductListener.class);

  @EventListener(condition = "#event.status == 'CREATE'")
  public void createEvent(ProductEvent event) {
    logger.info("Product Create Event is occured!!");

    try {
      TimeUnit.SECONDS.sleep(3L);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    event.getProduct().setCreatedAt(new Date());
   }

  @EventListener(condition = "#event.status == 'COMPLETE'")
  public void closeEvent(ProductEvent event) {
    logger.info("Product Complete Event is occured!!");

    try {
      TimeUnit.SECONDS.sleep(3L);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    event.getProduct().setCompletedAt(new Date());
  }

}

发布事件的类

不需要实现ApplicationEventPublisherAware。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

@Service
public class ProductService {
  final static Logger logger = LoggerFactory.getLogger(ProductService.class);

  @Autowired
  ApplicationEventPublisher publisher;

  public void createProduct(Product product) {
    logger.info("Publishing Product Create Event");
    ProductEvent event = new ProductEvent(product);
    publisher.publishEvent(event);
  }

  public void completeProduct(Product product) {
    logger.info("Publishing Product Complete Event");
    ProductEvent event = new ProductEvent(product);
    event.setStatus("COMPLETE");
    publisher.publishEvent(event);
  }

}

运动确认代码

@Autowired
ProductService productService;

public void confirm() {

  Product product = new Product(1L, "Mazda RX-7");
  logger.info(product.toString());
  // ⇒ Product [id=1, productName=Mazda RX-7, createdAt=null, completedAt=null]

  productService.createProduct(product);
  logger.info(product.toString());
  // ⇒ Product [id=1, productName=Mazda RX-7, createdAt=Sun Oct 04 20:25:36 JST 2015, completedAt=null]

  try {
    TimeUnit.SECONDS.sleep(3L);
  } catch (InterruptedException e) {
    e.printStackTrace();
  }

  productService.completeProduct(product);
  logger.info(product.toString());
  // ⇒ Product [id=1, productName=Mazda RX-7, createdAt=Sun Oct 04 20:25:36 JST 2015, completedAt=Sun Oct 04 20:25:42
}

4. 使用ResourceLoader从资源文件中获取资源

    環境: Spring Boot 1.3.0.M5, Java 1.8.0_60

如果无法使用ApplicationContext的实例来获取资源文件,可以使用ResourceLoader代替来获取资源文件。

    Interface ResourceLoader

资源文件

在/src/main/resources中创建用于动作确认的资源文件。

/src/main/resources/resource1.txt 的以下内容,用汉语进行本地化的释义。

123
456
789

/src/main/resources/resource2.txt 的中文本地化版本

ABC
DEF
GHI

/主/资源3.txt

あいうえお
かきくけこ
さしすせそ

动作确认代码

    1. 使用ApplicationContext的方式

 

    1. 使用ResourceLoader的方式

 

    使用DefaultResourceLoader的方式
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;

@Service
public class ResourceLoaderService {
  final static Logger logger = LoggerFactory.getLogger(ResourceLoaderService.class);

  @Autowired
  ResourceLoader resourceLoader;

  @Autowired
  ApplicationContext context;

  // (1)
  public void execute1() {
    try {
      Resource resource = context.getResource("classpath:resource1.txt");
      try (BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream()))) {
        reader.lines().forEach(line -> {
          logger.info(line);
          // ⇒ 123
          // ⇒ 456
          // ⇒ 789
        });
      }
    } catch (IOException e) {
      logger.error(e.getMessage());
    }
  }

  // (2)
  public void execute2() {
    try {
      Resource resource = resourceLoader.getResource("classpath:resource2.txt");
      try (BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream()))) {
        reader.lines().forEach(line -> {
          logger.info(line);
          // ⇒ ABC
          // ⇒ DEF
          // ⇒ GHI
        });
      }
    } catch (IOException e) {
      logger.error(e.getMessage());
    }
  }

  // (3)
  public void execute3() {
    try {
      Resource resource = new DefaultResourceLoader().getResource("classpath:resource3.txt");
      try (BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream()))) {
        reader.lines().forEach(line -> {
          logger.info(line);
          // ⇒ あいうえお
          // ⇒ かきくけこ
          // ⇒ さしすせそ
        });
      }
    } catch (IOException e) {
      logger.error(e.getMessage());
    }
  }

}

当尝试通过Resource.getFile来读取资源文件时,如果将应用程序打包成jar文件并执行,则会出现FileNotFoundException。

try (BufferedReader reader = new BufferedReader(new FileReader(resource.getFile()))) {
    reader.lines().forEach(line -> {
        //...処理
    });
}
java.io.FileNotFoundException: class path resource [resource3.txt] cannot be resolved to absolute file path because it does not reside in the file system: jar:file: 省略...

5. 通过MessageSource获取消息源。

    • 投稿: 2015/10/08

 

    環境: Spring Boot 1.3.0.M5, Java 1.8.0_60

在中国的母语中,以下是一个选项的释义:配置消息资源是通过spring.messages.*属性进行的。

# INTERNATIONALIZATION (MessageSourceAutoConfiguration)
spring:
  messages:
    basename: messages,views
    cache-seconds: -1
    encoding: utf-8
    • basename

メッセージソースファイルのベース名を指定します。複数指定する場合はカンマで区切ります。デフォルトはmessagesです。

cache-seconds

メッセージソースをキャッシュする秒数を指定します。デフォルトは-1でキャッシュの期限は無期限です。 0を指定するとキャッシュされません。

encoding

メッセージソースのエンコードを指定します。デフォルトはutf-8です。

可以通过向消息源文件的基本名称中添加像_ja或_en这样的区域设置来实现国际化。如果只需要支持一种语言,则不需要区域设置。

message.text1 = abc
message.text2 = 123
message.text3 = hello {0}.
layout.header = \u30D8\u30C3\u30C0\u30FC
layout.footer = Copyright {0} - {1}.

控制器

如果想在Controller类内引用消息源,可以使用MessageSource的实例。

@Autowired
MessageSource messageSource;
@RequestMapping(value = "/", method = RequestMethod.GET)
public String index(Model model, Locale locale) {

  String text1 = messageSource.getMessage("message.text1", null, locale);
  String text2 = messageSource.getMessage("message.text2", null, locale);
  String text3 = messageSource.getMessage("message.text3", new Object[]{"world!"}, locale);
  logger.info("message: text1:{} text2:{} text3:{}", text1, text2, text3);
  // ⇒ message: text1:abc text2:123 text3:hello world!.

  return "Index/index";
}

thymeleaf (百叶窗)

在thymeleaf模板中引用消息资源,可以使用#{资源键}的语法。

<h1 th:text="#{layout.header}">header</h1>
<h3 th:text="#{layout.footer('2014','2015')}">footer</h3>

以下是结果。

<h1>ヘッダー</h1>
<h3>Copyright 2014 - 2015.</h3>

使用SPRING INITIALIZR生成应用程序的模板。

    投稿: 2015/10/08

您可以使用SPRING INITIALIZR生成pom.xml和build.gradle的模板。

请指定项目类型(Maven或Gradle)和Spring-Boot的版本,并输入项目元数据。
然后点击”切换到完整版本”的链接文本。

i01.png

勾选必要的组件,最后点击“生成项目”按钮即可开始下载项目模板的zip文件。

下载:提示示例.zip

i02.png

以下是解压缩zip文件后的目录结构。

tips-example
  ├─.mvn
  │  └─wrapper
  └─src
      ├─main
      │  ├─java
      │  │  └─com
      │  │      └─example
      │  │          └─sbtip
      │  └─resources
      │      ├─static
      │      └─templates
      └─test
          └─java
              └─com
                  └─example
                      └─sbtip

要将此应用程序导入到eclipse中,请进入解压后的目录并执行以下命令。

> mvn eclipse:eclipse

7. 关机挂钩

    • 投稿: 2015/10/20

 

    環境: Spring Boot 1.3.0.M5, Java 1.8.0_60

在应用程序结束时执行任意方法,可以使用PreDestro注解。

@PreDestroy
public static void exit() {
  System.out.println("appliation exit");
}

8. 将属性文件的值设定为其他键的值。

    • 投稿: 2016/02/07

 

    環境: Spring Boot 1.3.0, Java 1.8.0_65

属性文件的值可以使用SpEL表达式进行定义。

myapp:
  configA:
    var1: ABC
    var2: DEF
  configB:
    var1: 123
    var2: ${myapp.configA.var1}

使用Thymeleaf直接输出并确认。

<div class="row">
  <p th:text="${@environment.getProperty('myapp.configA.var1')}"></p>
  <p th:text="${@environment.getProperty('myapp.configA.var2')}"></p>
  <p th:text="${@environment.getProperty('myapp.configB.var1')}"></p>
  <p th:text="${@environment.getProperty('myapp.configB.var2')}"></p>
</div>
<div class="row">
  <p>ABC</p>
  <p>DEF</p>
  <p>123</p>
  <p>ABC</p>
</div>

9. 在 Spring Boot 中,编写 Handler 方法的参数来接收任意类的实例作为参数。

    • 投稿: 2016/10/09

 

    環境: Spring Boot 1.3.0, Java 1.8.0_65

10. 使用Spring Boot来构建重定向目标URL

    • 投稿: 2016/10/09

 

    環境: Spring Boot 1.4.1, Java 1.8.0_101

11. 关于Spring Boot devtools的自动重启功能

    • 投稿: 2016/10/13

 

    環境: Spring Boot 1.4.1, Java 1.8.0_101

12. Spring Framework中方便的类,位于“org.springframework.web.util”包。

    • 投稿: 2016/10/27

 

    環境: Spring Boot 1.4.1, Java 1.8.0_101

13. 使用拦截器来设置响应头信息

    • 投稿: 2016/11/09

 

    環境: Spring Boot 1.4.1, Java 1.8.0_101

14. 关于Spring AOP的切点选择器写法

    • 投稿: 2017/05/02

 

    環境: Spring Boot 1.5.3, Java 1.8.0_131

15. 展示如何在Spring Boot 1.5版本中处理多个数据库的网络应用程序示例。

    • 投稿: 2017/05/04

 

    環境: Spring Boot 1.5.3, Java 1.8.0_131

16. 示例代码将属性文件的值分配给期望的字段类型

    • 投稿: 2017/08/20

 

    環境: Spring Boot 1.5.4, Java 1.8.0_144

17. 用maven的多模块构建Spring Boot应用程序

    • 投稿: 2017/09/25

 

    環境: Spring Boot 1.5.6, Java 1.8.0_144

在编辑应用程序的自定义属性时,使代码补全功能可用。

    • 投稿: 2018/02/25

 

    環境: Spring Boot 2.0.0.RC2, Java 1.8.0_162

19. 用Spring Boot开发的应用程序的GitHub存储库

    投稿: 2018/03/15

这是一个列出了迄今为止为了学习和验证而开发的Spring Boot 应用程序的GitHub仓库列表。

20. 学习掌握Spring Boot

    投稿: add 2018/04/03

以下是有效的信息来源的总结,适用于追赶时机。

Spring Data Jpa的技巧

在JPQL中查询具有JSON类型列的表

    • 投稿: 2017/09/15

 

    環境: Spring Boot 1.5.6, Java 1.8.0_144

2. 使用Spring Data JPA的QBE(Query by Example)进行搜索的示例代码。

    • 投稿: 2017/09/13

 

    環境: Spring Boot 1.5.6, Java 1.8.0_144

3. 使用JPQL调用数据库的用户自定义函数

    • 投稿: 2017/09/12

 

    環境: Spring Boot 1.5.6, Java 1.8.0_144

使用仓库进行搜索,并将特定列映射到POJO中。

    • 投稿: 2017/09/12

 

    環境: Spring Boot 1.5.6, Java 1.8.0_144

5. 我调查了在Spring Boot 1.5.9中进行异步查询的方式。

    • 投稿: 2017/12/23

 

    環境: Spring Boot 1.5.9, Java 1.8.0_144

6. Java持久化API 2.1的复习笔记

    • 投稿: 2018/03/11

 

    環境: Spring Boot 2.0.0, Spring Data JPA 2.0.5, Java 9.0.4

7. 如何在JPA中执行存储过程

    • 投稿: 2018/03/25

 

    環境: Spring Boot 2.0.0, Spring Data JPA 2.0.5, Java 1.8.0_162

测试技巧

1. 关于Spring Boot和单元测试环境的设计

    • 投稿: 2018/3/20

 

    環境: Spring Boot 2.0.0, Java 1.8.0_162

2. 接受Pageable作为参数的控制器处理方法的单元测试失败。

    • 投稿: 2018/06/30

 

    環境: Spring Boot 2.0.3, Java 10.0.1

当运行一个接受类似Pageable参数的处理程序方法的单元测试时,将输出以下错误消息并失败。

No primary or default constructor found for interface org.springframework.data.domain.Pageable

控制器的处理方法 de

@GetMapping(path = "list", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<List<Memo>> list(Pageable page) {

  //...省略

}

测试代码

@RunWith(SpringRunner.class)
@WebMvcTest(MemoController.class)
public class MemoControllerTests {

  @Autowired
  private MockMvc mvc;

  @Test
  public void getList() throws Exception {

    RequestBuilder builder = MockMvcRequestBuilders.get("/memo/list")
                               .param("page", "0")
                               .param("size", "5")
                               .accept(MediaType.APPLICATION_JSON_UTF8);

    MvcResult result = mvc.perform(builder)
                            .andExpect(status().isOk())
                            .andExpect(content().contentType(contentType))
                            .andDo(print())
                            .andReturn();

  }

}

失败的原因是,在使用WebMvcTest注解进行测试时,PageableHandlerMethodArgumentResolver无法有效地设置Pageable实例,因此为解决此问题而创建了一个问题。

@WebMvcTest 还应该自动配置Spring Data的Web集成功能

您可以通过在测试类中添加以下代码行来暂时解决此问题。

@Import(SpringDataWebAutoConfiguration.class)

開發環境的小貼士

1. 使用Spring Boot 2.0.0的谷歌云平台

    • 投稿: 2018/03/29

 

    環境: Spring Boot 2.0.0, Java 1.8.0_162

这是一个关于如何在Google Cloud Platform上使用Spring Boot运行web应用程序的总结文章。

在使用Java 10开发Spring Boot 2.0应用程序时需要注意的基本事项

    • 投稿: 2018/06/04

 

    環境: Spring Boot 2.0.2, Java 10.0.1

这是关于在Java 10上开发和运行环境的注意事项备忘。

使用Eclipse Photon (4.8)和Java 10来搭建Spring Boot应用程序的开发环境。

    • 投稿: 2018/6/30

 

    環境: Spring Boot 2.0.3, OpenJDK 10.0.1
广告
将在 10 秒后关闭
bannerAds