当前位置: 首页> 文旅> 艺术 > 广州企业网站设计_装修材料网购平台_爱站网关键词挖掘工具_网站搭建步骤

广州企业网站设计_装修材料网购平台_爱站网关键词挖掘工具_网站搭建步骤

时间:2025/7/28 18:28:27来源:https://blog.csdn.net/wswpomos/article/details/142961027 浏览次数:0次
广州企业网站设计_装修材料网购平台_爱站网关键词挖掘工具_网站搭建步骤

AOP 是 Spring 体系中非常重要的两个概念之一(另外一个是 IoC),今天这篇文章实战的方式使用 AOP 技术添加一个切面来实现接口访问的统一日志记录,进行公共字段填充。

了解AOP

AOP,也就是 Aspect-oriented Programming,译为面向切面编程,是计算机科学中的一个设计思想,旨在通过切面技术为业务主体增加额外的通知(Advice),从而对声明为“切点”(Pointcut)的代码块进行统一管理和装饰。

AOP 是对面向对象编程(Object-oriented Programming,俗称 OOP)的一种补充,OOP 的核心单元是类(class),而 AOP 的核心单元是切面(Aspect)。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而降低耦合度,提高程序的可重用性,同时也提高了开发效率。

我们可以简单的把 AOP 理解为贯穿于方法之中,在方法执行前、执行时、执行后、返回值后、异常后要执行的操作。

AOP相关术语

来看下面这幅图,这是一个 AOP 的模型图,就是在某些方法执行前后执行一些通用的操作,并且这些操作不会影响程序本身的运行。

 我们了解下 AOP 涉及到的 5 个关键术语:

1)横切关注点,从每个方法中抽取出来的同一类非核心业务

2)切面(Aspect),对横切关注点进行封装的类,每个关注点体现为一个通知方法;通常使用 @Aspect 注解来定义切面。

3)通知(Advice),切面必须要完成的各个具体工作,比如我们的日志切面需要记录接口调用前后的时长,就需要在调用接口前后记录时间,再取差值。通知的方式有五种:

  • @Before:通知方法会在目标方法调用之前执行
  • @After:通知方法会在目标方法调用后执行
  • @AfterReturning:通知方法会在目标方法返回后执行
  • @AfterThrowing:通知方法会在目标方法抛出异常后执行
  • @Around:把整个目标方法包裹起来,在被调用前和调用之后分别执行通知方法

4)连接点(JoinPoint),通知应用的时机,比如接口方法被调用时就是日志切面的连接点。

5)切点(Pointcut),通知功能被应用的范围,比如本篇日志切面的应用范围是所有 controller 的接口。通常使用 @Pointcut 注解来定义切点表达式。

切入点表达式的语法格式规范如下所示:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)throws-pattern?)
  • modifiers-pattern? 为访问权限修饰符
  • ret-type-pattern 为返回类型,通常用 * 来表示任意返回类型
  • declaring-type-pattern? 为包名
  • name-pattern 为方法名,可以使用 * 来表示所有,或者 set* 来表示所有以 set 开头的类名
  • param-pattern) 为参数类型,多个参数可以用 , 隔开,各个参与也可以使用 * 来表示所有类型的参数,还可以使用 (..) 表示零个或者任意参数
  • throws-pattern? 为异常类型
  • ? 表示前面的为可选项

举个例子:

@Pointcut("execution(public * com.codingmore.controller.*.*(..))")

表示 com.codingmore.controller 包下的所有 public 方法都要应用切面的通知。

在 Spring Boot 项目的 pom.xml 文件中添加 spring-boot-starter-aop 依赖。

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

AOP实现公共字段填充

实现对于添加,更新数据库操作时,对于创建时间,更新时间这些公共字段代码耦合度高,使用AOP实现自动填充字段。

自定义注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {//数据库操作类型 INSERT UPDATEOperationType value();}

 切面类:

@Aspect
@Component
@Slf4j
public class AutoFillAspect {/*** 切入点  指定自定义注解的位置*/@Pointcut("execution(* com.test.mapper.*.*(..)) && @annotation(com.test.annotation.AutoFill)")public void autoFillPointCut(){}/*** 前置通知* joinPoint 连接点对象*/@Before("autoFillPointCut()")public void autoFill(JoinPoint joinPoint){log.info("开始进行公共字段填充...");//获取当前被拦截的方法上的数据库操作 获取签名对象转型MethodSignature signature = (MethodSignature) joinPoint.getSignature();//通过反射获取注解对象AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获取操作类型OperationType operationType = autoFill.value();//获取当前被拦截的方法的参数--实体对象Object[] args = joinPoint.getArgs();if (args == null || args.length == 0){return;}//获取第一个参数Object entity = args[0];//准备赋值的数据LocalDateTime now = LocalDateTime.now();Long currentId = BaseContext.getCurrentId();//根据当前不同的操作类型,为对应的属性通过反射来赋值if (operationType == OperationType.INSERT){try {//对象的getClass方法获取class对象,获取自定义声明方法,通过反射获取类型Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);//反射赋值setCreateTime.invoke(entity , now);setCreateUser.invoke(entity , currentId);setUpdateTime.invoke(entity , now);setUpdateUser.invoke(entity , currentId);} catch (NoSuchMethodException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}} else if (operationType == OperationType.UPDATE) {try {//对象的getClass方法获取class对象,获取自定义声明方法,通过反射获取类型Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);setUpdateTime.invoke(entity , now);setUpdateUser.invoke(entity , currentId);} catch (NoSuchMethodException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}
}

使用的时候只需要把自定义注解贴到Dao层方法上面

实现前置接口拦截,登录才能访问

自定义注解:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequireLogin {
}

拦截器:

public class RequireLoginInterceptor implements HandlerInterceptor {private StringRedisTemplate redisTemplate;public RequireLoginInterceptor(StringRedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 判断当前请求是否是一个 api 请求if (handler instanceof HandlerMethod) {HandlerMethod handlerMethod = (HandlerMethod) handler;// 从请求头中获取 Feign 请求标识,以此来判断该请求是否是 FeignString feignRequest = request.getHeader(CommonConstants.FEIGN_REQUEST_KEY);// Feign请求标识不为空 && 不是 Feign 请求 && 访问的接口方法贴了 @RequireLoginif (!StringUtils.isEmpty(feignRequest)&& CommonConstants.FEIGN_REQUEST_FALSE.equals(feignRequest)&& handlerMethod.getMethodAnnotation(RequireLogin.class) != null) {// 设置响应类型为 jsonresponse.setContentType("application/json;charset=utf-8");// 从请求头中获取 tokenString token = request.getHeader(CommonConstants.TOKEN_NAME);if (StringUtils.isEmpty(token)) {// 如果 token 为空,返回 token 无效信息response.getWriter().write(JSON.toJSONString(Result.error(CommonCodeMsg.TOKEN_INVALID)));return false;}UserInfo userInfo = JSON.parseObject(redisTemplate.opsForValue().get(CommonRedisKey.USER_TOKEN.getRealKey(token)), UserInfo.class);// 基于 token 从 redis 中获取当前用户,如果获取不到,说明 token 无效if (userInfo == null) {response.getWriter().write(JSON.toJSONString(Result.error(CommonCodeMsg.TOKEN_INVALID)));return false;}}}// 如果不是接口请求,就直接放行return true;}
}

使用直接贴在控制层接口方法上

关键字:广州企业网站设计_装修材料网购平台_爱站网关键词挖掘工具_网站搭建步骤

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: