摘要:各位大厂的后端开发小伙伴们!在咱们日常的开发工作中,是不是经常会遇到这样的烦恼:像日志记录、权限验证、事务管理这类横切逻辑代码,总是穿插在业务代码里,导致代码变得又长又乱,维护起来超级麻烦?而且每次新增或修改一个功能,都要在多个地方重复编写这些横切逻辑代码,不
各位大厂的后端开发小伙伴们!在咱们日常的开发工作中,是不是经常会遇到这样的烦恼:像日志记录、权限验证、事务管理这类横切逻辑代码,总是穿插在业务代码里,导致代码变得又长又乱,维护起来超级麻烦?而且每次新增或修改一个功能,都要在多个地方重复编写这些横切逻辑代码,不仅浪费时间,还容易出现疏漏。别急,今天就给大家介绍一个超实用的 “神器”——Spring Boot AOP 中用到的代理设计模式,它能帮我们轻松解决这些难题!
Spring Boot 作为当下后端开发的热门框架,AOP(面向切面编程)是其重要的功能之一,而代理设计模式则是 AOP 实现的核心基础。代理模式,从本质上来说,是一种为其他对象提供代理以控制对该对象访问的设计模式。在 Spring Boot AOP 中,主要运用了 JDK 动态代理和 CGLIB 动态代理这两种动态代理方式。
JDK 动态代理是基于接口实现的,它利用反射机制生成实现代理接口的匿名类。当调用代理对象的具体方法时,实际上会先调用invoke方法,通过这种方式来实现业务增强,所以它比较适用于代理对象有接口的情况。举个简单的例子,假设我们有一个UserService接口和它的实现类UserServiceImpl,想要对UserService中的方法进行增强,就可以使用 JDK 动态代理。
import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;interface UserService {void addUser;}class UserServiceImpl implements UserService {@Overridepublic void addUser {System.out.println("添加用户");}}class JDKProxyHandler implements InvocationHandler {private Object target;public JDKProxyHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object args) throws Throwable {System.out.println("JDK动态代理:方法调用前增强逻辑");Object result = method.invoke(target, args);System.out.println("JDK动态代理:方法调用后增强逻辑");return result;}}public class JDKProxyExample {public static void main(String args) {UserService userService = new UserServiceImpl;JDKProxyHandler handler = new JDKProxyHandler(userService);UserService proxy = (UserService) Proxy.newProxyInstance(userService.getClass.getClassLoader,userService.getClass.getInterfaces,handler);proxy.addUser;}}而 CGLIB 动态代理则是通过生成目标类的子类来实现代理,它利用 asm 开源包,加载代理对象类的 class 文件,并修改字节码来生成子类进行处理。当我们的代理对象没有接口时,CGLIB 动态代理就派上用场了。例如,有一个没有实现任何接口的OrderService类,想要对它的方法进行增强,CGLIB 动态代理就很合适。
import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;class OrderService {public void placeOrder {System.out.println("下单");}}class CglibProxyHandler implements MethodInterceptor {@Overridepublic Object intercept(Object o, Method method, Object objects, MethodProxy methodProxy) throws Throwable {System.out.println("CGLIB动态代理:方法调用前增强逻辑");Object result = methodProxy.invokeSuper(o, objects);System.out.println("CGLIB动态代理:方法调用后增强逻辑");return result;}}public class CglibProxyExample {public static void main(String args) {Enhancer enhancer = new Enhancer;enhancer.setSuperclass(OrderService.class);enhancer.setCallback(new CglibProxyHandler);OrderService proxy = (OrderService) enhancer.create;proxy.placeOrder;}}这两种代理方式相互配合,让 Spring Boot AOP 能够在不修改原有业务逻辑代码的前提下,实现强大的功能增强。
日志记录:让代码 “有迹可循”
对于日志记录功能,我们可以定义一个切面类,使用注解来标注切点,比如在需要记录日志的业务方法上标注切点表达式。然后,在切面类中编写通知方法,利用代理机制,在目标方法执行前记录方法的入参信息,在方法执行后记录返回结果和执行时间等信息。
import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.springframework.stereotype.Component;@Aspect@Componentpublic class LogAspect {@Around("execution(* com.example.demo.service.*.*(..))")public Object log(ProceedingJoinPoint joinPoint) throws Throwable {long startTime = System.currentTimeMillis;System.out.println("方法名:" + joinPoint.getSignature.getName + ",入参:" + java.util.Arrays.toString(joinPoint.getArgs));Object result = joinPoint.proceed;long endTime = System.currentTimeMillis;System.out.println("方法名:" + joinPoint.getSignature.getName + ",执行时间:" + (endTime - startTime) + "ms,返回结果:" + result);return result;}}这样,我们就把日志记录的逻辑从业务代码中分离出来了,业务代码只专注于核心业务逻辑,大大提高了代码的可读性和可维护性。
权限验证:守护系统安全的 “卫士”
在权限验证方面,同样可以借助代理设计模式。我们可以创建一个权限验证的切面,通过切点表达式定位到需要进行权限验证的方法。在切面的通知方法中,获取当前用户的权限信息,与目标方法所需的权限进行比对,如果权限不足,就抛出相应的异常,阻止方法的执行。
import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.springframework.stereotype.Component;@Aspect@Componentpublic class PermissionAspect {@Around("execution(* com.example.demo.service.admin.*.*(..))")public Object permissionCheck(ProceedingJoinPoint joinPoint) throws Throwable {// 模拟获取当前用户权限,这里假设1表示管理员权限int currentUserPermission = 0;if (currentUserPermission通过这种方式,我们实现了权限验证逻辑的集中管理,无需在每个业务方法中都编写繁琐的权限验证代码。
事务管理:保障数据的 “坚固堡垒”
至于事务管理,Spring Boot AOP 中的代理设计模式也能发挥巨大作用。我们可以配置事务切面,利用代理自动开启、提交和回滚事务。当业务方法被调用时,代理会根据配置自动处理事务,确保数据的一致性和完整性,避免了因手动管理事务不当而导致的数据问题。
在 Spring Boot 项目中,我们只需要在启动类上添加@EnableTransactionManagement注解开启事务管理,然后在业务方法所在的类或方法上添加@Transactional注解,Spring Boot 就会通过代理机制自动处理事务。
import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;@Servicepublic class TransactionService {private final UserRepository userRepository;private final OrderRepository orderRepository;public TransactionService(UserRepository userRepository, OrderRepository orderRepository) {this.userRepository = userRepository;this.orderRepository = orderRepository;}@Transactionalpublic void createUserAndOrder(User user, Order order) {userRepository.save(user);// 假设这里抛出异常,user和order的插入操作都会回滚int i = 1 / 0; orderRepository.save(order);}}总结一下,Spring Boot AOP 中用到的代理设计模式,就像是一位贴心的助手,帮我们把横切逻辑代码从业务代码中分离出来,实现了代码的解耦,降低了模块间的耦合度,让我们的代码更加简洁、清晰、易于维护。同时,它还提供了强大的灵活性,我们可以根据实际需求,在运行时动态创建代理对象,实现更灵活的配置和扩展。
各位后端开发的小伙伴们,赶紧在自己的项目中实践一下 Spring Boot AOP 中的代理设计模式吧!如果在使用过程中有任何问题、经验或者新的发现,欢迎在评论区留言分享,咱们一起交流学习,共同提升后端开发技能!
来源:从程序员到架构师一点号