基于AOP注解实现业务功能的动态配置
一、导入jar包
<dependency><!-- 4.引入AOP-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
二、自定义注解
package com.test.domi.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* 标注业务功能注解
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface BusinessFuc { /**
* 校验功能
* beanNames,point(before | overwrite | after)
*/
String[] funcNames(); }
三 、定义切面切中注解并织入相关业务
package com.test.domi.aspect; import com.test.domi.annotation.BusinessFuc;
import com.test.domi.common.util.SpringContextUtil;
import com.test.domi.func.BusinessMethodParam;
import com.test.domi.func.IBusinessFunc;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map; /**
* 业务功能切面处理
*/
@Component
@Aspect
@Order(1)
public class BusinessFuncAspect { /**
* 前置扩展
*/
public static final String BEFORE_POINT = "before";
/**
* 后置扩展
*/
public static final String AFTER_POINT = "after";
/**
* 覆盖扩展
*/
public static final String OVERWRITE_POINT = "overwrite";
/**
* funcName和切面的分隔符
*/
public static final String FUNC_SEPARATOR = "|";
/**
* JS引擎
*/
public static final String NASHORN_ENGINE = "nashorn";
/**
* 业务功能类型 -JAVA
*/
public static final String JAVA_FUNC = "JAVA";
/**
* 业务功能类型 -JS
*/
public static final String JS_FUNC = "JS";
/**
* 业务功能类型 -groovy
*/
public static final String GROOVY_FUNC = "GROOVY"; /**
* 拦截@BusinessFuc注解,执行业务功能
* @param joinPoint
* @return
* @throws Exception
*/
@Around(value="@annotation(com.test.domi.annotation.BusinessFuc)")
@Transactional(rollbackFor = Throwable.class)
public Object businessFunc(ProceedingJoinPoint joinPoint) throws Throwable{
//获取注解中的Func分组
Map<String,List<String>> funcGroups = getAnnotationFuncGroup(joinPoint);
//触发业务功能
return executeBusinessFunc(joinPoint,funcGroups);
} /**
* 执行业务功能
* @param joinPoint 切面
* @param funcGroups 功能分组
* @return
* @throws Throwable
*/
private Object executeBusinessFunc(ProceedingJoinPoint joinPoint,Map<String,List<String>> funcGroups) throws Throwable{
//新增业务方法描述
BusinessMethodParam businessMethodParam = new BusinessMethodParam(joinPoint.getArgs());
//before处理
List<String> beforeFuncs = funcGroups.get(BEFORE_POINT);
if(!CollectionUtils.isEmpty(beforeFuncs)){
executeFunc(beforeFuncs,businessMethodParam);
}
//overwrite处理
List<String> overwriteFuncs = funcGroups.get(OVERWRITE_POINT);
if(!CollectionUtils.isEmpty(overwriteFuncs)){
//如果有多个功能,只执行最后一个
int overSize = overwriteFuncs.size();
executeFunc(overwriteFuncs.subList(overSize - 1,overSize),businessMethodParam);
}else{
//没有配置overwrite功能,则执行原业务方法
Object returnObj = joinPoint.proceed();
businessMethodParam.setResult(returnObj);
}
//after处理
List<String> afterFuncs = funcGroups.get(AFTER_POINT);
if(!CollectionUtils.isEmpty(afterFuncs)){
executeFunc(afterFuncs,businessMethodParam);
}
return businessMethodParam.getResult();
} /**
* 触发功能
* @param funcs 功能beanName集合
* @param businessMethodParam 业务方法描述
*/
private void executeFunc(List<String> funcs,BusinessMethodParam businessMethodParam) throws Throwable{
for (String funcName : funcs){
executeFunc(funcName,JAVA_FUNC,businessMethodParam);
}
} /**
* 触发功能
* @param func 业务功能
* @param func 业务功能类型:JAVA/JS
* @param businessMethodParam 业务方法描述
*/
private void executeFunc(String func,String funcType,BusinessMethodParam businessMethodParam) throws Throwable{
if(JAVA_FUNC.equalsIgnoreCase(funcType)){
//JAVA功能
IBusinessFunc businessFunc = (IBusinessFunc) SpringContextUtil.getBean(func);
businessFunc.executeFunc(businessMethodParam);
}else if (JS_FUNC.equalsIgnoreCase(funcType)){
//JS功能
ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName(NASHORN_ENGINE);
scriptEngine.eval(func);
Invocable invocable = (Invocable)scriptEngine;
invocable.invokeFunction("executeFunc",businessMethodParam);
}else if (GROOVY_FUNC.equalsIgnoreCase(funcType)){
//执行groovy功能
}
} /**
* 读取注解上的func配置
* @param joinPoint 切面
* @return func分组,按照before|after|overwrite分组
*/
private Map<String,List<String>> getAnnotationFuncGroup(ProceedingJoinPoint joinPoint)throws Exception{
MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
BusinessFuc businessFuc = methodSignature.getMethod().getAnnotation(BusinessFuc.class);
List<String> beforeFuncNames = new ArrayList<>();
List<String> overwriteFuncNames = new ArrayList<>();
List<String> afterFuncNames = new ArrayList<>();
for (String func : businessFuc.funcNames()){
String point = StringUtils.substringAfter(func,FUNC_SEPARATOR);
if(BEFORE_POINT.equals(point)){
beforeFuncNames.add(StringUtils.substringBefore(func,FUNC_SEPARATOR));
}else if(AFTER_POINT.equals(point)){
afterFuncNames.add(StringUtils.substringBefore(func,FUNC_SEPARATOR));
}else if(OVERWRITE_POINT.equals(point)){
overwriteFuncNames.add(StringUtils.substringBefore(func,FUNC_SEPARATOR));
}else{
//没有配置point,默认取overwrite
overwriteFuncNames.add(func);
}
}
Map<String,List<String>> funcGroup = new HashMap<>();
funcGroup.put(BEFORE_POINT,beforeFuncNames);
funcGroup.put(AFTER_POINT,afterFuncNames);
funcGroup.put(OVERWRITE_POINT,overwriteFuncNames);
return funcGroup;
} }
四、封装承载参数和返回值的VO
package com.test.domi.func; import java.util.HashMap;
import java.util.Map; /**
* 业务方法描述
*/
public class BusinessMethodParam { /**
* 业务方法参数
*/
private Object[] args;
/**
* 业务方法返回结果
*/
private Object result;
/**
* 自定义参数Map
*/
private Map<String,Object> paramMap; public BusinessMethodParam(){
this.paramMap = new HashMap<>();
}
public BusinessMethodParam(Object[] args){
this.args = args;
this.paramMap = new HashMap<>();
} /**
* 获取业务方法参数
*/
public Object[] getArgs(){
return args;
} /**
* 获取业务方法的返回结果
*/
public Object getResult(){
return result;
} /**
* 设置业务方法参数返回结果
*/
public void setResult(Object result){
this.result = result;
} /**
* 获取自定义参数值
*/
public Object get(String key){
return paramMap.get(key);
} /**
* 设置子弟你参数值,可用于不同功能之间传递自定义参数
*/
public void put(String key,Object value){
paramMap.put(key,value);
} }
五、为业务功能提供统一的约束接口
package com.test.domi.func; /**
* 报销类型关联功能接口
*/
public interface IBusinessFunc { /**
* 执行业务功能
*/
void executeFunc(BusinessMethodParam businessMethodParam) throws Exception;
}
六、编写业务功能扩展代码
package com.test.domi.func.impl; import com.alibaba.fastjson.JSONObject;
import com.test.domi.dto.RefundBillLine;
import com.test.domi.func.BusinessMethodParam;
import com.test.domi.func.IBusinessFunc;
import org.springframework.stereotype.Component; import java.util.List; @Component("testFunc")
public class TestFunc implements IBusinessFunc { @Override
public void executeFunc(BusinessMethodParam businessMethodParam) throws Exception {
Object[] args = businessMethodParam.getArgs();
List<RefundBillLine> result = (List<RefundBillLine>)businessMethodParam.getResult();
result.add(new RefundBillLine());
}
}
七、在service使用,在controller捕获异常
package com.test.domi.service.impl;
import com.test.domi.annotation.BusinessFuc;
import com.test.domi.dao.BillLineMapper;
import com.test.domi.dto.RefundBillLine;
import com.test.domi.service.BillLineService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List; @Service("billLineService")
public class BillLineServiceImpl implements BillLineService { @Autowired
private BillLineMapper billLineMapper; @Override
@BusinessFuc(funcNames = {"testFunc|after"})
public List<RefundBillLine> queryAll() {
return billLineMapper.queryInfos();
}
}
基于AOP注解实现业务功能的动态配置的更多相关文章
- JAVA Spring 事物 ( 已转账为例 ) 基于 AOP 注解
<一> 配置为文件 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns=&qu ...
- Springboot自带定时任务实现动态配置Cron参数
同学们,我今天分享一下SpringBoot动态配置Cron参数.场景是这样子的:后台管理界面对定时任务进行管理,可动态修改执行时间,然后保存入库,每次任务执行前从库里查询时间,以达到动态修改Cron参 ...
- Seata 动态配置订阅与降级实现原理
Seata 的动态降级需要结合配置中心的动态配置订阅功能.动态配置订阅,即通过配置中心监听订阅,根据需要读取已更新的缓存值,ZK.Apollo.Nacos 等第三方配置中心都有现成的监听器可实现动态刷 ...
- Kafka动态配置实现原理解析
问题导读 Apache Kafka在全球各个领域各大公司获得广泛使用,得益于它强大的功能和不断完善的生态.其中Kafka动态配置是一个比较高频好用的功能,下面我们就来一探究竟. 动态配置是如何设计的? ...
- Springboot定时任务实现动态配置Cron参数(从外部数据库获取)
https://blog.csdn.net/qq_35992900/article/details/80429245 我们主要讲解它的动态配置使用方法. 在刚开始使用的时候,我们更改一个任务的执行时间 ...
- winform app.config文件的动态配置
获取 获取应用程序exe.config文件中 节点value值 /// <summary> /// 功能: 读取应用程序exe.config文件中 /// appSettings节点下 ...
- 010-Spring aop 001-核心说明-拦截指定类与方法、基于自定义注解的切面
一.概述 面向切面编程(AOP)是针对面向对象编程(OOP)的补充,可以非侵入式的为多个不具有继承关系的对象引入相同的公共行为例如日志.安全.事务.性能监控等等.SpringAOP允许将公共行为从业务 ...
- Spring Aop(四)——基于Aspectj注解的Advice介绍
转发地址:https://www.iteye.com/blog/elim-2395315 4 基于Aspectj注解的Advice介绍 之前介绍过,Advice一共有五种类型,分别是before.af ...
- Spring学习之旅(七)基于XML配置与基于AspectJ注解配置的AOP编程比较
本篇博文用一个稍复杂点的案例来对比一下基于XML配置与基于AspectJ注解配置的AOP编程的不同. 相关引入包等Spring AOP编程准备,请参考小编的其他博文,这里不再赘述. 案例要求: 写一 ...
随机推荐
- WinAPI Hook
1.抢先load 需要hook的dll,替换需要hook的函数的地址, 2.调用堆栈信息的获取: 3.内存信息的统计: 4.如何统计已经free掉的内存? 5.如何批量注入程序load的dll? IA ...
- Qt::带返回值的信号发射方式
一般来说,我们发出信号使用emit这个关键字来操作,但是会发现,emit并不算一个调用,所以它没有返回值.那么如果我们发出这个信号想获取一个返回值怎么办呢? 两个办法:1.通过出参形式返回,引用或者指 ...
- 使用Maven根据WSDL生成生成Java代码
转载:https://blog.csdn.net/pzasdq/article/details/52601473 为便于自己学习,整理 修改pom.xml <project xmlns=&quo ...
- C语言编程中pid, tid以及真实pid的关系(转)
add by zhj: 下面是我对pid,tgid,ppid的个人理解 对于ubuntu14.04操作系统,可以在/usr/src/linux-headers-4.4.0-31/include/lin ...
- django时间的时区问题(转)
add by zhj: 使用django时,如果设置USE_TZ=True,那django在数据库中存储的是0时区的时间:如果USE_TZ=False,那存储的是本地时间 原文:https://www ...
- 第一章:HTML5的基础
HTML5基础 1.DoctYpe声明 <!DCTYPE html>必须放在第一行. <title> <title> 百度</title> <me ...
- maven工程中防止mapper.xml文件被漏掉、未加载的方法
maven工程中防止mapper.xml文件被漏掉.未加载的方法 就是在pom.xml文件中添加以下内容 <!-- 如果不添加此节点mybatis的mapper.xml文件都会被漏掉. --&g ...
- MyBatis的核心组件
MyBatis的核心组件主要分为4个部分 SqlSessionFactoryBuilder(构造器):它会根据配置或者代码来生成SqlSessionFactory,采用的是分步构建的Builder模式 ...
- ORACLE12C架构图
- 帝国cms-tab
<ul class="intro_commenTab"> [e:loop={"select classname,classpath,classid from ...