基于AOP注解实现业务功能的动态配置

Posted 老人与JAVA

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于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注解实现业务功能的动态配置的主要内容,如果未能解决你的问题,请参考以下文章

基于注解的Spring AOP的配置和使用--转载

基于注解的Spring AOP的配置和使用--转载

基于注解的Spring AOP的配置和使用

基于注解的Spring AOP的配置和使用

Spring Aop实现方式(注解和Xml)

Spring Aop实现方式(注解和Xml)