引言
面向切面编程(aop)是spring框架的核心功能之一,它通过预编译和运行期动态代理实现程序功能的统一维护。在springboot应用中,aop能够帮助我们优雅地解决横切关注点问题,如日志记录、权限控制、性能监控等,这些功能往往贯穿整个应用但又不属于业务核心逻辑。
本文将介绍springboot中4种aop实战应用场景,包括代码实现、核心原理及实践。
场景一:日志记录与性能监控
业务需求
在企业级应用中,我们通常需要:
- 记录api请求的调用情况
- 监控方法执行时间,发现性能瓶颈
- 追踪方法调用的入参和返回结果
实现方案
@aspect
@component
@slf4j
public class loggingaspect {
/**
* 定义切点:所有controller包下的所有方法
*/
@pointcut("execution(* com.example.demo.controller.*.*(..))")
public void controllermethods() {}
/**
* 环绕通知:记录请求日志和执行时间
*/
@around("controllermethods()")
public object logaroundcontrollers(proceedingjoinpoint joinpoint) throws throwable {
// 获取方法签名
methodsignature signature = (methodsignature) joinpoint.getsignature();
string methodname = signature.getname();
string classname = signature.getdeclaringtypename();
// 记录请求参数
string params = arrays.tostring(joinpoint.getargs());
log.info("request to {}.{} with params: {}", classname, methodname, params);
// 记录开始时间
long starttime = system.currenttimemillis();
// 执行目标方法
object result;
try {
result = joinpoint.proceed();
// 计算执行时间
long executiontime = system.currenttimemillis() - starttime;
// 记录返回结果和执行时间
log.info("response from {}.{} ({}ms): {}",
classname, methodname, executiontime, result);
// 记录慢方法
if (executiontime > 1000) {
log.warn("slow execution detected! {}.{} took {}ms",
classname, methodname, executiontime);
}
return result;
} catch (exception e) {
// 记录异常信息
log.error("exception in {}.{}: {}", classname, methodname, e.getmessage(), e);
throw e;
}
}
/**
* 定义服务层方法切点
*/
@pointcut("execution(* com.example.demo.service.*.*(..))")
public void servicemethods() {}
/**
* 记录服务层方法的关键调用
*/
@before("servicemethods() && @annotation(logmethod)")
public void logservicemethod(joinpoint joinpoint, logmethod logmethod) {
methodsignature signature = (methodsignature) joinpoint.getsignature();
string methodname = signature.getname();
// 获取参数名
string[] paramnames = signature.getparameternames();
object[] args = joinpoint.getargs();
stringbuilder logmessage = new stringbuilder();
logmessage.append("executing ").append(methodname).append(" with params: {");
for (int i = 0; i < paramnames.length; i++) {
logmessage.append(paramnames[i]).append("=").append(args[i]);
if (i < paramnames.length - 1) {
logmessage.append(", ");
}
}
logmessage.append("}");
// 根据注解设置的级别记录日志
switch (logmethod.level()) {
case debug:
log.debug(logmessage.tostring());
break;
case info:
log.info(logmessage.tostring());
break;
case warn:
log.warn(logmessage.tostring());
break;
case error:
log.error(logmessage.tostring());
break;
}
}
}
/**
* 自定义日志注解
*/
@retention(retentionpolicy.runtime)
@target(elementtype.method)
public @interface logmethod {
loglevel level() default loglevel.info;
public enum loglevel {
debug, info, warn, error
}
}
使用示例
@service
public class userservice {
@logmethod(level = logmethod.loglevel.info)
public user findbyid(long id) {
// 业务逻辑
return userrepository.findbyid(id).orelse(null);
}
@logmethod(level = logmethod.loglevel.warn)
public void updateuserstatus(long userid, string status) {
// 更新用户状态
}
}
扩展:mdc实现请求跟踪
@component
public class requestidfilter implements filter {
@override
public void dofilter(servletrequest request, servletresponse response, filterchain chain)
throws ioexception, servletexception {
try {
// 为每个请求生成唯一id
string requestid = uuid.randomuuid().tostring().replace("-", "");
mdc.put("requestid", requestid);
if (request instanceof httpservletrequest) {
httpservletrequest httprequest = (httpservletrequest) request;
// 记录用户信息
authentication auth = securitycontextholder.getcontext().getauthentication();
if (auth != null && auth.isauthenticated()) {
mdc.put("userid", auth.getname());
}
mdc.put("remoteaddr", httprequest.getremoteaddr());
}
chain.dofilter(request, response);
} finally {
// 请求完成后清理mdc
mdc.clear();
}
}
}
场景二:权限控制与安全增强
业务需求
在企业应用中,权限控制是一个常见的需求:
- 基于角色的接口访问控制
- 细粒度的操作权限控制
- 对敏感数据访问的记录
实现方案
首先,创建自定义注解:
@retention(retentionpolicy.runtime)
@target({elementtype.method, elementtype.type})
public @interface requirespermission {
/**
* 所需权限编码数组,满足其中任一即可
*/
string[] value() default {};
/**
* 权限逻辑类型:and(同时具有所有权限), or(满足任一权限即可)
*/
logicaltype logical() default logicaltype.or;
public enum logicaltype {
and, or
}
}
实现权限切面:
@aspect
@component
@slf4j
public class permissionaspect {
@autowired
private userservice userservice;
/**
* 定义切点:所有带有@requirespermission注解的方法
*/
@pointcut("@annotation(com.example.demo.annotation.requirespermission)")
public void permissioncheck() {}
/**
* 权限验证前置通知
*/
@before("permissioncheck() && @annotation(requirespermission)")
public void checkpermission(joinpoint joinpoint, requirespermission requirespermission) {
// 获取当前用户
user currentuser = getcurrentuser();
if (currentuser == null) {
throw new unauthorizedexception("用户未登录或会话已过期");
}
// 获取用户权限列表
set<string> userpermissions = userservice.getuserpermissions(currentuser.getid());
// 获取注解中要求的权限
string[] requiredpermissions = requirespermission.value();
requirespermission.logicaltype logicaltype = requirespermission.logical();
// 权限校验
boolean haspermission = false;
if (logicaltype == requirespermission.logicaltype.or) {
// 满足任一权限即可
for (string permission : requiredpermissions) {
if (userpermissions.contains(permission)) {
haspermission = true;
break;
}
}
} else {
// 必须同时满足所有权限
haspermission = true;
for (string permission : requiredpermissions) {
if (!userpermissions.contains(permission)) {
haspermission = false;
break;
}
}
}
if (!haspermission) {
log.warn("用户 {} 尝试访问未授权资源: {}.{}",
currentuser.getusername(),
joinpoint.getsignature().getdeclaringtypename(),
joinpoint.getsignature().getname());
throw new forbiddenexception("权限不足,无法执行该操作");
}
// 记录敏感操作
log.info("用户 {} 执行了需授权操作: {}.{}",
currentuser.getusername(),
joinpoint.getsignature().getdeclaringtypename(),
joinpoint.getsignature().getname());
}
/**
* 定义切点:带有@requiresrole注解的方法
*/
@pointcut("@annotation(com.example.demo.annotation.requiresrole)")
public void rolecheck() {}
/**
* 角色检查前置通知
*/
@before("rolecheck() && @annotation(requiresrole)")
public void checkrole(joinpoint joinpoint, requiresrole requiresrole) {
// 获取当前用户
user currentuser = getcurrentuser();
if (currentuser == null) {
throw new unauthorizedexception("用户未登录或会话已过期");
}
// 获取用户角色
set<string> userroles = userservice.getuserroles(currentuser.getid());
// 获取注解中要求的角色
string[] requiredroles = requiresrole.value();
// 角色校验
boolean hasrole = false;
for (string role : requiredroles) {
if (userroles.contains(role)) {
hasrole = true;
break;
}
}
if (!hasrole) {
log.warn("用户 {} 尝试访问未授权角色资源: {}.{}",
currentuser.getusername(),
joinpoint.getsignature().getdeclaringtypename(),
joinpoint.getsignature().getname());
throw new forbiddenexception("角色不足,无法执行该操作");
}
}
/**
* 数据权限过滤切点:针对查询方法
*/
@pointcut("execution(* com.example.demo.service.*.find*(..))")
public void datapermissionfilter() {}
/**
* 数据权限过滤通知
*/
@around("datapermissionfilter()")
public object filterdatabypermission(proceedingjoinpoint joinpoint) throws throwable {
// 获取当前用户
user currentuser = getcurrentuser();
// 默认情况下执行原方法
object result = joinpoint.proceed();
// 如果是管理员,无需过滤数据
if (userservice.isadmin(currentuser.getid())) {
return result;
}
// 对查询结果进行过滤
if (result instanceof collection) {
collection<?> collection = (collection<?>) result;
// 实现数据过滤逻辑...
} else if (result instanceof page) {
page<?> page = (page<?>) result;
// 实现分页数据过滤...
}
return result;
}
/**
* 获取当前登录用户
*/
private user getcurrentuser() {
authentication authentication = securitycontextholder.getcontext().getauthentication();
if (authentication == null || !authentication.isauthenticated()) {
return null;
}
object principal = authentication.getprincipal();
if (principal instanceof user) {
return (user) principal;
}
return null;
}
}
/**
* 自定义角色注解
*/
@retention(retentionpolicy.runtime)
@target({elementtype.method, elementtype.type})
public @interface requiresrole {
string[] value();
}
使用示例
@restcontroller
@requestmapping("/api/users")
public class usercontroller {
@autowired
private userservice userservice;
@getmapping
@requirespermission("user:list")
public list<user> listusers() {
return userservice.findall();
}
@getmapping("/{id}")
@requirespermission("user:view")
public user getuser(@pathvariable long id) {
return userservice.findbyid(id);
}
@postmapping
@requirespermission(value = {"user:create", "user:edit"}, logical = requirespermission.logicaltype.or)
public user createuser(@requestbody user user) {
return userservice.save(user);
}
@deletemapping("/{id}")
@requiresrole("admin")
public void deleteuser(@pathvariable long id) {
userservice.delete(id);
}
@putmapping("/{id}/status")
@requirespermission(value = {"user:edit", "user:manage"}, logical = requirespermission.logicaltype.and)
public user updateuserstatus(@pathvariable long id, @requestparam string status) {
return userservice.updatestatus(id, status);
}
}
场景三:自定义缓存实现
业务需求
缓存是提升应用性能的关键手段,通过aop可以实现:
- 自定义缓存策略,满足特定业务需求
- 细粒度的缓存控制
- 灵活的缓存键生成和过期策略
实现方案
首先定义缓存注解:
@retention(retentionpolicy.runtime)
@target(elementtype.method)
public @interface cacheable {
/**
* 缓存名称
*/
string cachename();
/**
* 缓存键表达式,支持spel表达式
*/
string key() default "";
/**
* 过期时间(秒)
*/
long expiretime() default 300;
/**
* 是否使用方法参数作为缓存键的一部分
*/
boolean usemethodparameters() default true;
}
@retention(retentionpolicy.runtime)
@target(elementtype.method)
public @interface cacheevict {
/**
* 缓存名称
*/
string cachename();
/**
* 缓存键表达式
*/
string key() default "";
/**
* 是否清除所有缓存
*/
boolean allentries() default false;
}
实现缓存切面:
@aspect
@component
@slf4j
public class cacheaspect {
@autowired
private redistemplate<string, object> redistemplate;
@autowired
private cachekeygenerator keygenerator;
/**
* 定义缓存获取切点
*/
@pointcut("@annotation(com.example.demo.annotation.cacheable)")
public void cacheableoperation() {}
/**
* 定义缓存清除切点
*/
@pointcut("@annotation(com.example.demo.annotation.cacheevict)")
public void cacheevictoperation() {}
/**
* 缓存环绕通知
*/
@around("cacheableoperation() && @annotation(cacheable)")
public object handlecacheable(proceedingjoinpoint joinpoint, cacheable cacheable) throws throwable {
// 生成缓存键
string cachekey = generatecachekey(joinpoint, cacheable.cachename(), cacheable.key(), cacheable.usemethodparameters());
// 检查缓存中是否已有数据
boolean haskey = redistemplate.haskey(cachekey);
if (boolean.true.equals(haskey)) {
object cachedvalue = redistemplate.opsforvalue().get(cachekey);
log.debug("cache hit for key: {}", cachekey);
return cachedvalue;
}
// 缓存未命中,执行方法获取结果
log.debug("cache miss for key: {}", cachekey);
object result = joinpoint.proceed();
// 将结果存入缓存
if (result != null) {
redistemplate.opsforvalue().set(cachekey, result, cacheable.expiretime(), timeunit.seconds);
log.debug("stored in cache with key: {}, expire time: {}s", cachekey, cacheable.expiretime());
}
return result;
}
/**
* 缓存清除前置通知
*/
@before("cacheevictoperation() && @annotation(cacheevict)")
public void handlecacheevict(joinpoint joinpoint, cacheevict cacheevict) {
if (cacheevict.allentries()) {
// 清除该缓存名称下的所有条目
string cachepattern = cacheevict.cachename() + ":*";
set<string> keys = redistemplate.keys(cachepattern);
if (keys != null && !keys.isempty()) {
redistemplate.delete(keys);
log.debug("cleared all cache entries with pattern: {}", cachepattern);
}
} else {
// 清除指定键的缓存
string cachekey = generatecachekey(joinpoint, cacheevict.cachename(), cacheevict.key(), true);
redistemplate.delete(cachekey);
log.debug("cleared cache with key: {}", cachekey);
}
}
/**
* 生成缓存键
*/
private string generatecachekey(joinpoint joinpoint, string cachename, string keyexpression, boolean useparams) {
stringbuilder keybuilder = new stringbuilder(cachename).append(":");
// 如果提供了自定义键表达式
if (stringutils.hastext(keyexpression)) {
string evaluatedkey = keygenerator.generatekey(keyexpression, joinpoint);
keybuilder.append(evaluatedkey);
} else if (useparams) {
// 使用方法签名和参数作为键
methodsignature signature = (methodsignature) joinpoint.getsignature();
string methodname = signature.getname();
keybuilder.append(methodname);
// 添加参数
object[] args = joinpoint.getargs();
if (args != null && args.length > 0) {
for (object arg : args) {
if (arg != null) {
keybuilder.append(":").append(arg.hashcode());
} else {
keybuilder.append(":null");
}
}
}
} else {
// 仅使用方法名
keybuilder.append(joinpoint.getsignature().getname());
}
return keybuilder.tostring();
}
}
/**
* 缓存键生成器,支持spel表达式
*/
@component
public class cachekeygenerator {
private final expressionparser parser = new spelexpressionparser();
private final standardevaluationcontext context = new standardevaluationcontext();
public string generatekey(string expression, joinpoint joinpoint) {
methodsignature signature = (methodsignature) joinpoint.getsignature();
method method = signature.getmethod();
object[] args = joinpoint.getargs();
string[] parameternames = signature.getparameternames();
// 设置方法参数为上下文变量
for (int i = 0; i < parameternames.length; i++) {
context.setvariable(parameternames[i], args[i]);
}
// 添加额外的元数据
context.setvariable("method", method.getname());
context.setvariable("class", method.getdeclaringclass().getsimplename());
context.setvariable("target", joinpoint.gettarget());
// 执行表达式
expression exp = parser.parseexpression(expression);
return exp.getvalue(context, string.class);
}
}
redis配置
@configuration
public class redisconfig {
@bean
public redistemplate<string, object> redistemplate(redisconnectionfactory connectionfactory) {
redistemplate<string, object> template = new redistemplate<>();
template.setconnectionfactory(connectionfactory);
// 使用jackson2jsonredisserializer序列化值
jackson2jsonredisserializer<object> jackson2jsonredisserializer = new jackson2jsonredisserializer<>(object.class);
objectmapper om = new objectmapper();
om.setvisibility(propertyaccessor.all, jsonautodetect.visibility.any);
om.activatedefaulttyping(laissezfairesubtypevalidator.instance, objectmapper.defaulttyping.non_final);
jackson2jsonredisserializer.setobjectmapper(om);
// 设置键的序列化方式为字符串
template.setkeyserializer(new stringredisserializer());
// 值使用json序列化
template.setvalueserializer(jackson2jsonredisserializer);
// hash键也使用字符串
template.sethashkeyserializer(new stringredisserializer());
// hash值使用json序列化
template.sethashvalueserializer(jackson2jsonredisserializer);
template.afterpropertiesset();
return template;
}
}
使用示例
@service
public class productservice {
@autowired
private productrepository productrepository;
@cacheable(cachename = "products", expiretime = 3600)
public product getbyid(long id) {
return productrepository.findbyid(id).orelse(null);
}
@cacheable(cachename = "products", key = "'list:category:' + #categoryid", expiretime = 1800)
public list<product> getbycategory(long categoryid) {
return productrepository.findbycategoryid(categoryid);
}
@cacheevict(cachename = "products", allentries = true)
public product save(product product) {
return productrepository.save(product);
}
@cacheevict(cachename = "products", key = "'list:category:' + #product.categoryid")
public void deleteproductfromcategory(product product) {
productrepository.delete(product);
}
}
场景四:统一异常处理与重试机制
业务需求
在分布式系统或复杂业务场景中,我们常常需要:
- 优雅地处理异常
- 对某些操作进行自动重试
- 对关键操作进行幂等性保证
实现方案
首先定义重试和异常处理注解:
@retention(retentionpolicy.runtime)
@target(elementtype.method)
public @interface retryable {
/**
* 最大重试次数
*/
int maxattempts() default 3;
/**
* 重试间隔(毫秒)
*/
long backoff() default 1000;
/**
* 指定捕获的异常类型
*/
class<? extends throwable>[] value() default {exception.class};
/**
* 重试策略
*/
retrystrategy strategy() default retrystrategy.fixed;
/**
* 重试策略枚举
*/
enum retrystrategy {
/**
* 固定间隔
*/
fixed,
/**
* 指数退避
*/
exponential
}
}
@retention(retentionpolicy.runtime)
@target(elementtype.method)
public @interface idempotent {
/**
* 幂等键表达式
*/
string key();
/**
* 过期时间(秒)
*/
long expireseconds() default 300;
}
实现异常处理和重试切面:
@aspect
@component
@slf4j
public class retryaspect {
@autowired
private redistemplate<string, string> redistemplate;
/**
* 定义可重试操作切点
*/
@pointcut("@annotation(com.example.demo.annotation.retryable)")
public void retryableoperation() {}
/**
* 定义幂等操作切点
*/
@pointcut("@annotation(com.example.demo.annotation.idempotent)")
public void idempotentoperation() {}
/**
* 重试环绕通知
*/
@around("retryableoperation() && @annotation(retryable)")
public object handleretry(proceedingjoinpoint joinpoint, retryable retryable) throws throwable {
int attempts = 0;
class<? extends throwable>[] retryableexceptions = retryable.value();
retryable.retrystrategy strategy = retryable.strategy();
while (true) {
attempts++;
try {
// 执行目标方法
return joinpoint.proceed();
} catch (throwable t) {
// 检查是否是需要重试的异常类型
boolean shouldretry = false;
for (class<? extends throwable> exceptiontype : retryableexceptions) {
if (exceptiontype.isinstance(t)) {
shouldretry = true;
break;
}
}
// 如果不需要重试,或者达到最大重试次数,则抛出异常
if (!shouldretry || attempts >= retryable.maxattempts()) {
log.warn("method {} failed after {} attempts: {}",
joinpoint.getsignature().getname(), attempts, t.getmessage());
throw t;
}
// 计算重试等待时间
long waittime;
if (strategy == retryable.retrystrategy.exponential) {
// 指数退避: 基础时间 * 2^(尝试次数-1)
waittime = retryable.backoff() * (long) math.pow(2, attempts - 1);
} else {
// 固定间隔
waittime = retryable.backoff();
}
log.info("retrying {} (attempt {}/{}) after {} ms due to: {}",
joinpoint.getsignature().getname(),
attempts,
retryable.maxattempts(),
waittime,
t.getmessage());
// 等待指定时间后重试
thread.sleep(waittime);
}
}
}
/**
* 幂等性环绕通知
*/
@around("idempotentoperation() && @annotation(idempotent)")
public object handleidempotent(proceedingjoinpoint joinpoint, idempotent idempotent) throws throwable {
// 解析幂等键
string idempotentkey = resolveidempotentkey(joinpoint, idempotent.key());
string lockkey = "idempotent:" + idempotentkey;
// 尝试设置分布式锁
boolean success = redistemplate.opsforvalue().setifabsent(
lockkey, "processing", idempotent.expireseconds(), timeunit.seconds);
if (boolean.true.equals(success)) {
try {
// 获取锁成功,执行业务逻辑
object result = joinpoint.proceed();
// 将结果存入redis
string resultkey = "result:" + lockkey;
redistemplate.opsforvalue().set(
resultkey, new objectmapper().writevalueasstring(result),
idempotent.expireseconds(), timeunit.seconds);
// 标记为已处理
redistemplate.opsforvalue().set(
lockkey, "completed", idempotent.expireseconds(), timeunit.seconds);
return result;
} catch (throwable t) {
// 处理失败,标记错误
redistemplate.opsforvalue().set(
lockkey, "error:" + t.getmessage(), idempotent.expireseconds(), timeunit.seconds);
throw t;
}
} else {
// 获取锁失败,表示操作正在处理或已处理
string status = redistemplate.opsforvalue().get(lockkey);
if ("processing".equals(status)) {
// 还在处理中
throw new concurrentoperationexception("操作正在处理中,请勿重复提交");
} else if (status != null && status.startswith("error:")) {
// 之前处理出错
throw new operationfailedexception("操作处理失败: " + status.substring(6));
} else if ("completed".equals(status)) {
// 已完成,尝试返回之前的结果
string resultkey = "result:" + lockkey;
string resultjson = redistemplate.opsforvalue().get(resultkey);
if (resultjson != null) {
// 将json反序列化为响应对象
method method = ((methodsignature) joinpoint.getsignature()).getmethod();
class<?> returntype = method.getreturntype();
try {
return new objectmapper().readvalue(resultjson, returntype);
} catch (exception e) {
log.error("failed to deserialize cached result: {}", e.getmessage());
}
}
// 如果没有找到结果或反序列化失败,返回成功但无法提供上次结果的消息
throw new operationalreadycompletedexception("操作已成功处理,但无法提供上次操作的结果");
}
// 状态未知,抛出异常
throw new operationfailedexception("操作状态未知");
}
}
/**
* 解析幂等键表达式
*/
private string resolveidempotentkey(joinpoint joinpoint, string keyexpression) {
methodsignature signature = (methodsignature) joinpoint.getsignature();
string[] paramnames = signature.getparameternames();
object[] args = joinpoint.getargs();
// 创建表达式上下文
standardevaluationcontext context = new standardevaluationcontext();
// 添加方法参数
for (int i = 0; i < paramnames.length; i++) {
context.setvariable(paramnames[i], args[i]);
}
// 添加类名和方法名
context.setvariable("method", signature.getmethod().getname());
context.setvariable("class", signature.getdeclaringtype().getsimplename());
// 解析表达式
expressionparser parser = new spelexpressionparser();
expression expression = parser.parseexpression(keyexpression);
return expression.getvalue(context, string.class);
}
}
// 自定义异常类
public class concurrentoperationexception extends runtimeexception {
public concurrentoperationexception(string message) {
super(message);
}
}
public class operationfailedexception extends runtimeexception {
public operationfailedexception(string message) {
super(message);
}
}
public class operationalreadycompletedexception extends runtimeexception {
public operationalreadycompletedexception(string message) {
super(message);
}
}
使用示例
@service
public class paymentservice {
@autowired
private paymentgateway paymentgateway;
@autowired
private orderrepository orderrepository;
/**
* 远程支付处理,可能遇到网络问题需要重试
*/
@retryable(
value = {connectexception.class, timeoutexception.class, paymentgatewayexception.class},
maxattempts = 3,
backoff = 2000,
strategy = retryable.retrystrategy.exponential
)
public paymentresult processpayment(string orderid, bigdecimal amount) {
log.info("processing payment for order {} with amount {}", orderid, amount);
// 调用远程支付网关
return paymentgateway.processpayment(orderid, amount);
}
/**
* 订单退款,需要保证幂等性
*/
@idempotent(key = "'refund:' + #orderid", expireseconds = 3600)
public refundresult refundorder(string orderid) {
order order = orderrepository.findbyid(orderid)
.orelsethrow(() -> new ordernotfoundexception("order not found: " + orderid));
// 验证订单状态
if (!"paid".equals(order.getstatus())) {
throw new invalidorderstatusexception("cannot refund order with status: " + order.getstatus());
}
// 调用支付网关退款
refundresult result = paymentgateway.refund(order.getpaymentid(), order.gettotalamount());
// 更新订单状态
order.setstatus("refunded");
order.setrefundtime(localdatetime.now());
orderrepository.save(order);
return result;
}
}
@service
public class stockservice {
@autowired
private stockrepository stockrepository;
/**
* 扣减库存,需要在分布式环境下重试和幂等
*/
@retryable(
value = {optimisticlockexception.class, stockinsufficientexception.class},
maxattempts = 5,
backoff = 500
)
@idempotent(key = "'deduct:' + #orderid")
public void deductstock(string orderid, list<orderitem> items) {
// 检查是否存在库存记录
for (orderitem item : items) {
stock stock = stockrepository.findbyproductid(item.getproductid());
if (stock == null) {
throw new productnotfoundexception("product not found: " + item.getproductid());
}
if (stock.getavailable() < item.getquantity()) {
throw new stockinsufficientexception(
"insufficient stock for product: " + item.getproductid() +
", requested: " + item.getquantity() +
", available: " + stock.getavailable());
}
}
// 执行库存扣减
for (orderitem item : items) {
stockrepository.deductstock(item.getproductid(), item.getquantity());
}
}
}
结论
aop是springboot中一个强大的编程范式,通过这些模式,我们可以将横切关注点与业务逻辑解耦,使代码更加模块化、可维护,同时提高系统的健壮性和安全性。
以上就是springboot中四种aop实战应用场景及代码实现的详细内容,更多关于springboot aop应用场景的资料请关注代码网其它相关文章!
发表评论