一、导入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注解实现业务功能的动态配置的更多相关文章

  1. JAVA Spring 事物 ( 已转账为例 ) 基于 AOP 注解

    <一> 配置为文件 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns=&qu ...

  2. Springboot自带定时任务实现动态配置Cron参数

    同学们,我今天分享一下SpringBoot动态配置Cron参数.场景是这样子的:后台管理界面对定时任务进行管理,可动态修改执行时间,然后保存入库,每次任务执行前从库里查询时间,以达到动态修改Cron参 ...

  3. Seata 动态配置订阅与降级实现原理

    Seata 的动态降级需要结合配置中心的动态配置订阅功能.动态配置订阅,即通过配置中心监听订阅,根据需要读取已更新的缓存值,ZK.Apollo.Nacos 等第三方配置中心都有现成的监听器可实现动态刷 ...

  4. Kafka动态配置实现原理解析

    问题导读 Apache Kafka在全球各个领域各大公司获得广泛使用,得益于它强大的功能和不断完善的生态.其中Kafka动态配置是一个比较高频好用的功能,下面我们就来一探究竟. 动态配置是如何设计的? ...

  5. Springboot定时任务实现动态配置Cron参数(从外部数据库获取)

    https://blog.csdn.net/qq_35992900/article/details/80429245 我们主要讲解它的动态配置使用方法. 在刚开始使用的时候,我们更改一个任务的执行时间 ...

  6. winform app.config文件的动态配置

    获取 获取应用程序exe.config文件中  节点value值 /// <summary> /// 功能: 读取应用程序exe.config文件中 /// appSettings节点下 ...

  7. 010-Spring aop 001-核心说明-拦截指定类与方法、基于自定义注解的切面

    一.概述 面向切面编程(AOP)是针对面向对象编程(OOP)的补充,可以非侵入式的为多个不具有继承关系的对象引入相同的公共行为例如日志.安全.事务.性能监控等等.SpringAOP允许将公共行为从业务 ...

  8. Spring Aop(四)——基于Aspectj注解的Advice介绍

    转发地址:https://www.iteye.com/blog/elim-2395315 4 基于Aspectj注解的Advice介绍 之前介绍过,Advice一共有五种类型,分别是before.af ...

  9. Spring学习之旅(七)基于XML配置与基于AspectJ注解配置的AOP编程比较

    本篇博文用一个稍复杂点的案例来对比一下基于XML配置与基于AspectJ注解配置的AOP编程的不同. 相关引入包等Spring  AOP编程准备,请参考小编的其他博文,这里不再赘述. 案例要求: 写一 ...

随机推荐

  1. AutoMapper介绍(未完待续、部分没实现)

    实体间转换工具.其实也可以用Json来实现同名属性.异名属性(用JsonProperty指明)的自动转换 最新版本6.11 需要使用vs2013以上.vs2012下载新版 nuget会遇到问题.只能旧 ...

  2. Django 的操作

    安装: pip install Django 创建django工程 django-admin startproject  mysite python manage.py startapp blog / ...

  3. Chap6:风险与监督[《区块链中文词典》维京&甲子]

  4. [security][modsecurity] modsecurity 规则说明/中文/转发

    原文转发以防丢失. 地址: http://www.catssec.com:8090/exploit/?p=691 转来细读之后,并没有太多的参考价值  :( modsecurity规则手册 通用格式 ...

  5. 一种比较简单的实现ping的方式

    <span style="font-family: Arial, Helvetica, sans-serif;">头文件</span> <span s ...

  6. vue打包后出现"Failed to load resource: net::ERR_FILE_NOT_FOUND"错误

    创建vue脚手架搭建项目之后,用npm run build经行打包,运行index.html后出现异常: 打开dist/index.html, 诸如这些的,引入是有问题的, 这边的全部是绝对路径,而本 ...

  7. Github上Laravel开源排行榜Star数61-90名

    Github上Laravel开源排行榜Star数61-90名,罗列所有 Laravel 开源扩展包,含 Github Star 数量,下载数量和项目简介.默认排序是按Star数量从多到少来排 61.c ...

  8. python类型错误:can only concatenate list (not "str") to list

    TypeError:can only concatenate list (not "str") to list: 类型错误:只能将list类型和list类型联系起来,而不是str类 ...

  9. 前端ps部分

    一. 使用ps前的一些设置工作 1.首选项的设置 编剧-首选项-单位与标尺-[标尺/文字]改为像素 2.面板的设置 窗口--仅保留常用的[F7图层].[F8信息].[历史记录]. 3.视图的设置 视图 ...

  10. [CC]Mesh文件保存

    CC中有两个地方使用了文件保存,一个是MainWindow,另一个是ccCommandLineParser. MainWindow的保存按钮关联的槽是doActionSaveFile()方法,实现了点 ...