ssm(3-2)Springmvc拓展

1.rest风格增删改查及druid数据源的配置及mybatis的配置

@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);
    }
}

 检查数据源是否配置成功

 

 druid监控页面

2.@Pathvariable注解

2.1配置正则实例

@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与@Validated

@valid与@Validated的区别,@Validated是spring中的,是在@valid基础上而来的,在@valid的基础上增加分组功能,这里就直接说@Validated,没有加分组都需要认证,加了分组只有符合分组的才需要认证,一般不使用分组

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:不能为空

备注:假如没有BindingResult result,那么在进入testUser之前就会被拦截,message="不能为空"并没有什么作用,加入BindingResult result之后,才会进入testUser方法,在没有进入方法时的异常页面见后该篇4

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();
        }
    }
}

 

 

 

 

 


 

posted @ 2018-11-10 22:40  fatale  阅读(232)  评论(0编辑  收藏  举报