spring切面注解失效

article/2025/8/18 19:37:15

在项目中使用切面注解做数据脱敏时,导出的数据也需要脱敏处理,遇到了在一个类里面调用本类的方法切面失效,解决方法如下:

切面注解:

package com.t3.ts.driver.resume.aspect;import java.lang.annotation.*;/*** @Description: 数据脱敏注解 Filed* @Date: 2019/9/10* @Author: wm yu*/
@Inherited
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptField {
}
package com.t3.ts.driver.resume.aspect;import java.lang.annotation.*;/*** @Description: 数据脱敏注解 Method* @Date: 2019/9/10* @Author: wm yu*/
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptMethod {
}

切面类:

package com.t3.ts.driver.resume.aspect;import com.alibaba.fastjson.JSON;
import com.t3.ts.driver.resume.utils.MD5Util;
import com.t3.ts.driver.resume.utils.StringUtils;
import com.t3.ts.driver.resume.utils.excel.FieldReflectionUtil;
import com.t3.ts.result.PageResult;
import com.t3.ts.result.Response;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;import java.lang.reflect.Field;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;/*** @class_name: DecryptAspect* @description:* @author: wm_yu* @create: 2019/09/10**/
@Aspect
@Component
@Order(-1)
public class EncryptAspect {private final static Logger log = LoggerFactory.getLogger(EncryptAspect.class);/*** 身份证脱敏正则 保留前后四位*/private final static String IDENTITY_CARD_DESENSITIZATION = "(?<=\\d{4})\\d(?=\\d{4})";/***银行卡脱敏正则  保留前后四位*/private final static String BLANK_CARD_DESENSITIZATION = "(?<=\\d{4})\\d(?=\\d{4})";/*** 手机号脱敏正则 保留前三后四位*/private final static String MOBILE_DESENSITIZATION = "(?<=\\d{3})\\d(?=\\d{4})";/*** 定义其他字段脱敏长度*/private final static Integer OTHER_DESENSITIZATION_LENGTH = 3;private final static Integer IDENTITY_CARD_LENGTH_18 = 18;private final static Integer IDENTITY_CARD_LENGTH_15 = 15;private final static Integer MOBILE_LENGTH = 11;@Pointcut("@annotation(com.t3.ts.driver.resume.aspect.EncryptMethod)")public void pointCut(){}/*** 注明切点*/@Around("pointCut()")public Object around(ProceedingJoinPoint joinPoint){Object responseObj = null;try {responseObj = joinPoint.proceed();//数据脱敏handleEncrypt(responseObj);} catch (Throwable throwable) {log.error("数据脱敏异常:{}", JSON.toJSONString(responseObj),throwable);}return responseObj;}/*** 处理加密** @param responseObj*/private void handleEncrypt(Object responseObj) throws IllegalAccessException {if (!Optional.ofNullable(responseObj).isPresent()) {return;}Object var = null;if(responseObj instanceof List){var = responseObj;}else{Response response = (Response) responseObj;var = response.getData();}if(!Optional.ofNullable(var).isPresent()){return;}this.dealDateByType(var);}/*** 类型判断处理* @param var* @throws IllegalAccessException*/private void dealDateByType(Object var) throws IllegalAccessException {Field[] fields = {};if(var instanceof PageResult){//分页列表数据PageResult pageResult = (PageResult) var;List list = pageResult.getList();List filterList = (List) list.stream().filter(x -> Optional.ofNullable(x).isPresent()).collect(Collectors.toList());for (Object o : filterList) {fields = FieldReflectionUtil.getAllFields(o.getClass());dealPrecisionField(fields,o);}}if(var instanceof List){List list = (List) var;List filterList = (List) list.stream().filter(x -> Optional.ofNullable(x).isPresent()).collect(Collectors.toList());for (Object o : filterList) {fields = FieldReflectionUtil.getAllFields(o.getClass());dealPrecisionField(fields,o);}}else{//详情页面等 --- 数据加密处理,需要前端配合解密展示fields = FieldReflectionUtil.getAllFields(var.getClass());dealEncryptField(fields,var);}}/*** 处理数据加密 前端配合解密处理* @param fields* @param var*/private void dealEncryptField(Field[] fields,Object var) throws IllegalAccessException {for (Field field : fields) {if(!Optional.ofNullable(field).isPresent() || !field.isAnnotationPresent(EncryptField.class)){continue;}if(!field.isAccessible()){field.setAccessible(true);}Object o = field.get(var);if(!Optional.ofNullable(o).isPresent()){continue;}if(!(o instanceof String)){//递归处理Field[] allFields = FieldReflectionUtil.getAllFields(o.getClass());this.dealEncryptField(allFields,o);}else{String value = encryptField((String) o);field.set(var,value);}}}/*** 字段加密处理* @return*/private String encryptField(String source){if(StringUtils.isEmpty(source)){return source;}String encryptValue = MD5Util.MD5Encode(source).toUpperCase();return encryptValue;}/*** 处理数据脱敏* @param fields* @param var* @throws IllegalAccessException*/private void dealPrecisionField(Field[] fields,Object var) throws IllegalAccessException {for (Field field : fields) {if(!Optional.ofNullable(field).isPresent() || !field.isAnnotationPresent(EncryptField.class)){continue;}if(!field.isAccessible()){field.setAccessible(true);}Object o = field.get(var);if(!Optional.ofNullable(o).isPresent()){continue;}if(!(o instanceof String) && !(o instanceof Integer)){//递归处理Field[] allFields = FieldReflectionUtil.getAllFields(o.getClass());this.dealPrecisionField(allFields,o);}else{Object value = null;if(o instanceof String){value = dealFieldValue(o);}if(o instanceof Integer){value = dealFieldValue( o);}field.set(var,value);}}}/*** 字段数据脱敏* @param obj* @return*/private Object dealFieldValue(Object obj){//integer类型枚举的直接返回if(obj instanceof Integer){return null;}String value = (String) obj;if(StringUtils.isEmpty(value)){return value;}if(value.length() == IDENTITY_CARD_LENGTH_18 || value.length() == IDENTITY_CARD_LENGTH_15){value = idCardReplace(value);}if(value.length() == MOBILE_LENGTH){value = mobileReplace(value);}if(value.length() <= OTHER_DESENSITIZATION_LENGTH){value = dealLessField(value);}return value;}private String dealLessField(String value){if(StringUtils.isEmpty(value) || value.length() > 3){return value;}StringBuilder builder = new StringBuilder();for (int i = 0; i < value.length(); i++) {builder.append("*");}return builder.toString();}/*** 身份证号脱敏,保留前四位和后四位* @param idCard 身份证号* @return*/public  String idCardReplace(String idCard) {if (StringUtils.isEmpty(idCard)) {return null;}return replaceAction(idCard, IDENTITY_CARD_DESENSITIZATION);}/*** 银行卡替换,保留后四位* @param bankCard 银行卡号* @return*/public  String bankCardReplace(String bankCard) {if (StringUtils.isEmpty(bankCard)) {return null;}return replaceAction(bankCard, BLANK_CARD_DESENSITIZATION);}/***手机号脱敏,保留前三后四位* @param mobile* @return*/public String mobileReplace(String mobile){if(StringUtils.isEmpty(mobile)){return mobile;}return replaceAction(mobile,MOBILE_DESENSITIZATION);}/*** 脱敏操作* @param source* @param regular  正则* @return*/private  String replaceAction(String source, String regular) {return source.replaceAll(regular, "*");}}

 

业务调用的方法:

public void export(ChargingSubsidiesReq chargingSubsidiesReq, HttpServletResponse servletResponse) {if (null == chargingSubsidiesReq) {chargingSubsidiesReq = new ChargingSubsidiesReq();}chargingSubsidiesReq.setPageSize(1);chargingSubsidiesReq.setCurrPage(1);ChargingSubsidiesReqDto reqDto = ObjectCheckUtil.createClass(ChargingSubsidiesReqDto.class);this.setCondition(chargingSubsidiesReq,reqDto);Response<PageResult<ChargingSubsidiesResDto>> response = chargingSubsidiesService.queryChargingSubsidies(reqDto);if (response.isSuccess() && Optional.ofNullable(response.getData()).isPresent()) {PageResult<ChargingSubsidiesResDto> pageResult = response.getData();ChargeSubsidyServiceImpl proxyObj = SpringContextUtil.getBean(ChargeSubsidyServiceImpl.class);List<ChargingSubsidiesResVo> voList = proxyObj.getExcelData(pageResult, reqDto);String excelTitle = StringUtils.isEmpty(chargingSubsidiesReq.getExcelTitle()) ? DriverEnum.DRIVER_CHARGE_SUBSIDIES_EXCEL_TITLE.getMsg() : chargingSubsidiesReq.getExcelTitle();List<String> headList = new ArrayList<>();CommonUtil.setHeadList(ChargingSubsidiesResVo.class, headList);ExcelUtil.downloadExcelFile(excelTitle, headList, voList, servletResponse);}}@EncryptMethodpublic List<ChargingSubsidiesResVo> getExcelData(PageResult pageResult,ChargingSubsidiesReqDto reqDto){ThreadPoolExecutor poolExecutor = BussinessThreadPool.getThreadPoolExecutor();int threadCount = CommonUtil.getThreadCount(pageResult);List<Future<List<ChargingSubsidiesResDto>>> futureList = new ArrayList<>();//多线程查询for (int i = 1; i <= threadCount; i++) {ChargingSubsidiesReqDto dto = ObjectCheckUtil.createClass(ChargingSubsidiesReqDto.class);BeanUtils.copyProperties(reqDto,dto);Future<List<ChargingSubsidiesResDto>> submit = poolExecutor.submit(new ChargingSubsidiesTask(i, ValidateConstant.EXCEL_EXPORT_DEFAULT_SIZE, dto, chargingSubsidiesService));futureList.add(submit);}List<ChargingSubsidiesResVo> voList = new ArrayList<>();List<ChargingSubsidiesResDto> tempList = new ArrayList<>();if (CollectionUtils.isNotEmpty(futureList)) {for (Future<List<ChargingSubsidiesResDto>> future : futureList) {try {List<ChargingSubsidiesResDto> dtoList = future.get();tempList.addAll(dtoList);} catch (InterruptedException | ExecutionException e) {future.cancel(true);log.error("获取线程数据异常{}:", e.getMessage(), e);}}tempList.stream().forEach(var -> {ChargingSubsidiesResVo vo = ObjectCheckUtil.createClass(ChargingSubsidiesResVo.class);BeanUtils.copyProperties(var, vo);vo.setIdentityCard(var.getIdNumber());vo.setMobile(var.getDriverMobile());voList.add(vo);});}return voList;}

 

该业务是使用多线程获取excel导出的数据,在再使用多线程填充excel,具体见我的另外一篇博客:

https://blog.csdn.net/qq_42151769/article/details/100674862

 

如果你在导出中使用:

this.getExcelData()的方法,那么不好意思,切面是无效的,原因是this是真实对象,不是一个代理对象,而aop切面必须是代理对象才能生效的,那么,我们就需要想办法获取到代理对象,因为spring IOC中存放的就是代理类对象,所以我们需要拿到它

如下: 注意别忘记了打上注解@Component

package com.t3.ts.driver.resume.context;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;import java.lang.reflect.Field;/*** @class_name: SpringContextUtil* @description: 获取spring ioc中的bean* @author: wm_yu* @create: 2019/09/12**/
@Component
public class SpringContextUtil implements ApplicationContextAware {private final static Logger log = LoggerFactory.getLogger(SpringContextUtil.class);private static ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContextParam) throws BeansException {applicationContext = applicationContextParam;}public static Object getObject(String id) {Object object = null;object = applicationContext.getBean(id);return object;}/*** 类路径获取* @param tClass* @return*/public static Object getBean(String tClass) {return applicationContext.getBean(tClass);}/*** 字节码对象获取* @param tClass* @param <T> 代理对象* @return*/public static <T> T getBean(Class<T> tClass) {return applicationContext.getBean(tClass);}/*** 根据传入获取真实对象* @param beanInstance* @return*/public static<T> T getTarget(T beanInstance) {if (!AopUtils.isAopProxy(beanInstance)) {return beanInstance;} else if (AopUtils.isCglibProxy(beanInstance)) {try {Field h = beanInstance.getClass().getDeclaredField("CGLIB$CALLBACK_0");h.setAccessible(true);Object dynamicAdvisedInterceptor = h.get(beanInstance);Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");advised.setAccessible(true);T target = (T)((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();return target;} catch (Exception e) {log.error("获取真实对象异常:{}",e.getMessage(),e);}}return null;}}

通过这个类,我们就能拿到代理对象了:

在代码中引用他:

ChargeSubsidyServiceImpl proxyObj = SpringContextUtil.getBean(ChargeSubsidyServiceImpl.class);List<ChargingSubsidiesResVo> voList = proxyObj.getExcelData(pageResult, reqDto);

 

我们可以debug调试下:

可以看到获取到了本类的代理对象,再看下用this调用也就是不获取代理对象的情况:

可以看到this是真实对象

这里看到的代理对象是cglib的,原因是我没有定义接口,所以spring使用的是cglib代理的

好了,完美解决了,aop切面失效的原因

 

 

 

 

 

 

 


http://chatgpt.dhexx.cn/article/6X7A7vRJ.shtml

相关文章

自定义切面类

直接贴入代码 &#xff0c;注释写的很清楚 此处为controller层 方法上加上自定义的切面注解 入参实体 public class TestM extends BaseModel{/****/private static final long serialVersionUID 1L; private String a;public String getA() {return a; }public void setA(S…

java 切面 注解_十、使用注解定义切面

一、本课目标 掌握使用注解实现AOP的方法 二、使用注解定义切面 2.1简介 AspectJ 面向切面的框架,它扩展了Java语言,定义了AOP语法,能够在编译期提供代码的织入。 @AspectJ AspectJ5新增的功能,使用JDK5.0注解技术和正规的AspectJ切点表达式语言描述切面(所以在使用@Aspect…

Spring 创建切面

目录 1、概述 2、切点类型 3、切面类型 4、静态普通方法名匹配切面 5、静态正则表达式方法匹配切面 6、动态切面 7、流程切面 8、复合切点切面 9、总结 1、概述 在前面介绍各类增强时&#xff0c;大家可能没有注意到一个问题&#xff1a;增强被织入到目标类的所有方法…

Spring基础:切面

前言在之前的文章中总结了 AOP 在 Spring 中的作用及地位&#xff0c;在分析 AOP 时涉及到切面的内容&#xff0c;这一节详细的分析切面的知识点。 正题 在开始文章前&#xff0c;有几个问题需要思考一下&#xff1a; 切面到底是神马切面的构成切面有哪些实现类切面有哪些类型…

java使用spring aop切面编程

aop概念 1、切面(Aspect) 首先要理解‘切’字&#xff0c;需要把对象想象成一个立方体&#xff0c;传统的面向对象变成思维&#xff0c;类定义完成之后&#xff08;封装&#xff09;。每次实例化一个对象&#xff0c;对类定义中的成员变量赋值&#xff0c;就相当于对这个立方体…

【Spring】面向切面编程详解(AOP)

文章目录 一、AOP概述什么是AOPAOP应用场景 二、AOP的基本术语术语介绍术语举例详解 三、AOP实例说明四、通知类型详解概述前置通知后置通知环绕通知最终通知 六、AOP实现声明式事务结语 &#x1f315;博客x主页&#xff1a;己不由心王道长&#x1f315;! &#x1f30e;文章说明…

时间序列预测方法

之前一直在看时间序列预测方法的文献&#xff0c;最近终于有点头绪了&#xff0c;然后就花了些时间来做一下整理。时间序列预测方法可分为传统的&#xff0c;基于机器学习的和基于深度学习的&#xff0c;以下每一方面都列举了几个比较经典的模型算法。 目录 1 背景 2 传统的时…

时间序列预处理

数据预处理的主要流程为&#xff1a;数据清洗、特征选择、归一化处理、划分窗口、Shuffle和划分数据集等五个阶段。选用何种方法没有统一的标准&#xff0c;只能根据不同类型的分析数据和业务需求&#xff0c;在对数据特性做了充分的理解之后&#xff0c;再选择与其最适配的数据…

时间序列分析相关概念

1. 在时间序列分析中&#xff0c; 数学模型是什么&#xff1f;数学公式又是什么&#xff1f;数学推导过程又是什么&#xff1f;… … 一句话&#xff1a;用数学公式后者符号来表示现实存在的意义。数学是“万金油”的科学&#xff0c;它是作为工作和分析方法运用到某个学科当中…

时间序列预测方法总结

前言 对时间序列数据预测模型做个简单的分类&#xff0c;方便日后对其进一步研究&#xff0c;理清楚技术更新发展方向。 时间序列预测分析就是利用过去一段时间内某事件时间的特征来预测未来一段时间内该事件的特征。 预测场景 单步预测 单步单变量预测 &#xff1a;在时间序…

时间序列的聚类方法

时间序列的聚类方法 时间序列是按照时间排序的一组随机变量&#xff0c;它通常是在相等间隔的时间段内&#xff0c;依照给定的采样率&#xff0c;对某种潜在过程进行观测的结果。 时间序列数据是实值型的序列数据&#xff0c;具有数据量大、数据维度高以及数据是不断更新的等…

【时间序列预测】Autoformer 长时间序列预测

论文链接&#xff1a;http://xxx.itp.ac.cn/pdf/2106.13008.pdf Abstract 延长预测时间是极端天气预警和长期能源消耗规划等实际应用的关键需求。本文研究时间序列的长期预测问题。先前的基于 Transformer 的模型采用各种 self-attention 机制来发现长期依赖关系。然而&…

时间序列分析预测

全栈工程师开发手册 &#xff08;作者&#xff1a;栾鹏&#xff09; python数据挖掘系列教程 时间序列中常用预测技术&#xff0c;一个时间序列是一组对于某一变量连续时间点或连续时段上的观测值。参考&#xff1a;https://blog.csdn.net/u010414589/article/details/49622625…

时间序列预测

问题简介 简单来说&#xff0c;时间序列是按照时间顺序&#xff0c;按照一定的时间间隔取得的一系列观测值&#xff0c;比如我们上边提到的国内生产总值&#xff0c;消费者物价指数&#xff0c;利率&#xff0c;汇率&#xff0c;股票价格等等。时间间隔可以是日&#xff0c;周…

时间序列分析

http://blog.csdn.net/pipisorry/article/details/62053938 时间序列简介 时间序列是时间间隔不变的情况下收集的时间点集合。这些集合被分析用来了解长期发展趋势&#xff0c;为了预测未来或者表现分析的其他形式。但是什么时间序列&#xff1f;与常见的回归问题的不同&…

时间序列模型调查

RNN 模型 Recurrent Neural Network (回流神经网络,有的译做递归神经网络) 时间序列模型最常用最强大的的工具就是递归神经网络(recurrent neural network, RNN)。相比与普通神经网络的各计算结果之间相互独立的特点,RNN的每一次隐含层的计算结果都与当前输入以及上一次的…

时间序列模型

1. 时间序列分析方法概述 一个时间序列往往是一下几类变化形式的叠加或耦合。 &#xff08;1&#xff09;长期趋势变动&#xff1a;反映主要变化趋势&#xff1b; &#xff08;2&#xff09;季节变动 &#xff08;3&#xff09;循环变动 &#xff08;4&#xff09;不规则变动 使…

python创建时间序列_python 时间序列

将dataframe的列转化为时间序列 #https://www.cnblogs.com/bolgbjg/p/14013300.html #datetime.now()获取现在的时间&#xff0c;now.year,获取现在的年份&#xff0c;其他以此类推 #timedelta()表示两个datetime之间的时间差,默认单位是天 strftime()参数&#xff0c; strftim…

时间序列(数据分析)

目录 第11章 时间序列 11.1 日期和时间数据的类型及工具 11.1.1 字符串与datetime互相转换 11.2 时间序列基础 11.2.1 索引、选择、子集 11.2.2 含有重复索引的时间序列 11.3 日期范围、频率和移位 11.3.1 生成日期范围 11.3.2 频率和日期偏置 11.3.3 移位&#xff0…

时间序列介绍

什么是时间序列&#xff1f; 时间序列是按照一定时间顺序&#xff0c;按照一定的时间间隔取得的一系列观测值。 怎样做时间序列分析&#xff1f; 时间序列分析尝试找出序列值在过去所呈现的特征模式&#xff0c;假定这种模式在未来能够持续&#xff0c;进而对未来进行预测 时…