仿牛客网项目(六)

文章详细介绍了Spring项目中实现全局异常处理的三种方式,特别是通过@ControllerAdvice注解的使用。同时,讲解了日志记录如何利用AOP进行增强,以及Redis的特性、应用场景和数据类型。最后,文章演示了SpringBoot如何整合Redis,包括配置、序列化以及不同数据类型的操作。
摘要由CSDN通过智能技术生成

异常统一处理

功能分析

在 Spring 项目中,我们可以通过如下三种常见方案来实现全局统一异常处理。

  • 基于 SpringBoot 的全局统一异常处理,需要实现 ErrorController 接口。
  • 基于 Spring AOP 实现全局统一异常处理。
  • 基于 @ControllerAdvice 注解实现 Controller 层全局统一异常处理。

使用统一异常处理的优点:

  • 标准统一的返回结果,系统交互更加友好
  • 有效防止业务异常没有被捕获的情况
  • 代码更加干净简洁,不需要开发者自己定义维护异常

本项目采用@ControllerAdvice注解的方式实现异常统一处理;
@ControllerAdvice作用于类上,使用该注解可以实现三个方面的功能:1. 全局异常处理;2. 全局数据绑定;3. 全局数据预处理。在项目中使用这个注解可以帮我们简化很多工作,它是 SpringMVC 提供的功能,并且在 SpringBoot 中也可以直接使用。

在进行全局异常处理时,需要配合 @ExceptionHandler 注解使用。

  • @ExceptionHandler

    • 用于修饰方法,该方法会在Controller出现异常后被调用,用于处理捕获到的异常
  • @ModelAttribute

    • 用于修饰方法,该方法会在Controller方法执行前被调用,用于为Model对象绑定参数。
  • @DataBinder

    • 用于修饰方法,该方法会在Controller方法执行前被调用,用于绑定参数的转换器。

在 controller 包下新建 advice 包,添加 ExceptionAdvice 类,代码如下:

@ControllerAdvice(annotations = Controller.class)
public class ExceptionAdvice {

    private static final Logger logger = LoggerFactory.getLogger(ExceptionAdvice.class);

    @ExceptionHandler({Exception.class})
    public void handleException(Exception e, HttpServletRequest request, HttpServletResponse response) throws IOException {
        logger.error("服务器发生异常: " + e.getMessage());
        for (StackTraceElement element : e.getStackTrace()) {
            logger.error(element.toString());
        }

        String xRequestedWith = request.getHeader("x-requested-with");
        //异步请求
        if ("XMLHttpRequest".equals(xRequestedWith)) {
            response.setContentType("application/plain;charset=utf-8");
            PrintWriter writer = response.getWriter();
            writer.write(CommunityUtil.getJSONString(1, "服务器异常!"));
        } else {
            response.sendRedirect(request.getContextPath() + "/error");
        }
    }
}

在HomeController中添加getErrorPage方法,代码如下:

 @GetMapping("/error")
    public String getErrorPage() {
        return "/error/500";

    }

功能测试

在这里插入图片描述

日志记录

功能分析

  • AOP(面向切面编程)
    很抽象的一个概念,AOP是一种编程思想,是对OOP的补充,它和OOP是一种互补的状态并非竞争状态,可以进一步提高编程的效率。

在这里插入图片描述
在这里插入图片描述

  • Spring AOP
    • JDK动态代理
      ava提供的动态代理技术,可以在运行时创建接口的代理实例。
      Spring AOP默认采用此种方式,在接口的代理实例中织入代码。
    • CGLib动态代理
      采用底层的字节码技术,在运行时创建子类代理实例。
      当目标对象不存在接口时,Spring AOP会采用此种方式,在子类实例中织入代码。

在controller包下新建aspectt包,新建ServiceLogAspect类,代码如下:

@Component
@Aspect
public class ServiceLogAspect {

    private static final Logger logger = LoggerFactory.getLogger(ServiceLogAspect.class);

    @Pointcut("execution(* com.nowcoder.community.service.*.*(..))")
    public void pointcut() {

    }
    @Before("pointcut()")
    public void before(JoinPoint joinPoint) {
        // 用户[1.2.3.4],在[xxx],访问了[com.gerrard.community.service.xxx()].
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String ip = request.getRemoteHost();
        String now = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        String target = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
        logger.info(String.format("用户[%s],在[%s],访问了[%s].", ip, now, target));
    }
}

功能测试

在这里插入图片描述

Redis

为什么用Redis

  • 读写性能优异
  • 数据类型丰富
    Redis 支持二进制案例的String,Lists,Hashes,Sets及Ordered Sets数据类型操作
  • 原子性:Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。
  • 丰富的特性
    Redis支持publish/subscribe,通知,key过期等特性
  • 持久化:Redis支持RDB,AOF等持久化方式
  • 发布订阅
  • 分布式

为什么Redis是单线程还那么快

在这里插入图片描述

