-
spring切面注解失效
2019-09-12 17:31:06在项目中使用切面注解做数据脱敏时,导出的数据也需要脱敏处理,遇到了在一个类里面调用本类的方法切面失效,解决方法如下: 切面注解: package com.t3.ts.driver.resume.aspect; import java.lang.annotation.*; /...在项目中使用切面注解做数据脱敏时,导出的数据也需要脱敏处理,遇到了在一个类里面调用本类的方法切面失效,解决方法如下:
切面注解:
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); } } @EncryptMethod public 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; @Override public 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切面失效的原因
-
java 切面 注解_spring aop 之 切面注解配置@Jointcut
2021-02-25 18:44:19切面表达式executionwithinthistargetargs@target@within@agrs@annotation&&||!executionexecution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)throws-...切面表达式
execution
within
this
target
args
@target
@within
@agrs
@annotation
&&
||
!
execution
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)
?代表可以不存在
execution(public * *(..)) 任意方法
execution(* set*(..)) 任意以set开头的方法
execution(* com.xyz.service.AccountService.*(..)) AccountService类下的任意方法
execution(* com.xyz.service.*.*(..)) service包下的任意方法,不包含子包
execution(* com.xyz.service..*.*(..)) service包下的任意方法,包含子包
execution(* com.xyz.service.*.*()) service包下不带参数的方法
execution(* com.xyz.service.*.*(*,String)) service包下带两参数的方法
within
匹配类型
within(com.xyz.service.*) service包下的任意方法
within(com.xyz.service..*) service包及子包下的任意方法
this(com.xyz.service.AccountService) AccountService类下的任意方法
this
target
this,target与within类似,
args
args(java.io.Serializable)
@within
@target,@within差不多,都是类上有某个注解,就匹配任意方法
@within(org.springframework.transaction.annotation.Transactional)
@target(org.springframework.transaction.annotation.Transactional)
@annotation
方法上存在某个注解
@agrs
方法参数上存在某个注解
@After
如果要带参数
@AfterReturning(
pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
returning="retVal")
public void doAccessCheck(Object retVal) {
// ...
}
@AfterThrowing
@AfterThrowing(
pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
throwing="ex")
public void doRecoveryActions(DataAccessException ex) {
// ...
}
@Around
@Around("com.xyz.myapp.SystemArchitecture.businessService()")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
// start stopwatch
Object retVal = pjp.proceed();
// stop stopwatch
return retVal;
}
@Befor 如果要带参数
@Pointcut("com.xyz.myapp.SystemArchitecture.dataAccessOperation() && args(account,..)")
private void accountDataAccessOperation(Account account) {}
@Before("accountDataAccessOperation(account)")
public void validateAccount(Account account) {
// ...
}
@Before("com.xyz.lib.Pointcuts.anyPublicMethod() && @annotation(auditable)")
public void audit(Auditable auditable) {
AuditCode code = auditable.value();
// ...
}
@Before(value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)",
argNames="bean,auditable")
public void audit(JoinPoint jp, Object bean, Auditable auditable) {
AuditCode code = auditable.value();
// ... use code, bean, and jp
}
-
AOP AspectJ 切面注解中五种通知注解:
2019-05-20 18:05:54AOP AspectJ 切面注解中五种通知注解: @Before: 前置通知, 在方法执行之前执行 @After: 后置通知, 在方法执行之后执行 。 @AfterRunning: 返回通知, 在方法返回结果之后执行 @AfterThrowing: 异常通知, 在方法抛出... -
Springboot(二十一)@Aspect 切面注解使用
2018-09-08 17:36:11Spring AOP面向切面编程,可以用来配置事务、做日志、权限验证、在用户请求时...首先定义一个切面类,加上@Component @Aspect这两个注解 @Component @Aspect public class LogAspect { private static final Lo...Spring AOP面向切面编程,可以用来配置事务、做日志、权限验证、在用户请求时做一些处理等等。用@Aspect做一个切面,就可以直接实现。
1.首先定义一个切面类,加上@Component @Aspect这两个注解
@Component @Aspect public class LogAspect { private static final Logger logger = LoggerFactory.getLogger(LogAspect.class); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); }
2.定义切点
private final String POINT_CUT = "execution(public * com.xhx.springboot.controller.*.*(..))"; @Pointcut(POINT_CUT) public void pointCut(){}
切点表达式中,..两个点表明多个,*代表一个, 上面表达式代表切入com.xhx.springboot.controller包下的所有类的所有方法,方法参数不限,返回类型不限。 其中访问修饰符可以不写,不能用*,,第一个*代表返回类型不限,第二个*表示所有类,第三个*表示所有方法,..两个点表示方法里的参数不限。 然后用@Pointcut切点注解,想在一个空方法上面,一会儿在Advice通知中,直接调用这个空方法就行了,也可以把切点表达式卸载Advice通知中的,单独定义出来主要是为了好管理。
3.Advice,通知增强,主要包括五个注解Before,After,AfterReturning,AfterThrowing,Around,下面代码中关键地方都有注释,我都列出来了。
@Before 在切点方法之前执行
@After 在切点方法之后执行
@AfterReturning 切点方法返回后执行
@AfterThrowing 切点方法抛异常执行
@Around 属于环绕增强,能控制切点执行前,执行后,,用这个注解后,程序抛异常,会影响@AfterThrowing这个注解
package com.xhx.springboot.config; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.SourceLocation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.util.Arrays; @Component @Aspect public class LogAspect { private static final Logger logger = LoggerFactory.getLogger(LogAspect.class); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private final String POINT_CUT = "execution(public * com.xhx.springboot.controller.*.*(..))"; @Pointcut(POINT_CUT) public void pointCut(){} @Before(value = "pointCut()") public void before(JoinPoint joinPoint){ logger.info("@Before通知执行"); //获取目标方法参数信息 Object[] args = joinPoint.getArgs(); Arrays.stream(args).forEach(arg->{ // 大大 try { logger.info(OBJECT_MAPPER.writeValueAsString(arg)); } catch (JsonProcessingException e) { logger.info(arg.toString()); } }); //aop代理对象 Object aThis = joinPoint.getThis(); logger.info(aThis.toString()); //com.xhx.springboot.controller.HelloController@69fbbcdd //被代理对象 Object target = joinPoint.getTarget(); logger.info(target.toString()); //com.xhx.springboot.controller.HelloController@69fbbcdd //获取连接点的方法签名对象 Signature signature = joinPoint.getSignature(); logger.info(signature.toLongString()); //public java.lang.String com.xhx.springboot.controller.HelloController.getName(java.lang.String) logger.info(signature.toShortString()); //HelloController.getName(..) logger.info(signature.toString()); //String com.xhx.springboot.controller.HelloController.getName(String) //获取方法名 logger.info(signature.getName()); //getName //获取声明类型名 logger.info(signature.getDeclaringTypeName()); //com.xhx.springboot.controller.HelloController //获取声明类型 方法所在类的class对象 logger.info(signature.getDeclaringType().toString()); //class com.xhx.springboot.controller.HelloController //和getDeclaringTypeName()一样 logger.info(signature.getDeclaringType().getName());//com.xhx.springboot.controller.HelloController //连接点类型 String kind = joinPoint.getKind(); logger.info(kind);//method-execution //返回连接点方法所在类文件中的位置 打印报异常 SourceLocation sourceLocation = joinPoint.getSourceLocation(); logger.info(sourceLocation.toString()); //logger.info(sourceLocation.getFileName()); //logger.info(sourceLocation.getLine()+""); //logger.info(sourceLocation.getWithinType().toString()); //class com.xhx.springboot.controller.HelloController ///返回连接点静态部分 JoinPoint.StaticPart staticPart = joinPoint.getStaticPart(); logger.info(staticPart.toLongString()); //execution(public java.lang.String com.xhx.springboot.controller.HelloController.getName(java.lang.String)) //attributes可以获取request信息 session信息等 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); logger.info(request.getRequestURL().toString()); //http://127.0.0.1:8080/hello/getName logger.info(request.getRemoteAddr()); //127.0.0.1 logger.info(request.getMethod()); //GET logger.info("before通知执行结束"); } /** * 后置返回 * 如果第一个参数为JoinPoint,则第二个参数为返回值的信息 * 如果第一个参数不为JoinPoint,则第一个参数为returning中对应的参数 * returning:限定了只有目标方法返回值与通知方法参数类型匹配时才能执行后置返回通知,否则不执行, * 参数为Object类型将匹配任何目标返回值 */ @AfterReturning(value = POINT_CUT,returning = "result") public void doAfterReturningAdvice1(JoinPoint joinPoint,Object result){ logger.info("第一个后置返回通知的返回值:"+result); } @AfterReturning(value = POINT_CUT,returning = "result",argNames = "result") public void doAfterReturningAdvice2(String result){ logger.info("第二个后置返回通知的返回值:"+result); } //第一个后置返回通知的返回值:姓名是大大 //第二个后置返回通知的返回值:姓名是大大 //第一个后置返回通知的返回值:{name=小小, id=1} /** * 后置异常通知 * 定义一个名字,该名字用于匹配通知实现方法的一个参数名,当目标方法抛出异常返回后,将把目标方法抛出的异常传给通知方法; * throwing:限定了只有目标方法抛出的异常与通知方法相应参数异常类型时才能执行后置异常通知,否则不执行, * 对于throwing对应的通知方法参数为Throwable类型将匹配任何异常。 * @param joinPoint * @param exception */ @AfterThrowing(value = POINT_CUT,throwing = "exception") public void doAfterThrowingAdvice(JoinPoint joinPoint,Throwable exception){ logger.info(joinPoint.getSignature().getName()); if(exception instanceof NullPointerException){ logger.info("发生了空指针异常!!!!!"); } } @After(value = POINT_CUT) public void doAfterAdvice(JoinPoint joinPoint){ logger.info("后置通知执行了!"); } /** * 环绕通知: * 注意:Spring AOP的环绕通知会影响到AfterThrowing通知的运行,不要同时使用 * * 环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。 * 环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型 */ @Around(value = POINT_CUT) public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){ logger.info("@Around环绕通知:"+proceedingJoinPoint.getSignature().toString()); Object obj = null; try { obj = proceedingJoinPoint.proceed(); //可以加参数 logger.info(obj.toString()); } catch (Throwable throwable) { throwable.printStackTrace(); } logger.info("@Around环绕通知执行结束"); return obj; } }
执行前后顺序是这样
: @Around环绕通知 : @Before通知执行 : @Before通知执行结束 : @Around环绕通知执行结束 : @After后置通知执行了! : @AfterReturning第一个后置返回通知的返回值:18
org.aspectj.lang.JoinPoint : 方法中的参数JoinPoint为连接点对象,它可以获取当前切入的方法的参数、代理类等信息,因此可以记录一些信息,验证一些信息等。
org.aspectj.lang.ProceedingJoinPoint: 为JoinPoint的子类,多了两个方法
public Object proceed() throws Throwable; //调用下一个advice或者执行目标方法,返回值为目标方法返回值,因此可以通过更改返回值,修改方法的返回值
public Object proceed(Object[] args) throws Throwable; //参数为目标方法的参数 因此可以通过修改参数改变方法入参
可以用下面的方式获取request、session等对象
//attributes可以获取request信息 session信息等 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest();
下面介绍一下切点表达式:
1.execution(方法修饰符 返回类型 方法全限定名(参数)) 主要用来匹配整个方法签名和返回值的
"execution(public * com.xhx.springboot.controller.*.*(..))"
*只能匹配一级路径
..可以匹配多级,可以是包路径,也可以匹配多个参数
+ 只能放在类后面,表明本类及所有子类
还可以按下面这么玩,所有get开头的,第一个参数是Long类型的
@Pointcut("execution(* *..get*(Long,..))")
2. within(类路径) 用来限定类,同样可以使用匹配符
下面用来表示com.xhx.springboot包及其子包下的所有类方法
"within(com.xhx.springboot..*)"
3. this与target
this与target在用法上有些重合,理解上有对比性。
this表示当前切入点表达式所指代的方法的对象的实例,即代理对象是否满足this类型
target表示当前切入点表达式所指代的方法的目标对象的实例 即是否是为target类做的代理
如果当前对象生成的代理对象符合this指定的类型,则进行切面,target是匹配业务对象为指定类型的类,则进行切面。
生成代理对象时会有两种方法,一个是CGLIB一个是jdk动态代理。
用下面三个例子进行说明:
- this(SomeInterface)或target(SomeInterface):这种情况下,无论是对于Jdk代理还是Cglib代理,其目标对象和代理对象都是实现SomeInterface接口的(Cglib生成的目标对象的子类也是实现了SomeInterface接口的),因而this和target语义都是符合的,此时这两个表达式的效果一样;
- this(SomeObject)或target(SomeObject),这里SomeObject没实现任何接口:这种情况下,Spring会使用Cglib代理生成SomeObject的代理类对象,由于代理类是SomeObject的子类,子类的对象也是符合SomeObject类型的,因而this将会被匹配,而对于target,由于目标对象本身就是SomeObject类型,因而这两个表达式的效果一样;
- this(SomeObject)或target(SomeObject),这里SomeObject实现了某个接口:对于这种情况,虽然表达式中指定的是一种具体的对象类型,但由于其实现了某个接口,因而Spring默认会使用Jdk代理为其生成代理对象,Jdk代理生成的代理对象与目标对象实现的是同一个接口,但代理对象与目标对象还是不同的对象,由于代理对象不是SomeObject类型的,因而此时是不符合this语义的,而由于目标对象就是SomeObject类型,因而target语义是符合的,此时this和target的效果就产生了区别;这里如果强制Spring使用Cglib代理,因而生成的代理对象都是SomeObject子类的对象,其是SomeObject类型的,因而this和target的语义都符合,其效果就是一致的。
4.args(paramType)
args无论其类路径或者是方法名是什么,表达式的作用是匹配指定参数类型和指定参数数量的方法,类型用全路径
args(java.lang.String,..,java.lang.Integer)
5.@within(annotationType) 匹配带有指定注解的类,,within为配置指定类型的类实例
下面匹配含有 @Component注解的类
"@within(org.springframework.stereotype.Component)"
6.@annotation(annotationType) 匹配带有指定注解的方法
7.@args(annotationType)
@args表示使用指定注解标注的类作为某个方法的参数时该方法将会被匹配
可以使用&&、||、!、三种运算符来组合切点表达式,表示与或非的关系。
@Around(value = "pointcut1() || pointcut2()")
参考文献:
实时内容请关注微信公众号,公众号与博客同时更新:程序员星星
-
同时使用切面注解和RowMapper报错
2019-06-05 21:51:10同时使用切面注解和RowMapper报错 今天在复习spring持久化是报的一个错 大致代码如下: <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name=... -
Spring boot 利用切面注解自动切换多个数据源
2020-12-08 21:02:491、需要切面首先切面注解(DataSourceTypeAnno) 2、数据源枚举类(表示数据源的bean)(DataSourceEnum) 3、Aspect切面对象 (DataSourceAspect) 4、线程上下文对象,以保证线程间安全,不同线程使用不同数据源... -
AOP切面注解
2018-08-07 15:35:23一.前言 在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一下什么是aop具体怎么用,看到了一篇博文写得...AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Ori... -
sping AOP切面注解使用中的坑--同一个类中嵌套方法注解不生效
2019-11-19 11:20:15sping AOP切面注解使用中的坑–同一个类中嵌套方法注解不生效 在开发过程中,监控方法运行时间,然后获取程序运行的瓶颈,是一个常见的优化步骤。写一个spring切面实现的注解来实现函数运行... -
springboot中使用切面注解@Aspect进行日志统一打印
2020-01-12 10:08:32springboot中使用切面注解@Aspect进行日志统一打印引言具体操作功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容... -
java 切面 注解_Java自学之spring:使用注解进行面向切面编程(AOP)
2021-02-25 18:45:00学习目的:学会使用注解进行面向切面编程(AOP),实现在面向切面编程(AOP)中,使用XML配置完成的操作。Part 1修改cn.vaefun.dao.UserServiceImpl.java,在类上添加Component注解,告诉spring这是一个bean,并命名为... -
切面注解执行顺序
2019-01-14 09:59:43try { try { // @Before mothod.invoke(); } finally { // @After } // @AfterReturning catch() { // @AfterThrowing } ...另@Around中包含方法调用,可手动调节方法调用和切面代码逻辑... -
aop切面 注解、参数获取方法
2019-08-25 22:26:54在工作中会经常使用aop,这里将aop使用...1、定义需要切面的注解 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface AnnDemo { String value(); boolean isA... -
async异步注解和aspect切面注解等注解的原理
2019-09-02 18:28:49在我们使用spring框架的过程中,在很多时候我们会使用@async注解来异步执行一些方法,提高系统的执行效率。今天我们来探讨下spring是如何完成这个功能的。1、spring 在扫描bean的时候会扫描方法上是否包含@async的... -
JAVA实现简单的切面注解
2019-08-22 00:31:07背景 今天在分析同事遇到一个springboot的注解和方法锁一起用而导致的问题(@...本文介绍java中其中一种(InvocationHandler)利用动态代理的方式实现的代理的方法,从而类似的机制我们推测出spring的切面... -
使用切面注解编程实现redis模糊删除数据之二使用spel表达式
2018-06-07 12:19:36传送门:使用切面注解编程实现redis模糊删除数据之一之前虽然用切面编程实现了redis的模糊查询,但是却没有实现像spring-redis原生的语法一样的spEl表达式,比如#参数名这种形式,然后在网上找了一些资料,实现了和... -
Spring 中aop切面注解实现
2019-03-04 21:40:00spring中aop的注解实现方式简单实例 上篇中我们讲到spring的xml实现,这里我们讲讲使用注解如何实现aop呢。前面已经讲过aop的简单...在切面类(为切点服务的类)前用@Aspect注释修饰,声明为一个切面类。 ... -
spring如何在自己实现的切面注解中使用${}获取properties配置文件中的参数值?
2019-12-03 10:13:10自己实现建了一个切面注解@Mypoint 注解有一个key属性 ``` String key(); ``` 在使用注解时 ``` @Mypoint(key = "abc-${env}") ``` 在properties配置文件中有 ``` env=dev ``` 这样通过配置文件可以... -
spring boot中使用切面注解
2019-09-06 20:38:06完成一个spring boot切面对参数加密解密的...引入依赖: 切面依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId&g... -
AspectJ 切面注解中五种通知注解:@Before、@After、@AfterRunning、@AfterThrowing、@Around
2017-12-16 23:52:27要在 Spring 中声明 AspectJ 切面, 只需要在 IOC 容器中将切面声明为 Bean 实例....在切面类中需要定义切面方法用于响应响应的目标方法,切面方法即为通知方法,通知方法需要用注解标识,AspectJ 支持 5 种类型的 -
Spring框架中使用AOP实现自定义重试切面注解功能
2020-08-03 14:03:462.@Aspect切面 三、切入AOP流程实现切面逻辑 1.Advice切面处理类 2.Pointcut切面切入点 3.Advisor类 4.Advisor可插拔式通过@Bean注入到Spring工厂 4.1 可插拔式配置类 4.2 可插拔式注解开关 4.3 注入到... -
Java-切面注解拦截器
2020-05-08 16:59:16创建切面类,启用注解@Aspect、@Component、@Order(-99);通过@Pointcut("@annotation(具体注解类路径)")指定注解切入点;最后通过环绕通知(@Around(“pointCut()”))实现拦截效果,在此方法里进行判断,最终通过... -
定义切面注解的方式来记录操作日志信息
2020-05-29 16:28:51故我们可以定义切面,来进行处理日志的记录;简化开发; 下面的案例是自定义注解来记入日志;可以根据自己的需求来改变自己的切面实现和连接点, 下面只是提供一个案例参考。 一、注解SaveLog @Target({ElementType.... -
spring aop 之 切面注解配置@Jointcut
2019-09-11 21:37:49切面表达式 execution within this target args @target @within @agrs @annotation && || ! execution execution(modifiers-pattern? ret-type-pattern d... -
SpringBoot项目 Spring AOP切面注解实现
2019-03-30 01:58:46//切面拦截器,拦截订单接口请求中是否能查询到token(类似SpringAOP) @Pointcut("execution(public * com.imooc.controller.Seller*.*(..))" + "&& !execution(public * ... -
Spring Aop切面注解 解决@annotation()无法切入方法内部调用的问题
2020-05-21 10:32:07本来我是使用@annotation() 直接切面指定注解的,结果方法内部调用的方法无法切入,因此我使用了一下方式替代。切面Action下所有类所有方法,同时方法上有ExecuteTask注解的才进行切面 @Component @Aspect public ... -
Spring AOP 切面 注解,拦截方法失效
2019-02-26 21:30:07问题 使用 aop 环绕拦截对应的方法,远程断点发现没有拦截到。 解决方法 发现拦截的方法并非直接从 controller -&... service 直接调用过来,而是在 service 层通过其他方法调用的,无法被拦截到;...
-
DVTk-Storage-SCP-Emulator/DVTk-RIS-Emulator-5.0.0
-
php数组索引的Key加引号和不加引号的区别
-
使用keras_bert调用bert的简单方法
-
SpringBoot集成SpringSession
-
2021-3-1博士工作日记
-
【硬核】一线Python程序员实战经验分享(1)
-
重大节日、去世人员页面变灰操作
-
LVS + Keepalived 实现 MySQL 负载均衡与高可用
-
中央广播电视大学《高等数学》期末总复习资料(含答案).pdf
-
基于Flink+Hudi构建企业亿级云上实时数据湖教程(PC、移动、小
-
Galera 高可用 MySQL 集群(PXC v5.7+Hapro)
-
git conflict
-
朱老师鸿蒙系列课程第1期-3.鸿蒙系统Harmonyos源码配置和管理
-
中央广播电视大学《公共行政学》期末总复习资料(含答案).pdf
-
vue3.0 打包后空白显示
-
Mycat 实现 MySQL的分库分表、读写分离、主从切换
-
中央广播电视大学《个人与团队管理-专科》期末总复习资料(含答案).pdf
-
自动化测试Python3+Selenium3+Unittest
-
中央广播电视大学《公共部门人力资源管理》期末总复习资料(含答案).pdf
-
ELF视频教程