SpringBoot2 学习笔记(二) Aop、Cache缓存、swagger2

本文介绍如何在Spring Boot项目中整合AOP、缓存管理和Swagger文档生成,包括自定义AOP切面、配置缓存策略及使用Swagger2生成RESTful API文档。

Aop

1.引入Aop对应依赖

pom.xml

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-aop</artifactId>
 </dependency>

2. 自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {

    String value() default "";
}

3. 注册切面、切点

@Aspect
@Component
public class LogAspect {

    @Autowired
    private SysLogDao sysLogDao;

    @Pointcut("@annotation(com.example.aoptest.myinterface.Log)")
    public void pointcut(){};

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint point){
        Object result = null;
        long beginTime = System.currentTimeMillis();
        try{
            result = point.proceed();
        }catch (Throwable e){
            e.printStackTrace();
        }
        long time = System.currentTimeMillis() - beginTime;
        saveLogs(point,time);
        return result;
    }

    private void saveLogs(ProceedingJoinPoint point, long time){
        SysLog log = new SysLog();
        //获取method
        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        Method method = methodSignature.getMethod();
        //获取注解对象
        Log logAnnotation = method.getAnnotation(Log.class);
        //获取注解的描述
        if (logAnnotation != null){
            log.setOperation(logAnnotation.value());
        }
        String className = point.getTarget().getClass().getName();
        log.setMethod(className + "." + method.getName());
        //获取参数信息
        Object[] args = point.getArgs();
        LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
        String[] paramNames = u.getParameterNames(method);
        if (args != null && paramNames != null){
            StringBuilder params = new StringBuilder();
            for (int i=0; i<args.length; i++){
                params.append(args[i] + ": " + paramNames[i]+" ");
            }
            log.setParams(params.toString());
        }
        //获取request
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String ip = IPUtils.getIpAddress(request);
        log.setIp(ip);
        log.setUsername("qiang");
        log.setCreateTime(new Date());
        log.setTime((int)time);
        sysLogDao.saveSysLog(log);
    }
}

4.使用

@RestController
public class AopController {

    @Log("执行方法一")
    @GetMapping("/one")
    public void methodOne(String name) { }

    @Log("执行方法二")
    @GetMapping("/two")
    public void methodTwo() throws InterruptedException {
        Thread.sleep(2000);
    }

    @Log("执行方法三")
    @GetMapping("/three")
    public void methodThree(String name, String age) { }

}

Cache

1.开启缓存支持

pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

接着在Spring Boot入口类中加入@EnableCaching注解开启缓存功能:

@SpringBootApplication
@EnableCaching
public class CachetestApplication {

2.配置redis(或其他缓存工具 Ehcache)

在application.yml中设置redis连接信息

spring:
  redis:
    # Redis数据库索引(默认为0)
    database: 0
    # Redis服务器地址
    host: localhost
    # Redis服务器连接端口
    port: 6379
    jedis:
      pool:
        # 连接池最大连接数(使用负值表示没有限制)
        max-active: 8
        # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1
        # 连接池中的最大空闲连接
        max-idle: 8
        # 连接池中的最小空闲连接
        min-idle: 4
    # 连接超时时间(毫秒)
    timeout: 10000

编写redis配置类

@Configuration
public class RedisConfiguration extends CachingConfigurerSupport {

    @Bean
    @ConditionalOnBean
    public ObjectMapper objectMapper() {
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); //补上
        return om;
    }

    @Bean
    public Jackson2JsonRedisSerializer jackson2JsonRedisSerializer (ObjectMapper om) {

        Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
        serializer.setObjectMapper(om);
        return serializer;
    }

    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory,
                                       Jackson2JsonRedisSerializer serializer) {

        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        RedisSerializer stringSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringSerializer);//key序列化
        redisTemplate.setValueSerializer(serializer);//value序列化
        redisTemplate.setHashKeySerializer(stringSerializer);//Hash key序列化
        redisTemplate.setHashValueSerializer(serializer);//Hash value序列化
        redisTemplate.afterPropertiesSet();
        return redisTemplate;

    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(1)); // 设置缓存有效期一小时
        return RedisCacheManager
                .builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
                .cacheDefaults(redisCacheConfiguration).build();
    }

    @Bean
    @Override
    public KeyGenerator keyGenerator() {
        return (target, method, objects) -> {
            StringBuilder sb = new StringBuilder();
            sb.append(target.getClass().getName());
            sb.append("::" + method.getName() + ":");
            for (Object obj : objects) {
                sb.append(obj.toString());
            }
            return sb.toString();
        };
    }
}

3.使用

在service层中缓存数据查询结果

@CacheConfig(cacheNames = "student")
public interface StudentService {

    @CachePut(key = "#p0.id")
    Student update(Student student);

    @CacheEvict(key = "#p0")
    void deleteStudentBySno(int id);

    @Cacheable(key = "#p0")
    Student queryStudentBySno(int id);
}

