ssm(3-2)Springmvc拓展

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();
        }
    }
}
相關文章
相關標籤/搜索