1.rest風格增刪改查及druid數據源的配置及mybatis的配置css
@AllArgsConstructor @NoArgsConstructor @Data @Accessors(chain = true)//lombok public class Book implements Serializable { private Integer id; private String name; private Integer price; }
public interface BookMapper {
@Select("select isbn id,book_name name,price from books where isbn=#{id}")
public Book getBookById(Integer id);
@Update("<script>" +
"update books " +
"<set>" +
"<if test= \"name!=null\">" +
"book_name=trim(#{name})," +
"</if>" +
"<if test= \"price!=null\">" +
"price=trim(#{price})," +
"</if>" +
"</set>"+
"where isbn=#{id} </script>")
public void updateBook(Book book);
@Delete("delete from books where isbn=#{id}")
public void deleteBookById(Integer id);
@Insert("insert into books(isbn,book_name,price) values(trim(#{id}),trim(#{name}),trim(#{price}))")
@Options(useGeneratedKeys = true)
public void insertBook(Book book);
}
@org.springframework.web.bind.annotation.RestController public class RestController { @Autowired private BookMapper bookMapper; @GetMapping("/book/{id:\\d+}") public Book get(@PathVariable Integer id){ return bookMapper.getBookById(id); } @PutMapping("/book") public Book put(Book book){ bookMapper.updateBook(book); Book bookNew = bookMapper.getBookById(book.getId()); return bookNew; } @DeleteMapping("/book/{id:\\d+}") public String post(@PathVariable Integer id){ bookMapper.deleteBookById(id); return id+"刪除成功"; } @PostMapping("/book") public Book post(Book book){ bookMapper.insertBook(book); Book bookNew = bookMapper.getBookById(book.getId()); return bookNew; } }
@Configuration
@ComponentScan(value = "springmvc.demo")
@MapperScan("springmvc.demo.dao")
public class MVCConfig {
}
@Configuration public class MVCConfigBean { @ConfigurationProperties(prefix ="spring.datasource") @Bean public DataSource getDataSource(){ return new DruidDataSource(); } //配置druid監控頁面 @Bean //添加servlet public ServletRegistrationBean statViewServlet(){ ServletRegistrationBean<StatViewServlet> servlet = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*"); HashMap<String, String> map = new HashMap<>(); map.put("loginUsername", "admin");//帳戶 map.put("loginPassword", "123456");//密碼 map.put("allow", "");//容許因此用戶登陸,默認容許全部用戶登陸 map.put("deny", "");//拒絕用戶登陸,能夠是ip地址等 servlet.setInitParameters(map); return servlet; } @Bean //添加過濾器 public FilterRegistrationBean webStatFilter(){//假如攔截全部那麼與標註@component註解做用同樣,其餘的也是 FilterRegistrationBean<Filter> filter = new FilterRegistrationBean<>(); filter.setFilter(new WebStatFilter()); HashMap<String, String> map = new HashMap<>(); map.put("exclusions", "*.js,*.css,*/druid/*,*.gif,*.jpg,*.png,*.ico"); filter.setInitParameters(map); filter.setUrlPatterns(Arrays.asList("/*")); return filter; } //添加配置mybatis的配置 @Bean public ConfigurationCustomizer configurationCustomizer(){ ConfigurationCustomizer configurationCustomizer = x->{ //設置駝峯命名法 x.setMapUnderscoreToCamelCase(true); }; return configurationCustomizer; } }
#編碼處理
spring.http.encoding.charset=UTF-8
spring.http.encoding.force=true
spring.http.encoding.enabled=true
server.tomcat.uri-encoding=UTF-8
#日誌
logging.level.springmvc.demo.mvcdemo=debug
#與mysql數據庫鏈接
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/book?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&useSSL=false&verifyServerCertificate=false
spring.datasource.username=root
spring.datasource.password=cgz12345678
#使用druid鏈接池
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#druid鏈接池鏈接池參數配置
#初始化時創建物理鏈接的個數。初始化發生在顯示調用init方法,或者第一次getConnection時
spring.datasource.initialSize=5
#最大鏈接池數量
spring.datasource.maxActive=10
#獲取鏈接時最大等待時間
spring.datasource.maxWait=3000
#最小鏈接池數量
spring.datasource.minIdle=3
# 配置間隔多久才進行一次檢測,檢測須要關閉的空閒鏈接,單位是毫秒
spring.datasource.timeBetweenEvictionRunsMillis=60000
# 配置一個鏈接在池中最小生存的時間,單位是毫秒
spring.datasource.minEvictableIdleTimeMillis=300000
#驗證數據庫鏈接的有效性
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
spring.datasource.poolPreparedStatements=true
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
#stat功能(監控)、wall功能(sql防火牆)、logback功能(監控日誌輸出),須要配置添加相應的配置文件不然會報錯
spring.datasource.filters=stat,wall,logback
# 經過connectProperties屬性來打開mergeSql功能;慢SQL記錄
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
@RunWith(SpringRunner.class) @SpringBootTest public class MvcDemoApplicationTests { @Autowired WebApplicationContext context; MockMvc mockMvc; Logger logger; @Before public void init() { mockMvc = MockMvcBuilders.webAppContextSetup(context).build(); logger=LoggerFactory.getLogger(MvcDemoApplicationTests.class); } @Test public void testGet() throws Exception { String contentAsString = mockMvc.perform(MockMvcRequestBuilders.get("/book/1001") .contentType(MediaType.APPLICATION_JSON_UTF8)).andReturn() .getResponse().getContentAsString(); logger.debug(contentAsString); System.out.println(contentAsString); } @Test public void testInsert() throws Exception { String contentAsString = mockMvc.perform(MockMvcRequestBuilders.post("/book") .param("name", " dokcer ") .param("price", "150") .contentType(MediaType.APPLICATION_JSON_UTF8)) .andReturn().getResponse().getContentAsString(); System.out.println(contentAsString); } @Test public void testDelete() throws Exception { String contentAsString = mockMvc.perform(MockMvcRequestBuilders.delete("/book/1003") .contentType(MediaType.APPLICATION_JSON_UTF8)).andReturn() .getResponse().getContentAsString(); System.out.println(contentAsString); } @Test public void testUpdate() throws Exception { String contentAsString = mockMvc.perform(MockMvcRequestBuilders.put("/book") .param("id","1009") .param("price", "1500") .contentType(MediaType.APPLICATION_JSON_UTF8)) .andReturn().getResponse().getContentAsString(); System.out.println(contentAsString); } }
檢查數據源是否配置成功html
druid監控頁面java
2.@Pathvariable註解mysql
2.1配置正則實例web
@AllArgsConstructor @NoArgsConstructor @Data @Accessors(chain = true) //lombok public class User implements Serializable { private String name; }
@RestController //responsebody +controller
public class UserController {
@GetMapping("/user/{name:.*}")
public User testUser(@PathVariable(required = false,value = "name") User user){
System.out.println(user.getClass());
return user;
}
}
@SpringBootApplication public class MvcDemoApplication { public static void main(String[] args) { SpringApplication.run(MvcDemoApplication.class, args); } }
@Configuration @ComponentScan(value = "springmvc.demo")//掃描包 public class MVCConfig { }
spring.http.encoding.charset=UTF-8 spring.http.encoding.force=true spring.http.encoding.enabled=true server.tomcat.uri-encoding=UTF-8
3.@valid與@Validatedspring
@valid與@Validated的區別,@Validated是spring中的,是在@valid基礎上而來的,在@valid的基礎上增長分組功能,這裏就直接說@Validated,沒有加分組都須要認證,加了分組只有符合分組的才須要認證,通常不使用分組sql
3.1基本使用數據庫
@AllArgsConstructor @NoArgsConstructor @Data @Accessors(chain = true) public class User implements Serializable { @NotNull(message="不能爲空")//含有屬性groups private String name; }
@RestController
public class UserController {
@GetMapping("/user")
public User testUser(@Validated User user, BindingResult result){
result.getAllErrors().stream().forEach((x)->{
System.out.println(x.getObjectName()+":"+x.getDefaultMessage());
});
return user;
}
}
輸出結果:user:不能爲空json
備註:假如沒有BindingResult result,那麼在進入testUser以前就會被攔截,message="不能爲空"並無什麼做用,加入BindingResult result以後,纔會進入testUser方法,在沒有進入方法時的異常頁面見後該篇4tomcat
3.2自定義符合valid的註解
這裏定義的是@NoNull註解
//會自動加載,不須要其餘的配置
public class MyValidAnnotation implements ConstraintValidator<MyNotNull,Object> {
/*
obj:須要驗證的參數
*/
@Override
public boolean isValid(Object obj, ConstraintValidatorContext constraintValidatorContext) {
System.out.println(obj);
return false;
}
@Override
public void initialize(MyNotNull constraintAnnotation) {
}
}
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy =MyValidAnnotation.class )//添加在@validated的必要註解
public @interface MyNotNull {
String message() default "{javax.validation.constraints.NotNull.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
@AllArgsConstructor
@NoArgsConstructor
@Data
@Accessors(chain = true)
public class User implements Serializable {
@MyNotNull(message="不能爲空---MyNotNull")
private String name;
}
@RestController public class UserController { @GetMapping("/user") public User testUser(@Validated User user, BindingResult result){ result.getAllErrors().stream().forEach((x)->{ System.out.println(x.getObjectName()+":"+x.getDefaultMessage()); }); return user; } }
4.異常消息的處理
4.1驗證異常消息的處理上述已經說明
4.2直接被攔截的異常處理
springboot會相應的轉向咱們在resources下新建的resources的error的相應的錯誤代碼的頁面
還能夠是以下這樣,表示以4開頭的錯誤頁面,例如
4.3運行過程當中出現的異常
4.3.1直接拋出異常
@GetMapping("/user/error") public User testUser1(@Validated User user, BindingResult result) throws Exception { throw new Exception("出錯了"); }
4.3.2 throw自定義異常類
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR,reason = "內部出錯了!") public class MyException extends RuntimeException { }
@GetMapping("/user/error") public User testUser1(@Validated User user, BindingResult result) throws Exception { throw new MyException(); }
4.3.3 使用@ExceptionHandler
註解
@ResponseBody @GetMapping("/testExceptionHandler") public User testUser1(@Validated User user, BindingResult result) throws Exception { int i=10/0; return user; }
@ExceptionHandler({Exception.class}) public String testExceptionHandler(){ return "redirect:/error.html"; //重定向,/站點目錄 }
4.3.4 修改狀態碼及緣由,@ResponseStatus也能在
@ExceptionHandler({Exception.class}) @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR,reason = "內部錯誤") public void testExceptionHandler(){ }
備註:這樣以後不會去相應的頁面,也就是加入有返回值例如return "redirect:/error.html";,會失效
4.3.5@ControllerAdvice定製全局的異常,類中的@ExceptionHandler優先級高於@ControllerAdvice中的@ExceptionHandler優先級
@ControllerAdvice public class MyExceptionHandler { @ExceptionHandler({Exception.class}) @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR,reason = "內部錯誤") public void testExceptionHandler(){ } }
5.jsonview
@NoArgsConstructor @AllArgsConstructor @Data @Accessors(chain = true) public class User implements Serializable { public interface UserSimpleView{} ; public interface UserDetailView extends UserSimpleView{} ; @JsonView(UserSimpleView.class) private String username; @JsonView(UserDetailView.class) private Integer password; }
@RestController public class UserController { @Autowired UserMapper userMapper; @GetMapping("/user/{username}") @JsonView(User.UserSimpleView.class) public User testGet(@PathVariable String username){ User user = userMapper.getUserById(username); return user; } }
6.異步
6.1直接使用callable
@RestController public class AsyncController { @Autowired UserMapper userMapper; @GetMapping("/async/{username}") @JsonView(User.UserSimpleView.class) public Callable<User> get(@PathVariable String username){ long id = Thread.currentThread().getId(); long start= Instant.now().atZone(ZoneId.of("Asia/Shanghai")).toInstant().toEpochMilli(); Callable<User> callable = ()->{ long id1 = Thread.currentThread().getId(); long start1= Instant.now().atZone(ZoneId.of("Asia/Shanghai")).toInstant().toEpochMilli(); User user = userMapper.getUserById(username); long end1= Instant.now().atZone(ZoneId.of("Asia/Shanghai")).toInstant().toEpochMilli(); System.out.println("副線程"+id1+":"+(end1-start1)); return user; }; long end= Instant.now().atZone(ZoneId.of("Asia/Shanghai")).toInstant().toEpochMilli(); System.out.println("主線程"+id+":"+(end-start)); return callable; } }
6.2 使用DeferredResult實現異步
@Bean("queue") public ConcurrentLinkedQueue<String> concurrentLinkedQueue() throws IOException { ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>(); return queue; } @Bean("map") public Map<String, DeferredResult<String>> map(){ return new HashMap(); }
@Autowired ConcurrentLinkedQueue<String> queue; @Autowired Map map; @GetMapping("/async/deferredResult") public Object message(@RequestParam(required = false) String message){ if(StringUtils.isNotBlank(message)){ queue.add(message); DeferredResult<String> result = new DeferredResult<>(); map.put(message, result); return result; } return "輸入格式不正確"; }
@Component public class QueueListener implements ApplicationListener {//啓動監聽器 @Autowired ConcurrentLinkedQueue<String> queue; @Autowired Map<String, DeferredResult<String>> map; @Override public void onApplicationEvent(ApplicationEvent applicationEvent) { new Thread(()->{ while (true){ try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } if(queue.size()>0){ String msg = queue.poll(); System.out.println(msg); map.get(msg).setResult(msg); map.remove(msg); }else { try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } }
7.swagger
依賴
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency>
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> </dependency>
添加註解
@EnableSwagger2 public class ControllerTest {
訪問頁面
http://127.0.0.1:8080/swagger-ui.html
增長描述信息
public class Book implements Serializable { private Integer id; @ApiModelProperty("名稱") private String name; @ApiModelProperty("價格") private Integer price; }
@GetMapping("/book/{id:\\d+}") public Book get(@PathVariable @ApiParam("書本id") Integer id){ return bookMapper.getBookById(id); }
8.攔截器
8.1 HandlerInterceptor
@Component public class MyHandlerIntercepter implements HandlerInterceptor { //在調用目標方法以前執行 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true;//false表示後續攔截器不在執行 } //在調用目標方法以後, 但渲染視圖以前, // 能夠對請求域中的屬性或視圖作出修改.,出現異常不會執行該方法 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { //能獲取類名,方法名,可是不能獲取方法的參數 System.out.println(((HandlerMethod)handler).getBean().getClass().getName()); System.out.println(((HandlerMethod)handler).getMethod().getName()); } //以後執行,出現異常也會執行 @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }
@Configuration @ComponentScan(value = "springmvc.demo") @MapperScan("springmvc.demo.dao") public class MVCConfig implements WebMvcConfigurer { @Autowired MyHandlerIntercepter myHandlerIntercepter; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(myHandlerIntercepter); } }
8.2 AOP(攔截的加強版)
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
@Aspect @Component public class MyAOP { @Pointcut("execution(* springmvc.demo.controller..*(*))") public void pointcut(){} @Around("pointcut()") public Object before(ProceedingJoinPoint joinPoint) { try { Object[] args = joinPoint.getArgs(); Arrays.stream(args).forEach(System.out::println); Object proceed = joinPoint.proceed();//至關於攔截器的dochain方法,不然不會執行下去 return proceed;//返回結果後纔會有值 } catch (Throwable throwable) { return throwable.getMessage(); } } }