缓存注解

  1. @CacheConfig:主要用于配置该类中会用到的一些共用的缓存配置。在这里@CacheConfig(cacheNames = “student”):配置了该数据访问对象中返回的内容将存储于名为student的缓存对象中,我们也可以不使用该注解,直接通过@Cacheable自己配置缓存集的名字来定义;

  2. @Cacheable:配置了queryStudentBySno函数的返回值将被加入缓存。同时在查询时,会先从缓存中获取,若不存在才再发起对数据库的访问。该注解主要有下面几个参数:

  • value、cacheNames:两个等同的参数(cacheNames为Spring 4新增,作为value的别名),用于指定缓存存储的集合名。由于Spring 4中新增了@CacheConfig,因此在Spring 3中原本必须有的value属性,也成为非必需项了;
  • key:缓存对象存储在Map集合中的key值,非必需,缺省按照函数的所有参数组合作为key值,若自己配置需使用SpEL表达式,比如:@Cacheable(key = “#p0”):使用函数第一个参数作为缓存的key值,更多关于SpEL表达式的详细内容可参考https://docs.spring.io/spring/docs/current/spring-framework-reference/integration.html#cache;
  • condition:缓存对象的条件,非必需,也需使用SpEL表达式,只有满足表达式条件的内容才会被缓存,比如:@Cacheable(key = “#p0”, condition = “#p0.length() < 3”),表示只有当第一个参数的长度小于3的时候才会被缓存;
  • unless:另外一个缓存条件参数,非必需,需使用SpEL表达式。它不同于condition参数的地方在于它的判断时机,该条件是在函数被调用之后才做判断的,所以它可以通过对result进行判断;
  • keyGenerator:用于指定key生成器,非必需。若需要指定一个自定义的key生成器,我们需要去实现org.springframework.cache.interceptor.KeyGenerator接口,并使用该参数来指定;
  • cacheManager:用于指定使用哪个缓存管理器,非必需。只有当有多个时才需要使用;
  • cacheResolver:用于指定使用那个缓存解析器,非必需。需通过org.springframework.cache.interceptor.CacheResolver接口来实现自己的缓存解析器,并用该参数指定;
  1. @CachePut:配置于函数上,能够根据参数定义条件来进行缓存,其缓存的是方法的返回值,它与@Cacheable不同的是,它每次都会真实调用函数,所以主要用于数据新增和修改操作上。它的参数与@Cacheable类似,具体功能可参考上面对@Cacheable参数的解析;

  2. @CacheEvict:配置于函数上,通常用在删除方法上,用来从缓存中移除相应数据。除了同@Cacheable一样的参数之外,它还有下面两个参数:

  • allEntries:非必需,默认为false。当为true时,会移除所有数据;
  • beforeInvocation:非必需,默认为false,会在调用方法之后移除数据。当为true时,会在调用方法之前移除数据。

Swagger2

引入Swagger依赖

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.6.1</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.6.1</version>
</dependency>

配置SwaggerConfig

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    public Docket buildDocket() {
        return new Docket(DocumentationType.SWAGGER_2)
            .apiInfo(buildApiInf())
            .select()
            .apis(RequestHandlerSelectors.basePackage("com.example.demo.controller"))
            .paths(PathSelectors.any())
            .build();
    }
    private ApiInfo buildApiInf() {
        return new ApiInfoBuilder()
            .title("系统RESTful API文档")
            .contact(new Contact("mrbird", "https://mrbird.cc", "852252810@qq.com"))
            .version("1.0")
            .build();
    }
}

Swagger常用注解

  • @Api:修饰整个类,描述Controller的作用;
  • @ApiOperation:描述一个类的一个方法,或者说一个接口;
  • @ApiParam:单个参数描述;
  • @ApiModel:用对象来接收参数;
  • @ApiProperty:用对象接收参数时,描述对象的一个字段;
  • @ApiResponse:HTTP响应其中1个描述;
  • @ApiResponses:HTTP响应整体描述;
  • @ApiIgnore:使用该注解忽略这个API;
  • @ApiError :发生错误返回的信息;
  • @ApiImplicitParam:一个请求参数;
  • @ApiImplicitParams:多个请求参数。

使用

@Api(value = "用户Controller")
@Controller
@RequestMapping("user")
public class UserController {

    @ApiIgnore
    @GetMapping("hello")
    public @ResponseBody String hello() {
        return "hello";
    }

    @ApiOperation(value = "获取用户信息", notes = "根据用户id获取用户信息")
    @ApiImplicitParam(name = "id", value = "用户id", required = true, dataType = "Long", paramType = "path")
    @GetMapping("/{id}")
    public @ResponseBody User getUserById(@PathVariable(value = "id") Long id) {
        User user = new User();
        user.setId(id);
        user.setName("mrbird");
        user.setAge(25);
        return user;
    }

    @ApiOperation(value = "获取用户列表", notes = "获取用户列表")
    @GetMapping("/list")
    public @ResponseBody List<User> getUserList() {
        List<User> list = new ArrayList<>();
        User user1 = new User();
        user1.setId(1l);
        user1.setName("mrbird");
        user1.setAge(25);
        list.add(user1);
        User user2 = new User();
        user2.setId(2l);
        user2.setName("scott");
        user2.setAge(29);
        list.add(user2);
        return list;
    }

    @ApiOperation(value = "新增用户", notes = "根据用户实体创建用户")
    @ApiImplicitParam(name = "user", value = "用户实体", required = true, dataType = "User")
    @PostMapping("/add")
    public @ResponseBody Map<String, Object> addUser(@RequestBody User user) {
        Map<String, Object> map = new HashMap<>();
        map.put("result", "success");
        return map;
    }

    @ApiOperation(value = "删除用户", notes = "根据用户id删除用户")
    @ApiImplicitParam(name = "id", value = "用户id", required = true, dataType = "Long", paramType = "path")
    @DeleteMapping("/{id}")
    public @ResponseBody Map<String, Object> deleteUser(@PathVariable(value = "id") Long id) {
        Map<String, Object> map = new HashMap<>();
        map.put("result", "success");
        return map;
    }

    @ApiOperation(value = "更新用户", notes = "根据用户id更新用户")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "id", value = "用户id", required = true, dataType = "Long", paramType = "path"),
        @ApiImplicitParam(name = "user", value = "用户实体", required = true, dataType = "User") })
    @PutMapping("/{id}")
    public @ResponseBody Map<String, Object> updateUser(@PathVariable(value = "id") Long id, @RequestBody User user) {
        Map<String, Object> map = new HashMap<>();
        map.put("result", "success");
        return map;
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值