目录
一、前言
Spring AOP是一种基于方法的AOP,只能用在方法上,在业务上我们一般使用Spring AOP去约定编程一套业务逻辑织入到相关的业务处理中,并抽取通用逻辑默认加入到相关业务中(前置处理后置业务处理或异常处理等),同时在应用上在想对一些现成业务进行干预处理时都会使用(比方说可以考虑对特定方法进行mock操作等处理)。Spring AOP和我们平时开发中使用的约定编程基本类似,本质都是通过约定对相应的方法通过动态代理技术织入约定流程中。本次一些基本的理念还是直接通过自己看过的一些书籍和博客进行总结了,实际应用上基本是结合自己的开发经验进行了一些基本的应用分享,如果有理解错误的地方请留言指正,谢谢!
二、Spring AOP基本理论知识
(一)基本术语总结
术语 | 基本概念 |
连接点(join point) | 具体被拦截的对象,往往指的就是特定的方法,也可以指某个包路径下的所有类下的所有方法(进行mock干预经常这样干)或直接就是某个类下面的所有方法 |
切点(point cut) | 切面作用的方法不只是单一的,可以通过正则和指示器的规则去定义,适配连接点 |
通知(advice) |
按照约定的流程方法,根据约定织入流程中。 分类:前置通知、后置通知、环绕通知、事后反悔通知、异常通知 |
目标对象(target) | 即被代理对象,也就是上面连接点(join point)中的指定的类 |
引入(introduction) | 增加现有Bean的功能,引入新的类和方法 |
织入(weaving) | 通过动态代理,为原对象生成代理对象,然后与切点定义匹配的连接点拦截,按约定将各类通知织入约定流程中 |
切面(aspect) | Spring AOP通过切面信息来增加Bean的功能或将对应方法织入流程,在切面中定义切点、各类 通知和引入内容 |
(二)具体开发注意事项
有相关疑问的可以留言直接问,以下内容,只是我平时觉得重点的应用注意事项总结:
- ProceedingJoinPoint,继承自连接点JoinPoint,实际作用上就是我们指定的具体某个类或某些类,本质上连接点就是什么类的什么方法。
- 具体切面采用@Aspect注解标注,切面中定义我们想要约定实现的基本内容,以及相关的通知@Before、@After、@AfterReturning、@AfterThrowing、@Around,各通知中需要统一指明切点,一般我们都是直接定义切点@Pointcut,然后直接在各通知中进行引入即可。
- @Around是我们业务干预中的重点流程改造,一般主要干预处理的内容都在该通知中实现,处理上务必重点分析处理逻辑,代码上做兜底是必须操作。
- @DeclareParents业务处理中针对老逻辑进行干预并增加功能时,可以考虑采用引入方式处理,即通过该注解指明两个主要参数value和defaultImpl,value指明要增强的目标对象,defaultImpl指引入增强功能的类,具体案例肩=见相关文档。
- 非环绕通知中对于参数的处理直接使用JoinPoint(当然也可以不用),例如前置打印参数等内容,环绕通知则使用ProceedingJoinPoint,在使用上需要注意。
- 如果多个切面作用的类和方法是一样的,则切面的执行顺序是无序的,如果切面之间需要有先后顺序的的话,需要使用@Order注解或实现Ordered接口实现,数字越小优先级越高,实际处理上如果既有前置通知又有后置通知,则可以看到前置从小到大执行,后置从大到小执行,这说明其内部是一个典型的责任链模式(其应用具体见:)。
(三)切点正则和指示器规则
平时开发时对于切点的定义较多,如果已经很明确处理的切点的话则不需要进行复杂正则配置,但是如果是多类多方法时则正则是必须的。一般我们常用的AspectJ的指示器主要如下表:
AspectJ指示器 | 描述 |
arg() | 限制连接点匹配参数为制定类型的方法 |
@args() | 限制而连接点匹配参数为指定注解标注的只是方法 |
execution | 用于匹配连接点的执行方法 |
this() | 限制连接点匹配AOP代理的Bean,引用为指定类型的类 |
target | 限制连接点匹配被代理对象为指定的类型 |
@target | 限制连接点匹配特定的执行对象,这些对象要符合指定的注解类型 |
@within() | 限制连接点匹配指定的类型 |
within() | 限制连接点匹配指定的包 |
@annotation | 限定匹配带有指定注解的连接点 |
基本使用举例如下
联合使用切点指示器
/*匹配任意public访问修饰符修饰的方法*/
@Pointcut("execution(public * (..))")
private void anyPublicOperation() {}
/*匹配包名以com.zyf.javabasic.trading开头的任意类的所有方法*/
@Pointcut("within(com.zyf.javabasic.trading..)")
private void inTrading() {}
/*匹配以com.zyf.javabasic.trading开头的任意类中任意以public访问修饰符修饰的方法*/
@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation() {}
共享切点定义
/*匹配定义在web层且目标对象在com.zyf.javabasic.web包及其子包中的所有类的任意方法*/
@Pointcut("within(com.zyf.javabasic.web..)")
public void inWebLayer() {}
/*匹配定义在service层且目标对象在com.zyf.javabasic.service包及其子包中所有类中的任意方法*/
@Pointcut("within(com.zyf.javabasic.service..)")
public void inServiceLayer() {}
/*匹配定义在数据访问层且目标对象在com.zyf.javabasic.dao包及其子包中所有类中的任意方法*/
@Pointcut("within(com.zyf.javabasic.dao..)")
public void inDataAccessLayer() {}
/*匹配数据库访问层中目标对象在com.zyf.javabasic.dao..(..)及其子包中的任意方法*/
@Pointcut("execution( com.zyf.javabasic.dao..(..))")
public void dataAccessOperation() {}
切点表达式解读与使用示例
/*匹配任意公共方法*/
execution(public * *(..))
/*匹配任意方法名以set字符开头的方法*/
execution(* set*(..))
/*匹配com.xyz.service.AccountService类下任意方法*/
execution(* com.xyz.service.AccountService.*(..))
/*匹配com.zyf.service包及其子包下任意类的所有方法*/
execution(* com.zyf.service.*.*(..))
/*匹配com.zyf.service包下任意接口的所有方法*/
within(com.zyf.service.*)
/*匹配com.zyf.service包及其子包下任意类的所有方法*/
within(com.zyf.service..*)
/*匹配实现AccountService接口的代理的任意方法*/
this(com.zyf.service.AccountService)
/*匹配实现AccountService接口的目标对象的任意方法*/
target(com.zyf.service.AccountService)
/*匹配只带一个参数的方法,且该参数为可序列化参数*/
args(java.io.Serializable)
/*匹配具有Transactional的目标对象任意方法*/
@target(org.springframework.transaction.annotation.Transactional)
/*匹配目标对象声名有Transactional的方法*/
@within(org.springframework.transaction.annotation.Transactional)
/*匹配带有个参数的方法,且运行时参数类型绑定有Classified注解*/
@args(com.zyf.security.Classified)
/*匹配Spring容器中id或name属性值为tradeService的bean实例的方法*/
bean(tradeService)
/*匹配Spring容器中id或name属性值以Service结尾的bean实例的方法*/
bean(*Service)
三、AOP开发应用与分析
(一)方法自动打印出入参举例
场景分析
平时的开发中有些特定的接口为了方便后续定位问题,往往需要打印方法的出入参数以及本次处理所消耗的时间信息等内容,在该背景下,我们可以增加切面对于指定的方法进行处理前的参数打印以及返回前的参数结果打印的处理,同时可以去指定机器或环境来打印等基本控制手段,具体流程图如下。
方法日志打印注解
package org.zyf.javabasic.aop.printlog;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author yanfengzhang
* @description 方法日志打印信息处理
* @date 2022/6/13 23:54
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PrintLogInfo {
/**
* 自定义日志描述信息文案
*/
String description() default "";
}
切面定义
package org.zyf.javabasic.aop.printlog;
import com.alibaba.fastjson.JSON;
import com.sankuai.inf.octo.mns.model.HostEnv;
import com.sankuai.inf.octo.mns.util.ProcessInfoUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.zyf.javabasic.config.SwitchCommonConfig;
import java.lang.reflect.Method;
import java.util.Objects;
/**
* @author yanfengzhang
* @description
* @date 2022/6/15 23:02
*/
@Slf4j
@Aspect
@Component
public class PrintLogInfoAspet {
StopWatch watch = new StopWatch();
/**
* 方法以 @PrintLogInfo 注解作为切面入口
*/
@Pointcut("@annotation(org.zyf.javabasic.aop.printlog.PrintLogInfo)")
public void printBasicLog() {
}
/**
* 方法入参前打印对应的入参信息内容
*
* @param joinPoint 切入点信息
* @throws Throwable 异常信息
*/
@Before("printBasicLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method targetMethod = methodSignature.getMethod();
/*1.判断是否需要开启打印日志功能*/
if (!isPrintLog()) {
return;
}
/*2.从方法上提取当前配置的注解信息*/
PrintLogInfo printLogInfo = targetMethod.getAnnotation(PrintLogInfo.class);
String description = printLogInfo.description();
if (StringUtils.isBlank(description)) {
description = "未表明归属";
}
/*3.打印相关入参参数*/
StringBuilder methodInfo = new StringBuilder();
methodInfo.append(methodSignature.getDeclaringTypeName()).append(".").append(targetMethod.getName());
watch.start(methodInfo.toString());
StringBuilder logRequestInfo = new StringBuilder();
logRequestInfo.append("\n").append("请求方法:").append(methodInfo).append("\n");
logRequestInfo.append("方法日志描述信息:").append(description).append("\n");
logRequestInfo.append("入参信息:").append(JSON.toJSONString(joinPoint.getArgs()));
log.info(logRequestInfo.toString());
}
/**
* 方法返回之前执行,打印才返回值以及方法消耗时间
*
* @param response 返回值
*/
@AfterReturning(returning = "response", pointcut = "printBasicLog()")
public void doAfterRunning(Object response) {
/*1.判断是否需要开启打印日志功能*/
if (!isPrintLog()) {
return;
}
/*2.从方法上提取当前配置的注解信息*/
StringBuilder logResponseInfo = new StringBuilder();
logResponseInfo.append("\n").append("出参信息:").append(JSON.toJSONString(response)).append("\n");
logResponseInfo.append("本次方法执行时间耗时:").append(watch.getTotalTimeMillis()).append("ms");
log.info(logResponseInfo.toString());
}
/**
* 日志打印生效环境和机器提取提取,用于测试指定环境和机器来定位问题
*
* @return false-默认不进行打印
*/
private boolean isPrintLog() {
/*1.全部全部统一生效或失效开关*/
if (SwitchCommonConfig.openPrintLog()) {
return true;
}
/*2.按生效条件分析是否打印*/
HostEnv hostEnv = ProcessInfoUtil.getHostEnv();
String localIpV4 = ProcessInfoUtil.getLocalIpV4();
/*2.1没有配置默认不打印相关数据*/
if (Objects.isNull(hostEnv) && StringUtils.isBlank(localIpV4)) {
return false;
}
/*2.2如果配置了指定机器打印的话,优先对机器执行打印*/
if (SwitchCommonConfig.getIpsForPrintLog().contains(localIpV4)) {
return true;
}
/*2.3当机器没有生效的时候,看生效环境*/
if (SwitchCommonConfig.getEnvsForPrintLog().contains(hostEnv)) {
return true;
}
return false;
}
}
业务应用
基本数据准备
package org.zyf.javabasic.aop.printlog;
import lombok.Builder;
import lombok.Data;
/**
* @author yanfengzhang
* @description
* @date 2022/7/13 23:56
*/
@Data
@Builder
public class ZyfInfoRequest {
/**
* 基本信息
*/
private boolean needBasicInfo;
/**
* 博客信息
*/
private boolean needBlogInfo;
}
package org.zyf.javabasic.aop.printlog;
import lombok.Builder;
import lombok.Data;
import java.util.List;
/**
* @author yanfengzhang
* @description
* @date 2022/7/13 23:56
*/
@Data
@Builder
public class ZyfInfoResponse {
/**
* 姓名
*/
private String name;
/**
* 年纪
*/
private int age;
/**
* 身高:cm
*/
private float height;
/**
* 体重:kg
*/
private float weight;
/**
* 博客信息展示
*/
private List<String> blogShowcases;
}
具体服务
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author yanfengzhang
* @description
* @date 2022/7/13 23:55
*/
@Service
@Slf4j
public class ZyfInfoService {
@PrintLogInfo(description = "获取张彦峰的相关信息")
public ZyfInfoResponse getZyfInfo(ZyfInfoRequest zyfInfoRequest) {
if (zyfInfoRequest.isNeedBasicInfo() && !zyfInfoRequest.isNeedBlogInfo()) {
return ZyfInfoResponse.builder()
.name("张彦峰")
.age(29)
.height(185)
.weight(83).build();
}
List<String> blogShowcases = Stream.of("https://blog.csdn.net/xiaofeng10330111/article/details/106086200",
"https://blog.csdn.net/xiaofeng10330111/article/details/106086035").collect(Collectors.toList());
log.info("获取张彦峰的相关信息业务处理完成");
return ZyfInfoResponse.builder()
.name("张彦峰")
.age(29)
.height(185)
.weight(83)
.blogShowcases(blogShowcases).build();
}
}
测试结果
基本测试代码
package org.zyf.javabasic.aop.printlog;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.zyf.javabasic.ZYFApplication;
/**
* @author yanfengzhang
* @description
* @date 2022/7/13 23:49
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ZYFApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Slf4j
public class ZyfInfoServiceTest {
@Autowired
private ZyfInfoService zyfInfoService;
@Test
public void test() {
zyfInfoService.getZyfInfo(ZyfInfoRequest.builder().needBlogInfo(true).needBasicInfo(true).build());
}
}
测试结果展示
(二)mock赋能干预举例
场景分析
假设为了更好的分析某服务的一些接口处理和迭代变动,我们需要对其内部很多thrift接口进行干预处理。我们可以牵起将相关的thrift接口统计出来,然后将其中的各接口进行一套模版的配置用于控制分析处理,在配置上针对各个接口可以定义其基本的一些策略:
- 透传策略-该接口按实际调用方式触发实际内容;
- 异常策略-该接口调用时直接抛出异常即可;
- mock策略-将具体结果按我们定义的内容进行返回处理;
在实际处理上收敛各个接口从而分析变动点以及不影响实际其他业务等,具体流程图如下。
该方式内部也可以使用切面处理,处于考虑,我们也可以通过过滤器方式来完成,本次按该方式代码如下(个人觉得该场景就应该使用过滤器实现即可),后期按切面处理。
相关代码展示
package org.zyf.javabasic.aop.mock;
import com.alibaba.fastjson.JSON;
import com.dianping.lion.Environment;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Lists;
import com.zyf.horse.common.RpcRole;
import com.zyf.horse.rpc.handler.filter.Filter;
import com.zyf.horse.rpc.handler.filter.FilterHandler;
import com.zyf.horse.rpc.meta.RpcInvocation;
import com.zyf.horse.rpc.meta.RpcResult;
import com.zyf.service.mobile.mtthrift.client.pool.MTThriftPoolConfig;
import com.zyf.service.mobile.mtthrift.proxy.ThriftClientProxy;
import com.zyf.product.bsap.thrift.domain.common.ProductBsapThirftException;
import com.zyf.product.bsap.thrift.domain.mock.MockRuleDto;
import com.zyf.product.bsap.thrift.iface.MockRuleThriftService;
import com.zyf.tsp.product.bsap.enums.MockRuleType;
import com.zyf.tsp.product.bsap.mcc.BsapCommonSwitch;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.thrift.TException;
import org.springframework.stereotype.Component;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
/**
* @author yanfengzhang
* @description mock组件对指定接口按指定内容进行干预
* @date 2021/12/28 23:33
*/
@Log4j2
@Component
public class MockRemoteThriftClientFilter implements Filter {
private static MockRuleThriftService.Iface mockRuleThriftService = null;
private static final String MOCK_CLIENT_APPKEY = BsapCommonSwitch.getMockClientApp();
private static final String MOCK_SERVER_APPKEY = "com.zyf.product.bsap";
private static volatile Set<String> rpcInfos = new HashSet<>();
static {
MockRemoteThriftClientFilter.init(MOCK_CLIENT_APPKEY);
}
private static LoadingCache<String, List<MockRuleDto>> mockRuleCache = CacheBuilder.newBuilder()
/*初始化加载一次,后续每隔十分钟刷新缓存数据*/
.refreshAfterWrite(BsapCommonSwitch.getMockRuleReflushTime(), TimeUnit.MINUTES)
/*构建缓存*/
.build(new CacheLoader<String, List<MockRuleDto>>() {
/*初始化加载数据的缓存信息*/
@Override
public List<MockRuleDto> load(String str) throws TException, ProductBsapThirftException {
Long total = 60L;
List<MockRuleDto> mockRuleDtos = Lists.newArrayList();
Long validMockRuleCount = mockRuleThriftService.getValidCount();
if (validMockRuleCount < total) {
/*总接口数较少时直接全量拉取*/
return mockRuleThriftService.getAllMockRules();
}
/*数据量大时进行分次拉取*/
for (long i = 0; i <= validMockRuleCount; ) {
mockRuleDtos.addAll(mockRuleThriftService.getMockRulesByRange(i, i + total));
i = i + total + 1;
}
return mockRuleDtos;
}
});
/**
* 初始化thrift客户端
*/
public static void init(String appKey) {
MTThriftPoolConfig mtThriftPoolConfig = new MTThriftPoolConfig();
mtThriftPoolConfig.setMaxActive(5);
mtThriftPoolConfig.setMaxIdle(2);
mtThriftPoolConfig.setMinIdle(1);
mtThriftPoolConfig.setMaxWait(3000L);
mtThriftPoolConfig.setTestOnBorrow(false);
ThriftClientProxy mockClientProxy = new ThriftClientProxy();
mockClientProxy.setMtThriftPoolConfig(mtThriftPoolConfig);
mockClientProxy.setAppKey(appKey);
mockClientProxy.setRemoteAppkey(MOCK_SERVER_APPKEY);
mockClientProxy.setTimeout(5000);
mockClientProxy.setServiceInterface(MockRuleThriftService.class);
mockClientProxy.setFilterByServiceName(true);
try {
mockClientProxy.afterPropertiesSet();
mockRuleThriftService = (MockRuleThriftService.Iface) mockClientProxy.getObject();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static List<MockRuleDto> loadData(String str) throws TException, ProductBsapThirftException {
Long total = 60L;
List<MockRuleDto> mockRuleDtos = Lists.newArrayList();
Long validMockRuleCount = mockRuleThriftService.getValidCount();
if (validMockRuleCount < total) {
/*总接口数较少时直接全量拉取*/
return mockRuleThriftService.getAllMockRules();
}
/*数据量大时进行分次拉取*/
for (long i = 0; i <= validMockRuleCount; ) {
mockRuleDtos.addAll(mockRuleThriftService.getMockRulesByRange(i, i + total));
i = i + total + 1;
}
return mockRuleDtos;
}
/**
* 将此过滤进行优先处理,优先级至高
*
* @return 过滤器优先级优先级
*/
@Override
public int getPriority() {
return 1000;
}
/**
* 本sdk作为本身的调用服务进行处理
*
* @return
*/
@Override
public RpcRole getRole() {
return RpcRole.INVOKER;
}
/**
* 实时拦截干预处理
*
* @param rpcInvocation rpc调用消息
* @param filterHandler 过滤处理
* @return 过滤处理结果
* @throws Throwable 异常消息
*/
@Override
public RpcResult filter(RpcInvocation rpcInvocation, FilterHandler filterHandler) throws Throwable {
final String MOCK_RULE_DATA = "mock_data";
/*1.特定service不进行干预*/
if (BsapCommonSwitch.getCommonRpcAppKeyList().contains(rpcInvocation.getServiceInterface().getName())) {
return filterHandler.handle(rpcInvocation);
}
/*特定服务进行记录相关内容*/
if (BsapCommonSwitch.getMockRpcInfo() && Environment.getAppName().equals(BsapCommonSwitch.getMockClientApp())) {
rpcInfos.add(rpcInvocation.getServiceInterface().getName() + ":" + rpcInvocation.getMethod().getName());
FileWriteUtils.writeToFile(rpcInfos.toString(), "/var/zyf/rpcInfo.txt");
}
/*2.无mock规则直接按原方式进行处理*/
List<MockRuleDto> mockRuleDtos = mockRuleCache.getUnchecked(MOCK_RULE_DATA);
if (CollectionUtils.isEmpty(mockRuleDtos)) {
/*全新修改0516:如果不在mock配置中都返回默认数据干预*/
return defaultDealResult(rpcInvocation);
}
/*3.查看当前客户端调用接口是否在mock规则的配置中*/
MockRuleDto mockRuleDto = mockRuleDtos.stream()
.filter(mockRuleDtoInfo -> mockRuleDtoInfo.getInterfaceInfo().equals(rpcInvocation.getServiceInterface().getName())
&& mockRuleDtoInfo.getMethodName().equals(rpcInvocation.getMethod().getName()))
.findAny().orElse(null);
/*4.如果对应接口和方法并不是指定的mock数据,直接按原方式进行处理(后续改成不在配置中的直接抛错)*/
if (null == mockRuleDto) {
/*全新修改0516:如果不在mock配置中都返回默认数据干预*/
return defaultDealResult(rpcInvocation);
}
/*5.查看当前的mock规则,如果是透传则直接进行处理*/
int isMock = mockRuleDto.getIsMock();
if (MockRuleType.PENETRATE.getType() == isMock) {
/*如果是透传,则直接按原方式进行处理*/
return filterHandler.handle(rpcInvocation);
}
/*6.如果是拒绝类型则直接抛出异常*/
if (MockRuleType.EWJECT.getType() == isMock) {
/*如果是拒绝,则直接按异常返回*/
throw new Exception("当前rpc接口mock规则为拒绝策略,不允许远程调用!");
}
/*7.需要进行mock的,按当前的处理方式进行mock数据的返回*/
/*返回的具体类*/
String resultTypeName = rpcInvocation.getMethod().getAnnotatedReturnType().getType().getTypeName();
if (resultTypeName.equals("void") && mockRuleDto.getResult().equals("void")) {
return new RpcResult(true);
}
if (mockRuleDto.getResult().equals("true") && (resultTypeName.equals("boolean") || resultTypeName.equals("java.lang.Boolean"))) {
return new RpcResult(true);
}
if (isNumeric(mockRuleDto.getResult()) && (resultTypeName.equals("int") || resultTypeName.equals("java.lang.Integer"))) {
return new RpcResult(Integer.valueOf(mockRuleDto.getResult()));
}
if (isNumeric(mockRuleDto.getResult()) && (resultTypeName.equals("long") || resultTypeName.equals("java.lang.Long"))) {
return new RpcResult(Long.valueOf(mockRuleDto.getResult()));
}
if (isDoubleOrFloat(mockRuleDto.getResult()) && (resultTypeName.equals("double") || resultTypeName.equals("java.lang.Double"))) {
return new RpcResult(Double.valueOf(mockRuleDto.getResult()));
}
if (isDoubleOrFloat(mockRuleDto.getResult()) && (resultTypeName.equals("float") || resultTypeName.equals("java.lang.Float"))) {
return new RpcResult(Float.valueOf(mockRuleDto.getResult()));
}
Object rpcResult = JSON.parseObject(mockRuleDto.getResult(), rpcInvocation.getMethod().getReturnType());
return new RpcResult(rpcResult);
}
private boolean isNumeric(String str) {
if (StringUtils.isBlank(str)) {
return false;
}
for (int i = str.length(); --i >= 0; ) {
int chr = str.charAt(i);
if (chr < 48 || chr > 57) {
return false;
}
}
return true;
}
/**
* 是否为浮点数?double或float类型
*
* @param str 传入的字符串
* @return 是浮点数返回true, 否则返回false
*/
public static boolean isDoubleOrFloat(String str) {
Pattern pattern = Pattern.compile("^[-\\+]?[.\\d]*$");
return pattern.matcher(str).matches();
}
/**
* 没有mock的直接按默认的来处理
*
* @param rpcInvocation
* @return
*/
private RpcResult defaultDealResult(RpcInvocation rpcInvocation) {
String resultTypeName = rpcInvocation.getMethod().getAnnotatedReturnType().getType().getTypeName();
if (resultTypeName.equals("void")) {
return new RpcResult(true);
}
if (resultTypeName.equals("boolean") || resultTypeName.equals("java.lang.Boolean")) {
return new RpcResult(true);
}
if (resultTypeName.equals("int") || resultTypeName.equals("java.lang.Integer")) {
return new RpcResult(0);
}
if (resultTypeName.equals("long") || resultTypeName.equals("java.lang.Long")) {
return new RpcResult(0L);
}
if (resultTypeName.equals("double") || resultTypeName.equals("java.lang.Double")) {
return new RpcResult(0D);
}
if (resultTypeName.equals("float") || resultTypeName.equals("java.lang.Float")) {
return new RpcResult(0F);
}
return null;
}
}
(三)场景业务处理举例
场景分析
业务场景上假设我们可以创建很多活动类型,每个类型对应的其特定的定义与结构但是又有很多自己的共性,所有的活动基本都有固有的CRUD和上下线逻辑等,此时我们也可以通过业务类型等内容来统一转发实现,具体代码如下,该处为了让你们感受下业务复杂性写了很多有的没的,看不懂可以留言,当前只为了可以切面符合业务逻辑而写的。
方法日志打印注解
package org.zyf.javabasic.aop.bizdeal.annotation;
import org.zyf.javabasic.aop.bizdeal.constants.ActivityBizMethod;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author yanfengzhang
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ZYFActivityDealer {
/**
* 活动的具体类型
*/
String activityType() default "";
/**
* 活动触发的业务方法
*/
ActivityBizMethod activityMethod() default ActivityBizMethod.DEFAULT;
}
切面定义
package org.zyf.javabasic.aop.bizdeal.aspect;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.zyf.javabasic.aop.bizdeal.annotation.ZYFActivityDealer;
import org.zyf.javabasic.aop.bizdeal.constants.ActivityBizConstants;
import org.zyf.javabasic.aop.bizdeal.constants.ActivityBizMethod;
import org.zyf.javabasic.aop.bizdeal.factory.ActivityServiceStrategyFactory;
import org.zyf.javabasic.aop.bizdeal.service.activity.ActivityService;
import org.zyf.javabasic.common.ActivityBizException;
import java.lang.reflect.Method;
/**
* @author yanfengzhang
* @description 活动业务对应的切面处理类
* @date 2020/10/30 23:06
*/
@Component
@Aspect
@Slf4j
public class ActivityBizAspect {
@Pointcut("@annotation(org.zyf.javabasic.aop.bizdeal.annotation.ZYFActivityDealer)")
public void execute() {
}
// @Before("execute()")
// public void printLog(JoinPoint point) {
// System.out.println("的地方v官方大肆宣传" + point.getArgs());
// log.info("hhhh{}", point.getArgs());
// }
@Around("execute()")
public Object around(ProceedingJoinPoint point) throws Throwable {
log.info("AOP处理开始:");
Signature signature = point.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method targetMethod = methodSignature.getMethod();
ZYFActivityDealer zyfActivityDealer = targetMethod.getAnnotation(ZYFActivityDealer.class);
if (null != zyfActivityDealer) {
log.info("活动业务切面生效!");
/*业务上存在对应注解,则需要进入活动对应的业务处理*/
return handleActivityBizMethod(point, zyfActivityDealer);
}
log.info("AOP处理结束!当前按原方式返回!");
return point.proceed();
}
/**
* @param point 指定不变,不可更改
* @param zyfActivityDealer 对应的新建注解
* @return 具体业务结果
* @throws Throwable 相关异常
* @description 处理对应的活动业务处理方法
*/
private Object handleActivityBizMethod(ProceedingJoinPoint point, ZYFActivityDealer zyfActivityDealer) throws Throwable {
log.info("AOP操作:zyfActivityDealer={}", zyfActivityDealer);
/*1.获取当前指定的活动类型:降价活动、限时活动、买赠活动、首购优惠活动、自动续费活动、折上优惠活动*/
String activityType = zyfActivityDealer.activityType();
if (StringUtils.isBlank(activityType)) {
throw new ActivityBizException("活动类型异常:获取活动类型activityType失败, 请检查指定是否正确!", ActivityBizConstants.ActivityBizCode.ANNO_EMPTY_BIZTYPE_ERR);
}
/*2.按照活动类型返回需要提供服务的活动类型*/
ActivityService activityService = ActivityServiceStrategyFactory.getActivityBytype(activityType);
if (null == activityService) {
throw new ActivityBizException("活动类型异常: 获取handler失败,请联系管理员查看!", ActivityBizConstants.ActivityBizCode.NO_HANDLER_FOUND);
}
/*3.确定对应活动需要执行的业务方法*/
ActivityBizMethod activityBizMethod = zyfActivityDealer.activityMethod();
String handleMethodName = activityBizMethod.getMethodName();
if (StringUtils.isBlank(handleMethodName)) {
throw new ActivityBizException("活动业务执行方法异常: 获取对应活动业务方法失败,请联系管理员查看!", ActivityBizConstants.ActivityBizCode.HANDLER_NO_VALID_METHOD_ERR);
}
/*4.根据对应活动类型进行反射获取对应的执行的业务方法*/
Method activityBizMethodForHandler = null;
try {
activityBizMethodForHandler = activityService.getClass().getMethod(handleMethodName, Object[].class);
} catch (NoSuchMethodException e) {
throw new ActivityBizException("活动业务执行方法异常: 获取handler指定method失败,请联系管理员查看!", ActivityBizConstants.ActivityBizCode.HANDLER_NO_VALID_METHOD_ERR);
}
/*5.按照指定活动和对应业务方法执行对应的方法*/
try {
Object[] args = point.getArgs();
Object result = activityBizMethodForHandler.invoke(activityService, new Object[]{args});
if (activityBizMethodForHandler.getReturnType().equals(Void.TYPE) && activityBizMethod.doBizMethodAfterHandler()) {
/*返回类型为void的话,则直接看对应业务要求是否还处理原逻辑*/
return point.proceed();
} else if (null == result || activityBizMethod.doBizMethodAfterHandler()) {
/*有返回类型但为空的话,则直接看对应业务要求是否还处理原逻辑*/
return point.proceed();
} else {
return result;
}
} catch (Exception e) {
log.error("处理活动AOP切面存在异常: ", e);
throw new ActivityBizException("服务端异常:副本逻辑异常,请联系管理员查看!", ActivityBizConstants.ActivityBizCode.HANDLER_BIZ_ERR);
}
}
}
业务应用
基本数据准备
活动基本接口
package org.zyf.javabasic.aop.bizdeal.service.activity;
/**
* 活动基本接口
*
* @author yanfengzhang
*/
public interface ActivityService<T> {
/**
* 查询对应活动详细信息
*
* @param args 被拦截方法的参数
* @return 基于活动详细信息构造的业务返回值
*/
T queryActivityDetail(Object[] args);
/**
* 创建或者更新活动详细信息
*
* @param args 被拦截方法的参数
* @return 基于活动详细信息构造的业务返回值
*/
T createOrUpdateActivityDetail(Object[] args);
/**
* 将活动详细信息置为删除-----软删除
*
* @param args 被拦截方法的参数
*/
void deleteActivityDetail(Object[] args);
/**
* 检查活动详细信息
*
* @param args 被拦截方法的参数
*/
boolean checkActivityInfo(Object[] args);
/**
* 活动详细信息上线
*
* @param args 被拦截方法的参数
*/
void onlineActivity(Object[] args);
/**
* 活动详细信息下线
*
* @param args 被拦截方法的参数
*/
void offlineActivity(Object[] args);
}
自动续费活动相关操作
package org.zyf.javabasic.aop.bizdeal.service.activity.impl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import org.zyf.javabasic.aop.bizdeal.constants.ActivityBizConstants;
import org.zyf.javabasic.aop.bizdeal.entity.ActivityDo;
import org.zyf.javabasic.aop.bizdeal.entity.dto.AutoRenewalActivityDto;
import org.zyf.javabasic.aop.bizdeal.factory.ActivityServiceStrategyFactory;
import org.zyf.javabasic.aop.bizdeal.service.activity.ActivityService;
/**
* @author yanfengzhang
* @description 自动续费活动相关方法整理
* @date 2020/11/2 12:13
*/
@Component
@Slf4j
public class AutoRenewalActivityServiceImpl implements ActivityService<AutoRenewalActivityDto>, InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
ActivityServiceStrategyFactory.register(ActivityBizConstants.ActivityBizType.AUTO_RENEMAL, this);
}
@Override
public AutoRenewalActivityDto queryActivityDetail(Object[] args) {
/*按传入信息查当前数据库对应活动信息 只是测试*/
ActivityDo activityDo = new ActivityDo();
activityDo.setActivityName("数据库中的活动信息");
/*转换为对应活动信息*/
AutoRenewalActivityDto autoRenewalActivityDto = new AutoRenewalActivityDto();
autoRenewalActivityDto.setActivityType(5);
autoRenewalActivityDto.setActivityName("自动续费活动");
log.info("查询自动续费活动:{}",autoRenewalActivityDto);
return autoRenewalActivityDto;
}
@Override
public AutoRenewalActivityDto createOrUpdateActivityDetail(Object[] args) {
/*按传入信息更新或创建当前数据库对应活动信息 只是测试*/
ActivityDo activityDo = new ActivityDo();
activityDo.setActivityName("数据库中的活动信息");
/*转换为对应活动信息*/
AutoRenewalActivityDto autoRenewalActivityDto = new AutoRenewalActivityDto();
autoRenewalActivityDto.setActivityType(5);
autoRenewalActivityDto.setActivityName("自动续费活动");
log.info("创建或更新自动续费活动:{}",autoRenewalActivityDto);
return autoRenewalActivityDto;
}
@Override
public void deleteActivityDetail(Object[] args) {
log.info("删除自动续费活动");
}
@Override
public boolean checkActivityInfo(Object[] args) {
log.info("校验自动续费活动信息是否有误");
return false;
}
@Override
public void onlineActivity(Object[] args) {
log.info("上线自动续费活动");
}
@Override
public void offlineActivity(Object[] args) {
log.info("下线自动续费活动");
}
public String test() {
return "sss";
}
}
买赠活动相关操作整理
package org.zyf.javabasic.aop.bizdeal.service.activity.impl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import org.zyf.javabasic.aop.bizdeal.constants.ActivityBizConstants;
import org.zyf.javabasic.aop.bizdeal.entity.ActivityDo;
import org.zyf.javabasic.aop.bizdeal.entity.dto.BuyForGetActivityDto;
import org.zyf.javabasic.aop.bizdeal.factory.ActivityServiceStrategyFactory;
import org.zyf.javabasic.aop.bizdeal.service.activity.ActivityService;
/**
* @author yanfengzhang
* @description 买赠活动相关操作
* @date 2020/11/2 12:08
*/
@Component
@Slf4j
public class BuyForGetActivityServiceImpl implements ActivityService<BuyForGetActivityDto>, InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
ActivityServiceStrategyFactory.register(ActivityBizConstants.ActivityBizType.BUY_FOR_GET, this);
}
@Override
public BuyForGetActivityDto queryActivityDetail(Object[] args) {
/*按传入信息更新或创建当前数据库对应活动信息 只是测试*/
ActivityDo activityDo = new ActivityDo();
activityDo.setActivityName("数据库中的活动信息");
/*转换为对应活动信息*/
BuyForGetActivityDto buyForGetActivityDto = new BuyForGetActivityDto();
buyForGetActivityDto.setActivityType(3);
buyForGetActivityDto.setActivityName("买赠活动");
log.info("查询买赠活动:{}",buyForGetActivityDto);
return buyForGetActivityDto;
}
@Override
public BuyForGetActivityDto createOrUpdateActivityDetail(Object[] args) {
/*按传入信息查当前数据库对应活动信息 只是测试*/
ActivityDo activityDo = new ActivityDo();
activityDo.setActivityName("数据库中的活动信息");
/*转换为对应活动信息*/
BuyForGetActivityDto buyForGetActivityDto = new BuyForGetActivityDto();
buyForGetActivityDto.setActivityType(3);
buyForGetActivityDto.setActivityName("买赠活动");
log.info("创建或更新买赠活动:{}",buyForGetActivityDto);
return buyForGetActivityDto;
}
@Override
public void deleteActivityDetail(Object[] args) {
log.info("删除买赠活动");
}
@Override
public boolean checkActivityInfo(Object[] args) {
log.info("校验买赠活动信息是否有误");
return false;
}
@Override
public void onlineActivity(Object[] args) {
log.info("上线买赠活动");
}
@Override
public void offlineActivity(Object[] args) {
log.info("下线买赠活动");
}
}
折上优惠活动相关操作
package org.zyf.javabasic.aop.bizdeal.service.activity.impl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import org.zyf.javabasic.aop.bizdeal.constants.ActivityBizConstants;
import org.zyf.javabasic.aop.bizdeal.entity.ActivityDo;
import org.zyf.javabasic.aop.bizdeal.entity.dto.DiscountActivityDto;
import org.zyf.javabasic.aop.bizdeal.factory.ActivityServiceStrategyFactory;
import org.zyf.javabasic.aop.bizdeal.service.activity.ActivityService;
/**
* @author yanfengzhang
* @description 折上优惠活动相关方法整理
* @date 2020/11/2 12:14
*/
@Component
@Slf4j
public class DiscountActivityServiceImpl implements ActivityService<DiscountActivityDto>, InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
ActivityServiceStrategyFactory.register(ActivityBizConstants.ActivityBizType.DISCOUNT, this);
}
@Override
public DiscountActivityDto queryActivityDetail(Object[] args) {
/*按传入信息查当前数据库对应活动信息 只是测试*/
ActivityDo activityDo = new ActivityDo();
activityDo.setActivityName("数据库中的活动信息");
/*转换为对应活动信息*/
DiscountActivityDto discountActivityDto = new DiscountActivityDto();
discountActivityDto.setActivityType(6);
discountActivityDto.setActivityName("折上优惠活动");
log.info("查询折上优惠活动:{}",discountActivityDto);
return discountActivityDto;
}
@Override
public DiscountActivityDto createOrUpdateActivityDetail(Object[] args) {
/*按传入信息更新或创建当前数据库对应活动信息 只是测试*/
ActivityDo activityDo = new ActivityDo();
activityDo.setActivityName("数据库中的活动信息");
/*转换为对应活动信息*/
DiscountActivityDto discountActivityDto = new DiscountActivityDto();
discountActivityDto.setActivityType(6);
discountActivityDto.setActivityName("折上优惠活动");
log.info("创建或更新折上优惠活动:{}",discountActivityDto);
return discountActivityDto;
}
@Override
public void deleteActivityDetail(Object[] args) {
log.info("删除折上优惠活动");
}
@Override
public boolean checkActivityInfo(Object[] args) {
log.info("校验折上优惠活动信息是否有误");
return false;
}
@Override
public void onlineActivity(Object[] args) {
log.info("上线折上优惠活动");
}
@Override
public void offlineActivity(Object[] args) {
log.info("下线折上优惠活动");
}
}
首购优惠活动相关操作
package org.zyf.javabasic.aop.bizdeal.service.activity.impl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import org.zyf.javabasic.aop.bizdeal.constants.ActivityBizConstants;
import org.zyf.javabasic.aop.bizdeal.entity.ActivityDo;
import org.zyf.javabasic.aop.bizdeal.entity.dto.FirstPurchaseActivityDto;
import org.zyf.javabasic.aop.bizdeal.factory.ActivityServiceStrategyFactory;
import org.zyf.javabasic.aop.bizdeal.service.activity.ActivityService;
/**
* @author yanfengzhang
* @description 首购优惠活动相关操作
* @date 2020/11/2 12:10
*/
@Component
@Slf4j
public class FirstPurchaseActivityServiceImpl implements ActivityService<FirstPurchaseActivityDto>, InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
ActivityServiceStrategyFactory.register(ActivityBizConstants.ActivityBizType.FIRST_PURCHASE, this);
}
@Override
public FirstPurchaseActivityDto queryActivityDetail(Object[] args) {
/*按传入信息查当前数据库对应活动信息 只是测试*/
ActivityDo activityDo = new ActivityDo();
activityDo.setActivityName("数据库中的活动信息");
/*转换为对应活动信息*/
FirstPurchaseActivityDto firstPurchaseActivityDto = new FirstPurchaseActivityDto();
firstPurchaseActivityDto.setActivityType(4);
firstPurchaseActivityDto.setActivityName("首购优惠活动");
log.info("查询首购优惠活动:{}",firstPurchaseActivityDto);
return firstPurchaseActivityDto;
}
@Override
public FirstPurchaseActivityDto createOrUpdateActivityDetail(Object[] args) {
/*按传入信息创建或更新当前数据库对应活动信息 只是测试*/
ActivityDo activityDo = new ActivityDo();
activityDo.setActivityName("数据库中的活动信息");
/*转换为对应活动信息*/
FirstPurchaseActivityDto firstPurchaseActivityDto = new FirstPurchaseActivityDto();
firstPurchaseActivityDto.setActivityType(4);
firstPurchaseActivityDto.setActivityName("首购优惠活动");
log.info("创建或更新首购优惠活动:{}",firstPurchaseActivityDto);
return firstPurchaseActivityDto;
}
@Override
public void deleteActivityDetail(Object[] args) {
log.info("删除首购优惠活动");
}
@Override
public boolean checkActivityInfo(Object[] args) {
log.info("校验首购优惠活动信息是否有误");
return false;
}
@Override
public void onlineActivity(Object[] args) {
log.info("上线首购优惠活动");
}
@Override
public void offlineActivity(Object[] args) {
log.info("下线首购优惠活动");
}
}
限时活动相关操作
package org.zyf.javabasic.aop.bizdeal.service.activity.impl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import org.zyf.javabasic.aop.bizdeal.constants.ActivityBizConstants;
import org.zyf.javabasic.aop.bizdeal.entity.ActivityDo;
import org.zyf.javabasic.aop.bizdeal.entity.dto.LimitTimeActivityDto;
import org.zyf.javabasic.aop.bizdeal.factory.ActivityServiceStrategyFactory;
import org.zyf.javabasic.aop.bizdeal.service.activity.ActivityService;
/**
* @author yanfengzhang
* @description 限时活动相关操作
* @date 2020/11/2 12:06
*/
@Component
@Slf4j
public class LimitTimeActivityServiceImpl implements ActivityService<LimitTimeActivityDto>, InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
ActivityServiceStrategyFactory.register(ActivityBizConstants.ActivityBizType.LIMIT_TIME, this);
}
@Override
public LimitTimeActivityDto queryActivityDetail(Object[] args) {
/*按传入信息查当前数据库对应活动信息 只是测试*/
ActivityDo activityDo = new ActivityDo();
activityDo.setActivityName("数据库中的活动信息");
/*转换为对应活动信息*/
LimitTimeActivityDto limitTimeActivityDto = new LimitTimeActivityDto();
limitTimeActivityDto.setActivityType(2);
limitTimeActivityDto.setActivityName("限时活动");
log.info("查询限时活动:{}",limitTimeActivityDto);
return limitTimeActivityDto;
}
@Override
public LimitTimeActivityDto createOrUpdateActivityDetail(Object[] args) {
/*按传入信息创建或更新当前数据库对应活动信息 只是测试*/
ActivityDo activityDo = new ActivityDo();
activityDo.setActivityName("数据库中的活动信息");
/*转换为对应活动信息*/
LimitTimeActivityDto limitTimeActivityDto = new LimitTimeActivityDto();
limitTimeActivityDto.setActivityType(2);
limitTimeActivityDto.setActivityName("限时活动");
log.info("创建或更新限时活动:{}",limitTimeActivityDto);
return limitTimeActivityDto;
}
@Override
public void deleteActivityDetail(Object[] args) {
log.info("删除限时活动");
}
@Override
public boolean checkActivityInfo(Object[] args) {
log.info("校验限时活动信息是否有误");
return false;
}
@Override
public void onlineActivity(Object[] args) {
log.info("上线限时活动");
}
@Override
public void offlineActivity(Object[] args) {
log.info("下线限时活动");
}
}
具体服务
package org.zyf.javabasic.aop.bizdeal.service.verify;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.zyf.javabasic.aop.bizdeal.annotation.ZYFActivityDealer;
import org.zyf.javabasic.aop.bizdeal.constants.ActivityBizConstants;
import org.zyf.javabasic.aop.bizdeal.constants.ActivityBizMethod;
import org.zyf.javabasic.aop.bizdeal.entity.dto.AutoRenewalActivityDto;
import org.zyf.javabasic.aop.bizdeal.entity.dto.BuyForGetActivityDto;
import org.zyf.javabasic.aop.bizdeal.entity.dto.DiscountActivityDto;
import org.zyf.javabasic.aop.bizdeal.entity.dto.FirstPurchaseActivityDto;
import org.zyf.javabasic.aop.bizdeal.entity.dto.LimitTimeActivityDto;
import org.zyf.javabasic.aop.bizdeal.entity.dto.PriceCutActivityDto;
/**
* @author yanfengzhang
* @description 各活动AOP实现操作汇总
* @date 2020/11/10 23:19
*/
@Component
@Slf4j
public class VerifyAopService {
/**
* 降价活动的相关AOP操作
*/
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.PRICE_CUT, activityMethod = ActivityBizMethod.QUERY)
public PriceCutActivityDto queryPriceCutActivityDetail(Long id) {
PriceCutActivityDto priceCutActivityDto = PriceCutActivityDto.builder().activityType(1).activityName("降价活动").build();
priceCutActivityDto.setActivityName("降价活动---AOP失效");
return priceCutActivityDto;
}
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.PRICE_CUT, activityMethod = ActivityBizMethod.CREATE_OR_UPDATE)
public void addOrUpdatePriceCutActivityDetail(PriceCutActivityDto priceCutActivityDto) {
log.info("addOrUpdatePriceCutActivityDetail:若打印本日志,则说明对应增加或更新AOP未按切面进行或AOP失效,按照本方法的实际内容操作!");
return;
}
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.PRICE_CUT, activityMethod = ActivityBizMethod.DELETE)
public boolean deletePriceCutActivityDetail(Long id) {
return true;
}
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.PRICE_CUT, activityMethod = ActivityBizMethod.ONLINE)
public void onlinePriceCutActivityDetail(Long id) {
return;
}
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.PRICE_CUT, activityMethod = ActivityBizMethod.OFFLINE)
public void offlinePriceCutActivityDetail(Long id) {
return;
}
/**
* 首购优惠活动相关AOP实现
*/
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.FIRST_PURCHASE, activityMethod = ActivityBizMethod.QUERY)
public FirstPurchaseActivityDto queryFirstPurchaseActivityDetail(Long id) {
FirstPurchaseActivityDto firstPurchaseActivityDto = new FirstPurchaseActivityDto();
firstPurchaseActivityDto.setActivityName("首购优惠活动---AOP失效");
return firstPurchaseActivityDto;
}
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.FIRST_PURCHASE, activityMethod = ActivityBizMethod.CREATE_OR_UPDATE)
public void addOrUpdateFirstPurchaseActivityDetail(FirstPurchaseActivityDto firstPurchaseActivityDto) {
log.info("addOrUpdateFirstPurchaseActivityDetail:若打印本日志,则说明对应增加或更新AOP未按切面进行或AOP失效,按照本方法的实际内容操作!");
return;
}
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.FIRST_PURCHASE, activityMethod = ActivityBizMethod.DELETE)
public void deleteFirstPurchaseActivityDetail(Long id) {
return;
}
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.FIRST_PURCHASE, activityMethod = ActivityBizMethod.ONLINE)
public void onlineFirstPurchaseActivityDetail(Long id) {
return;
}
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.FIRST_PURCHASE, activityMethod = ActivityBizMethod.OFFLINE)
public void offlineFirstPurchaseActivityDetail(Long id) {
return;
}
/**
* 限时活动相关AOP实现
*/
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.LIMIT_TIME, activityMethod = ActivityBizMethod.QUERY)
public LimitTimeActivityDto queryLimitTimeActivityDetail(Long id) {
LimitTimeActivityDto limitTimeActivityDto = new LimitTimeActivityDto();
limitTimeActivityDto.setActivityName("限时活动---AOP失效");
return limitTimeActivityDto;
}
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.LIMIT_TIME, activityMethod = ActivityBizMethod.CREATE_OR_UPDATE)
public void addOrUpdateLimitTimeActivityDetail(LimitTimeActivityDto limitTimeActivityDto) {
log.info("addOrUpdateLimitTimeActivityDetail:若打印本日志,则说明对应增加或更新AOP未按切面进行或AOP失效,按照本方法的实际内容操作!");
return;
}
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.LIMIT_TIME, activityMethod = ActivityBizMethod.DELETE)
public void deleteLimitTimeActivityDetail(Long id) {
return;
}
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.LIMIT_TIME, activityMethod = ActivityBizMethod.ONLINE)
public void onlineLimitTimeActivityDetail(Long id) {
return;
}
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.LIMIT_TIME, activityMethod = ActivityBizMethod.OFFLINE)
public void offlineLimitTimeActivityDetail(Long id) {
return;
}
/**
* 折上优惠活动相关AOP实现
*/
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.DISCOUNT, activityMethod = ActivityBizMethod.QUERY)
public DiscountActivityDto queryDiscountActivityDetail(Long id) {
DiscountActivityDto discountActivityDto = new DiscountActivityDto();
discountActivityDto.setActivityName("折上优惠活动---AOP失效");
return discountActivityDto;
}
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.DISCOUNT, activityMethod = ActivityBizMethod.CREATE_OR_UPDATE)
public void addOrUpdateDiscountActivityDetail(DiscountActivityDto discountActivityDto) {
log.info("addOrUpdateDiscountActivityDetail:若打印本日志,则说明对应增加或更新AOP未按切面进行或AOP失效,按照本方法的实际内容操作!");
return;
}
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.DISCOUNT, activityMethod = ActivityBizMethod.DELETE)
public void deleteDiscountActivityDetail(Long id) {
return;
}
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.DISCOUNT, activityMethod = ActivityBizMethod.ONLINE)
public void onlineDiscountActivityDetail(Long id) {
return;
}
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.DISCOUNT, activityMethod = ActivityBizMethod.OFFLINE)
public void offlineDiscountActivityDetail(Long id) {
return;
}
/**
* 买赠活动相关AOP实现
*/
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.BUY_FOR_GET, activityMethod = ActivityBizMethod.QUERY)
public BuyForGetActivityDto queryBuyForGetActivityDetail(Long id) {
BuyForGetActivityDto buyForGetActivityDto = new BuyForGetActivityDto();
buyForGetActivityDto.setActivityName("买赠活动---AOP失效");
return buyForGetActivityDto;
}
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.BUY_FOR_GET, activityMethod = ActivityBizMethod.CREATE_OR_UPDATE)
public void addOrUpdateBuyForGetActivityDetail(BuyForGetActivityDto buyForGetActivityDto) {
log.info("addOrUpdateBuyForGetActivityDetail:若打印本日志,则说明对应增加或更新AOP未按切面进行或AOP失效,按照本方法的实际内容操作!");
return;
}
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.BUY_FOR_GET, activityMethod = ActivityBizMethod.DELETE)
public void deleteBuyForGetActivityDetail(Long id) {
return;
}
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.BUY_FOR_GET, activityMethod = ActivityBizMethod.ONLINE)
public void onlineBuyForGetActivityDetail(Long id) {
return;
}
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.BUY_FOR_GET, activityMethod = ActivityBizMethod.OFFLINE)
public void offlineBuyForGetActivityDetail(Long id) {
return;
}
/**
* 自动续费活动相关AOP实现
*/
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.AUTO_RENEMAL, activityMethod = ActivityBizMethod.QUERY)
public AutoRenewalActivityDto queryAutoRenewalActivityDetail(Long id) {
AutoRenewalActivityDto autoRenewalActivityDto = new AutoRenewalActivityDto();
autoRenewalActivityDto.setActivityName("自动续费活动---AOP失效");
return autoRenewalActivityDto;
}
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.AUTO_RENEMAL, activityMethod = ActivityBizMethod.CREATE_OR_UPDATE)
public void addOrUpdateAutoRenewalActivityDetail(AutoRenewalActivityDto autoRenewalActivityDto) {
log.info("addOrUpdateAutoRenewalActivityDetail:若打印本日志,则说明对应增加或更新AOP未按切面进行或AOP失效,按照本方法的实际内容操作!");
return;
}
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.AUTO_RENEMAL, activityMethod = ActivityBizMethod.DELETE)
public void deleteAutoRenewalActivityDetail(Long id) {
return;
}
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.AUTO_RENEMAL, activityMethod = ActivityBizMethod.ONLINE)
public void onlineAutoRenewalActivityDetail(Long id) {
return;
}
@ZYFActivityDealer(activityType = ActivityBizConstants.ActivityBizType.AUTO_RENEMAL, activityMethod = ActivityBizMethod.OFFLINE)
public void offlineAutoRenewalActivityDetail(Long id) {
return;
}
}
测试结果
测试代码
package org.zyf.javabasic.aop.bizdeal;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.zyf.javabasic.ZYFApplication;
import org.zyf.javabasic.aop.bizdeal.entity.dto.PriceCutActivityDto;
import org.zyf.javabasic.aop.bizdeal.service.verify.VerifyAopService;
import javax.annotation.Resource;
/**
* @author yanfengzhang
* @description
* @date 2022/4/8 23:38
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ZYFApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Slf4j
public class AopComplexTest {
@Resource
private VerifyAopService verifyAopService;
@Test
public void testPriceCutActivityDetail() {
log.info("======测试降价活动删除AOP情况======");
verifyAopService.deletePriceCutActivityDetail(null);
log.info("======测试降价活动新增AOP情况======");
verifyAopService.addOrUpdatePriceCutActivityDetail(PriceCutActivityDto.builder().build());
}
}
结果展示
(四)异步业务处理举例
场景分析
可以根据业务类型只要业务存储入库后异步操作保存数据到reidis,这种异步场景也可以使用切面去处理,相关操作如下图,具体代码上如果有疑问直接留言吧。
方法日志打印注解
package org.zyf.javabasic.aop.redissync;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author yanfengzhang
* @description 异步同步redis注解:将需要异步处理的内容放置到该信息内容上
* --主要针对相关基本信息的增删改,批量的暂时先不考虑
* @date 2022/7/13 23:10
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface OperationRedisSync {
/**
* 需要异步处理的信息操作:新增1 修改2 删除3
*/
int dealInfoType() default 0;
/**
* 需要异步处理的业务归属:0-敏感词业务;1-超范围资质业务;3-会员渠道业务;4-红包发布业务
*/
int dealBizType() default 0;
/**
* 需要异步处理的信息名称
*/
String dealInfo() default "";
/**
* 将要被删除的id
*/
String deleteId() default "id";
}
切面定义
package org.zyf.javabasic.aop.redissync;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.CodeSignature;
import org.springframework.stereotype.Component;
import org.zyf.javabasic.designpatterns.responsibility.pipeline.SensitiveWord;
import java.util.Objects;
/**
* @author yanfengzhang
* @description 异步同步redis同步切面操作
* @date 2022/7/13 23:23
*/
@Component
@Aspect
@Slf4j
public class OperationRedisAspect {
@Pointcut("@annotation(org.zyf.javabasic.aop.redissync.OperationRedisSync) && @annotation(operationRedisSync)")
private void operationRedisSyncDeal(OperationRedisSync operationRedisSync) {
}
@AfterReturning(pointcut = "operationRedisSyncDeal(operationRedisSync)", argNames = "joinPoint, operationRedisSync")
public void operationRedisSyncDealer(JoinPoint joinPoint, OperationRedisSync operationRedisSync) {
/*1.如果要求指明的异步操作没有处理的话直接返回*/
int dealInfoType = operationRedisSync.dealInfoType();
int dealBizType = operationRedisSync.dealBizType();
if (!OperationRedisSyncType.isOperationRedisSyncType(dealInfoType) || !OperationRedisSyncBiz.isOperationRedisSyncBiz(dealBizType)) {
return;
}
/*2.需要异步处理的信息名称没有指定的不进行相关业务操作*/
int index = 0;
if (dealInfoType == OperationRedisSyncType.DELETE.getType()) {
index = OperationRedisAspectUtils.fetchParamIndex((CodeSignature) joinPoint.getSignature(), operationRedisSync.deleteId());
} else {
index = OperationRedisAspectUtils.fetchParamIndex((CodeSignature) joinPoint.getSignature(), operationRedisSync.dealInfo());
}
if (index < 0) {
return;
}
/*3.根据dealBizType进行数据适配转换到业务侧进行实际的内容处理*/
bizOperationRedisSync(dealBizType, dealInfoType, joinPoint.getArgs()[index]);
}
/**
* 只是模拟:根据业务处理内容进行数据适配转换到业务侧进行实际的内容处理
*
* @param dealBizType 具体处理的业务
* @param dealInfoType 具体异步操作的内容
* @param bizDealInfo 具体业务信息详细
*/
private void bizOperationRedisSync(int dealBizType, int dealInfoType, Object bizDealInfo) {
if (Objects.isNull(bizDealInfo)) {
return;
}
/*以下只做基本的模拟操作,实际要求按业务做相关的适配*/
if (dealBizType == OperationRedisSyncBiz.SENSITIVE.getType() && dealInfoType == OperationRedisSyncType.CREATE.getType()) {
/*如果是对应敏感词业务的新增逻辑,调用命敏感词异步同步redis相关操作*/
SensitiveWord sensitiveWord = (SensitiveWord) bizDealInfo;
log.info("敏感词业务的新增逻辑-调用命敏感词异步同步redis相关操作开始");
log.info("敏感词业务的新增异步同步redis逻辑:sensitiveWord={},key-{},value:{}",
sensitiveWord,
sensitiveWord.getSensitiveId() + "-" + sensitiveWord.getSensitive() + "-" + sensitiveWord.getKind()
, sensitiveWord);
log.info("敏感词业务的新增逻辑-调用命敏感词异步同步redis相关操作完成");
}
if (dealBizType == OperationRedisSyncBiz.SENSITIVE.getType() && dealInfoType == OperationRedisSyncType.DELETE.getType()) {
/*如果是对应敏感词业务的新增逻辑,调用命敏感词异步同步redis相关操作*/
Long sensitiveWordId = (Long) bizDealInfo;
log.info("敏感词业务的删除逻辑-调用命敏感词异步同步redis相关操作开始");
log.info("敏感词业务的删除异步同步redis逻辑:sensitiveWordId={}", sensitiveWordId);
log.info("敏感词业务的删除逻辑-调用命敏感词异步同步redis相关操作完成");
}
}
}
业务应用
package org.zyf.javabasic.aop.redissync;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.zyf.javabasic.designpatterns.responsibility.pipeline.SensitiveWord;
/**
* @author yanfengzhang
* @description
* @date 2022/7/18 23:55
*/
@Service
@Slf4j
public class SensitiveOperationService {
@OperationRedisSync(dealBizType = OperationRedisSyncCons.BizType.SENSITIVE,
dealInfoType = OperationRedisSyncCons.DealType.CREATE,
dealInfo = "sensitiveWord")
public void createSensitive(SensitiveWord sensitiveWord, String opName) {
log.info("敏感词业务,新建操作");
log.info("敏感词业务,DB新建操作完成");
}
@OperationRedisSync(dealBizType = OperationRedisSyncCons.BizType.SENSITIVE,
dealInfoType = OperationRedisSyncCons.DealType.DELETE,
dealInfo = "id")
public void deleteSensitive(Long id, String opName) {
log.info("敏感词业务,删除操作");
log.info("敏感词业务,DB删除操作完成");
}
}
测试结果
测试代码
package org.zyf.javabasic.aop.redissync;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.zyf.javabasic.ZYFApplication;
import org.zyf.javabasic.designpatterns.responsibility.pipeline.SensitiveWord;
/**
* @author yanfengzhang
* @description
* @date 2022/7/19 23:48
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ZYFApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Slf4j
public class SensitiveOperationServiceTest {
@Autowired
private SensitiveOperationService sensitiveOperationService;
@Test
public void testOperationRedisSync() {
log.info("=====测试新建敏感词异步同步redis=====");
sensitiveOperationService.createSensitive(SensitiveWord.builder()
.sensitiveId(11L).sensitive("刘亦菲").kind(1).build(), "zhangyanfeng");
log.info("=====测试删除敏感词异步同步redis=====");
sensitiveOperationService.deleteSensitive(11L, "zhangyanfeng");
}
}
结果展示
参考文献、书籍和链接
1.杨开振,深入浅出Spring Boot 2.X,中国工信出版社集团/人民邮电出版社,2015.10.
2.Spring AOP详解及其用法(一)_heshengfu1211的博客-CSDN博客_auditable注解
3.Spring AOP 面向切面编程_劳资是学渣丶的博客-CSDN博客
今天的文章Spring AOP理解与研发使用分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/6406.html