一、导入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. .net Core2建立MVC网站,部署

    1..net Core2使用sqlservver.EFCore,部署在linux上将出错,具体是错原因大概是:连接超时的意思.=>就想测试下linux到底能不能连接sqlserver.是两者技术 ...

  2. 【Mysql数据库访问利器】phpMyadmin

    缘由 我们程序员难免要和数据库打交道,经过这几年的锻炼,感觉手写SQL语句已经忘记的差不错了,促使我一定要这篇文章的原因是,有一次晚上我更新某个系统的数据库的表(由于目前公司比较严格,数据库都只能通过 ...

  3. 查找->静态查找表->分块查找(索引顺序表)

    文字描述 分块查找又称为索引顺序查找,是顺序查找的一种改进方法.在此查找算法中,除表本身外, 还需要建立一个”索引表”.索引表中包括两项内容:关键字项(其值为该字表内的最大关键字)和指针项(指示该子表 ...

  4. Linux中常用命令

    .cd命令 cd    回到跟目录 cd uqihong     进入到uqihong这个文件夹(且cd命令只能一级一级的进入) 2.复制命令      cp -r /usr/local/tomcat ...

  5. /etc/passwd- 和/etc/shadow-文件

    今天偶尔看到系统里有/etc/passwd- 和/etc/shadow-文件,经测试只要执行过系统的用户操作命令就会产生,如deluser.passwd.chpasswd.adduser等命令,应该是 ...

  6. Ansible 快速上手(转)

    add by zhj: 执行Ansible(发音时,重音在最前面)命令有两种方式,一种是ad-hoc形式,另一种是playbooks,对于软件开发者来说,一般使用ad-hoc就足够了.playbook ...

  7. GDB常用命令系列

    本文由霸气的菠萝原创,转载请注明出处:http://www.cnblogs.com/xsln/p/gdb_instructions.html 本文为索引,请点击以下链接进行阅读: GDB调试原理——p ...

  8. VS Code对.NET Core项目持续的Build

    首先打开csproj文件, 添加一个watcher tool: <Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGr ...

  9. what's the 回撤

    什么是“回撤”? “回撤”是个谓语,前面隐含了一个主语.一般来说,没有人说“亏损回撤”的,我们说的“回撤”,通常指“股价回撤”.“市值回撤”.“净值回撤”和“盈利回撤”. “股价回撤”是针对个股的,即 ...

  10. MySQL准入规范及容量评估

    一.数据库设计 1.表结构设计 -表中的自增列(auto_increment属性)推荐使用bigint类型 -首选使用非空的唯一键, 其次选择自增列或发号器 不使用更新频繁的列,尽量不选择字符串列,不 ...