Redis一般使用场景

  • 热点数据的存储
  • 限时业务的运用
  • 计数器相关问题
  • 分布式锁

Redis五大数据类型

在这里插入图片描述

spring整合Redis

  • 添加依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  • 参数配置
    • 配置数据库参数
    # RedisProperties
    spring.redis.database=11
    spring.redis.host=localhost
    spring.redis.port=6379
    
    • 编写配置类,构造 RedisTemplate
      在config包下新建RedisConfig类,代码如下:
    @Configuration
    public class RedisConfig {
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
    
        // 设置key的序列化方式
        template.setKeySerializer(RedisSerializer.string());
    
        // 设置value的序列化方式
        template.setValueSerializer(RedisSerializer.json());
    
        // 设置hash的key序列化方式
        template.setHashKeySerializer(RedisSerializer.string());
    
        // 设置hash的value序列化方式
        template.setHashValueSerializer(RedisSerializer.json());
    
        template.afterPropertiesSet();;
        return template;
    	}
    }
    
  • 访问Redis
    redisTemplate.opsForValue()
    redisTemplate.opsForHash()
    redisTemplate.opsForList()
    redisTemplate.opsForSet()
    redisTemplate.opsForZSet()

添加 RedisTests 测试类,各种测试的代码如下:

@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class RedisTests {

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    public void testStrings() {
        String redisKey = "test:count";

        System.out.println("result:");
        redisTemplate.opsForValue().set(redisKey, 1);

        System.out.println(redisTemplate.opsForValue().get(redisKey));
        System.out.println(redisTemplate.opsForValue().increment(redisKey));
        System.out.println(redisTemplate.opsForValue().decrement(redisKey));
    }


    @Test
    public void testHashes() {
        String redisKey = "test:user";

        redisTemplate.opsForHash().put(redisKey, "id", 1);
        redisTemplate.opsForHash().put(redisKey, "username", "zhangsan");

        System.out.println(redisTemplate.opsForHash().get(redisKey, "id"));
        System.out.println(redisTemplate.opsForHash().get(redisKey, "username"));
    }

    @Test
    public void testLists() {
        String redisKey = "test:ids";

        redisTemplate.opsForList().leftPush(redisKey, 101);
        redisTemplate.opsForList().leftPush(redisKey, 102);
        redisTemplate.opsForList().leftPush(redisKey, 103);

        System.out.println(redisTemplate.opsForList().size(redisKey));
        System.out.println(redisTemplate.opsForList().index(redisKey, 0));
        System.out.println(redisTemplate.opsForList().range(redisKey,0, 2));

        System.out.println(redisTemplate.opsForList().leftPop(redisKey));
        System.out.println(redisTemplate.opsForList().leftPop(redisKey));
        System.out.println(redisTemplate.opsForList().leftPop(redisKey));
    }
    @Test
    public void testSet() {
        String redisKey = "test:teacher";

        redisTemplate.opsForSet().add(redisKey,"张三", "李四", "王五", "赵六");
    System.out.println(redisTemplate.opsForSet().size(redisKey));
        System.out.println(redisTemplate.opsForSet().pop(redisKey));
        System.out.println(redisTemplate.opsForSet().members(redisKey));
    }

    @Test
    public void testSortedSet() {
        String redisKey = "test:students";

        redisTemplate.opsForZSet().add(redisKey, "张三", 10);
        redisTemplate.opsForZSet().add(redisKey, "李四", 20);
        redisTemplate.opsForZSet().add(redisKey, "王五", 40);
        redisTemplate.opsForZSet().add(redisKey, "赵六", 30);
        System.out.println(redisTemplate.opsForZSet().zCard(redisKey));
        System.out.println(redisTemplate.opsForZSet().score(redisKey,"张三"));
        System.out.println(redisTemplate.opsForZSet().reverseRank(redisKey,"张三"));
        System.out.println(redisTemplate.opsForZSet().reverseRange(redisKey,0, 2));
    }
    @Test
    public void testKeys() {
        redisTemplate.delete("test:user");

        System.out.println(redisTemplate.hasKey("test:user"));

        redisTemplate.expire("test:students", 10, TimeUnit.SECONDS);
    }
    // 多次访问同一个key
    @Test
    public void testBoundOperations() {
        String redisKey = "test:count";
        BoundValueOperations operations = redisTemplate.boundValueOps(redisKey);
        operations.increment();
        operations.increment();
        operations.increment();
        operations.increment();
        operations.increment();
        operations.increment();
        System.out.println(operations.get());
    }
    // 编程式事务
    @Test
    public void testTransactional() {
        Object obj = redisTemplate.execute(new SessionCallback() {
            @Override
            public Object execute(RedisOperations operations) throws DataAccessException {
                String redisKey = "test:tx";

                operations.multi();

                operations.opsForSet().add(redisKey, "aa");
                operations.opsForSet().add(redisKey, "bb");
                operations.opsForSet().add(redisKey, "cc");

                return operations.exec();
            }
        });
        System.out.println(obj);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值