基于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编程准备,请参考小编的其他博文,这里不再赘述. 案例要求: 写一 ...
随机推荐
- python immutable and mutable
https://blog.csdn.net/hsuxu/article/details/7785835 python mutable as default parameter(NONONO) def ...
- [maven] 初试maven
环境 CentOS 6 一, 安装: [root@okk ~]# wget http://mirrors.hust.edu.cn/apache/maven/maven-3/3.5.0/binaries ...
- js中的事件轮询(event loop)机制
异步任务指的是,不进入主线程.而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行. ...
- Django实现邮件发送功能
首先申请邮箱并在设置中申请到授权码,授权码的目的仅仅是让你有权限发邮件,但是不能登录到邮箱进行修改,发送邮件时,可以代替密码 1,配置文件settings.py #邮件服务配置文件 EMAIL_USE ...
- 生成器-代码举例:()和yield
怎么自定义一个生成器:两个方法: 1.小括号包裹表达式 2.函数中用yield返回 方法一:①小括号包裹表达式 G=(x*2 for x in range(5)) print(G)输出:<gen ...
- thinkphp模板使用
1.模板文件 就是个html,可以保存到View的Public文件夹下,比如叫base.html(参考onethink) <block name="a">a</b ...
- vue-router重新定向、redirect与alias区别
redirect app.vue <router-link to="/goParams/918/i like vue">goParams</router-link ...
- 虚存管理页面置换算法 — FIFO和RUL算法模拟实现
本篇博文为追忆以前写过的算法系列第一篇(20081021) 温故知新 目的: 为了解决内存容量有限与多作业执行的冲突.运用了虚拟存储技术.能从逻辑上对内存进行扩充,达到扩充内存的效果.分页存储管理是实 ...
- MySQL group replication介绍
“MySQL group replication” group replication是MySQL官方开发的一个开源插件,是实现MySQL高可用集群的一个工具.第一个GA版本正式发布于MySQL5.7 ...
- TADOConnection组件
该组件用于建立数据库的连接.ADO的数据源组件和命令组件可以通过该组件运行命令及数据库中提取数据等. 该组件用于建立数据库的连接,该连接可被多个数据集所共享,但是并不是应用程序中必须的,因为ADO数据 ...