AOP的写法

前言

什么是AOP?

AOP全称(Aspect Oriented Programming)面向切片编程的简称。AOP面向方面编程基于IoC,是对OOP的有益补充;
AOP利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了 多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的 逻辑或责任封装起来,比如日志记录,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。
AOP代表的是一个横向的关 系,将“对象”比作一个空心的圆柱体,其中封装的是对象的属性和行为;则面向方面编程的方法,就是将这个圆柱体以切面形式剖开,选择性的提供业务逻辑。而 剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹,但完成了效果。

实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。

Spring实现AOP:JDK动态代理和CGLIB代理 JDK动态代理:其代理对象必须是某个接口的实现,它是通过在运行期间创建一个接口的实现类来完成对目标对象的代理;其核心的两个类是InvocationHandler和Proxy。 CGLIB代理:实现原理类似于JDK动态代理,只是它在运行期间生成的代理对象是针对目标类扩展的子类。CGLIB是高效的代码生成包,底层是依靠ASM(开源的java字节码编辑类库)操作字节码实现的,性能比JDK强;需要引入包asm.jar和cglib.jar。使用AspectJ注入式切面和@AspectJ注解驱动的切面实际上底层也是通过动态代理实现的。

基础概念

1.通知(Advice)
  就是你想要的功能,也就是上面说的 安全,事物,日志等。你给先定义好把,然后在想用的地方用一下。

2.连接点(JoinPoint)

这个更好解释了,就是spring允许你使用通知的地方,那可真就多了,基本每个方法的前,后(两者都有也行),或抛出异常时都可以是连接点,spring只支持方法连接点.其他如aspectJ还可以让你在构造器或属性注入时都行,不过那不是咱关注的,只要记住,和方法有关的前前后后(抛出异常),都是连接点。

3.切入点(Pointcut)

上面说的连接点的基础上,来定义切入点,你的一个类里,有15个方法,那就有几十个连接点了对把,但是你并不想在所有方法附近都使用通知(使用叫织入,以后再说),你只想让其中的几个,在调用这几个方法之前,之后或者抛出异常时干点什么,那么就用切点来定义这几个方法,让切点来筛选连接点,选中那几个你想要的方法。

4.切面(Aspect)

切面是通知和切入点的结合。现在发现了吧,没连接点什么事情,连接点就是为了让你好理解切点,搞出来的,明白这个概念就行了。通知说明了干什么和什么时候干(什么时候通过方法名中的before,after,around等就能知道),而切入点说明了在哪干(指定到底是哪个方法),这就是一个完整的切面定义。

5.引入(introduction)

允许我们向现有的类添加新方法属性。这不就是把切面(也就是新方法属性:通知定义的)用到目标类中吗

6.目标(target)

引入中所提到的目标类,也就是要被通知的对象,也就是真正的业务逻辑,他可以在毫不知情的情况下,被咱们织入切面。而自己专注于业务本身的逻辑。

7.代理(proxy)

怎么实现整套aop机制的,都是通过代理,这个一会给细说。

8.织入(weaving)

把切面应用到目标对象来创建新的代理对象的过程。有3种方式,spring采用的是运行时,为什么是运行时,后面解释。关键就是:切点定义了哪些连接点会得到通知

简单实现 - 敏感词替换 (注解+AOP+DFA算法)

使用的DFA算法

package tech.niua.core.utils.sensitiveword;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.*;
import java.util.*;

@Component
@ConditionalOnProperty(name = "sensitiveWord.enable", havingValue = "true")
public class SensitiveWordUtil {
    //字库路径
    @Value("${sensitiveWord.path}")
    private String filePath;
    private static String SENSITIVE_WORD_PATH;

    private static Logger logger = LoggerFactory.getLogger(SensitiveWordUtil.class);

    /**
     * 敏感词匹配规则
     */
    public static final int MinMatchTYpe = 1;      //最小匹配规则,如:敏感词库["中国","中国人"],语句:"我是中国人",匹配结果:我是[中国]人
    public static final int MaxMatchType = 2;      //最大匹配规则,如:敏感词库["中国","中国人"],语句:"我是中国人",匹配结果:我是[中国人]

    /**
     * 敏感词集合
     */
    @SuppressWarnings("rawtypes")
    public static HashMap sensitiveWordMap;

    /**
     * 初始化敏感词库,构建DFA算法模型
     */
    @PostConstruct
    private synchronized void init(){
        SENSITIVE_WORD_PATH = filePath;
        Set<String> sensitiveWordSet = new HashSet<>();

        // 读取指定路径下的敏感字库
        try {
            logger.info("正在初始化敏感字库....{}",SENSITIVE_WORD_PATH);
            File wordFileDir = new File(SENSITIVE_WORD_PATH);
            File[] wordFiles = wordFileDir.listFiles();
            for (File wordFile : wordFiles) {
                logger.info("加载{}字库",wordFile.getName());
                BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(wordFile),"utf-8"));
                String line;
                while ((line = reader.readLine()) != null) {
                    sensitiveWordSet.add(line);
                    logger.trace("加载敏感字{}",line);
                }
                reader.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
            logger.error("初始化敏感字库失败,未加载字库....");
        }
        logger.info("加载{}个敏感字",sensitiveWordSet.size());
        initSensitiveWordMap(sensitiveWordSet);
    }

    /**
     * 初始化敏感词库,构建DFA算法模型
     *
     * @param sensitiveWordSet 敏感词库
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    private static void initSensitiveWordMap(Set<String> sensitiveWordSet) {
        //初始化敏感词容器,减少扩容操作
        sensitiveWordMap = new HashMap(sensitiveWordSet.size());
        String key;
        Map nowMap;
        Map<String, String> newWorMap;
        //迭代sensitiveWordSet
        Iterator<String> iterator = sensitiveWordSet.iterator();
        while (iterator.hasNext()) {
            //关键字
            key = iterator.next();
            nowMap = sensitiveWordMap;
            for (int i = 0; i < key.length(); i++) {
                //转换成char型
                char keyChar = key.charAt(i);
                //库中获取关键字
                Object wordMap = nowMap.get(keyChar);
                //如果存在该key,直接赋值,用于下一个循环获取
                if (wordMap != null) {
                    nowMap = (Map) wordMap;
                } else {
                    //不存在则,则构建一个map,同时将isEnd设置为0,因为他不是最后一个
                    newWorMap = new HashMap<>();
                    //不是最后一个
                    newWorMap.put("isEnd", "0");
                    nowMap.put(keyChar, newWorMap);
                    nowMap = newWorMap;
                }

                if (i == key.length() - 1) {
                    //最后一个
                    nowMap.put("isEnd", "1");
                }
            }
        }
    }

    /**
     * 判断文字是否包含敏感字符
     *
     * @param txt       文字
     * @param matchType 匹配规则 1:最小匹配规则,2:最大匹配规则
     * @return 若包含返回true,否则返回false
     */
    public static boolean contains(String txt, int matchType) {
        boolean flag = false;
        for (int i = 0; i < txt.length(); i++) {
            int matchFlag = checkSensitiveWord(txt, i, matchType); //判断是否包含敏感字符
            if (matchFlag > 0) {    //大于0存在,返回true
                flag = true;
            }
        }
        return flag;
    }

    /**
     * 判断文字是否包含敏感字符
     *
     * @param txt 文字
     * @return 若包含返回true,否则返回false
     */
    public static boolean contains(String txt) {
        return contains(txt, MaxMatchType);
    }

    /**
     * 获取文字中的敏感词
     *
     * @param txt       文字
     * @param matchType 匹配规则 1:最小匹配规则,2:最大匹配规则
     * @return
     */
    public static Set<String> getSensitiveWord(String txt, int matchType) {
        Set<String> sensitiveWordList = new HashSet<>();

        for (int i = 0; i < txt.length(); i++) {
            //判断是否包含敏感字符
            int length = checkSensitiveWord(txt, i, matchType);
            if (length > 0) {//存在,加入list中
                sensitiveWordList.add(txt.substring(i, i + length));
                i = i + length - 1;//减1的原因,是因为for会自增
            }
        }

        return sensitiveWordList;
    }

    /**
     * 获取文字中的敏感词
     *
     * @param txt 文字
     * @return
     */
    public static Set<String> getSensitiveWord(String txt) {
        return getSensitiveWord(txt, MaxMatchType);
    }

    /**
     * 替换敏感字字符
     *
     * @param txt         文本
     * @param replaceChar 替换的字符,匹配的敏感词以字符逐个替换,如 语句:我爱中国人 敏感词:中国人,替换字符:*, 替换结果:我爱***
     * @param matchType   敏感词匹配规则
     * @return
     */
    public static String replaceSensitiveWord(String txt, char replaceChar, int matchType) {
        String resultTxt = txt;
        //获取所有的敏感词
        Set<String> set = getSensitiveWord(txt, matchType);
        Iterator<String> iterator = set.iterator();
        String word;
        String replaceString;
        while (iterator.hasNext()) {
            word = iterator.next();
            replaceString = getReplaceChars(replaceChar, word.length());
            resultTxt = resultTxt.replaceAll(word, replaceString);
        }

        return resultTxt;
    }

    /**
     * 替换敏感字字符
     *
     * @param txt         文本
     * @param replaceChar 替换的字符,匹配的敏感词以字符逐个替换,如 语句:我爱中国人 敏感词:中国人,替换字符:*, 替换结果:我爱***
     * @return
     */
    public static String replaceSensitiveWord(String txt, char replaceChar) {
        return replaceSensitiveWord(txt, replaceChar, MaxMatchType);
    }

    /**
     * 替换敏感字字符
     *
     * @param txt        文本
     * @param replaceStr 替换的字符串,匹配的敏感词以字符逐个替换,如 语句:我爱中国人 敏感词:中国人,替换字符串:[屏蔽],替换结果:我爱[屏蔽]
     * @param matchType  敏感词匹配规则
     * @return
     */
    public static String replaceSensitiveWord(String txt, String replaceStr, int matchType) {
        String resultTxt = txt;
        //获取所有的敏感词
        Set<String> set = getSensitiveWord(txt, matchType);
        Iterator<String> iterator = set.iterator();
        String word;
        while (iterator.hasNext()) {
            word = iterator.next();
            resultTxt = resultTxt.replaceAll(word, replaceStr);
        }

        return resultTxt;
    }

    /**
     * 替换敏感字字符
     *
     * @param txt        文本
     * @param replaceStr 替换的字符串,匹配的敏感词以字符逐个替换,如 语句:我爱中国人 敏感词:中国人,替换字符串:[屏蔽],替换结果:我爱[屏蔽]
     * @return
     */
    public static String replaceSensitiveWord(String txt, String replaceStr) {
        return replaceSensitiveWord(txt, replaceStr, MaxMatchType);
    }

    /**
     * 获取替换字符串
     *
     * @param replaceChar
     * @param length
     * @return
     */
    private static String getReplaceChars(char replaceChar, int length) {
        String resultReplace = String.valueOf(replaceChar);
        for (int i = 1; i < length; i++) {
            resultReplace += replaceChar;
        }

        return resultReplace;
    }

    /**
     * 检查文字中是否包含敏感字符,检查规则如下:<br>
     *
     * @param txt
     * @param beginIndex
     * @param matchType
     * @return 如果存在,则返回敏感词字符的长度,不存在返回0
     */
    @SuppressWarnings("rawtypes")
    private static int checkSensitiveWord(String txt, int beginIndex, int matchType) {
        //敏感词结束标识位:用于敏感词只有1位的情况
        boolean flag = false;
        //匹配标识数默认为0
        int matchFlag = 0;
        char word;
        Map nowMap = sensitiveWordMap;
        for (int i = beginIndex; i < txt.length(); i++) {
            word = txt.charAt(i);
            //获取指定key
            nowMap = (Map) nowMap.get(word);
            if (nowMap != null) {//存在,则判断是否为最后一个
                //找到相应key,匹配标识+1
                matchFlag++;
                //如果为最后一个匹配规则,结束循环,返回匹配标识数
                if ("1".equals(nowMap.get("isEnd"))) {
                    //结束标志位为true
                    flag = true;
                    //最小规则,直接返回,最大规则还需继续查找
                    if (MinMatchTYpe == matchType) {
                        break;
                    }
                }
            } else {//不存在,直接返回
                break;
            }
        }
        if (matchFlag < 2 || !flag) {//长度必须大于等于1,为词
            matchFlag = 0;
        }
        return matchFlag;
    }

//    public static void main(String[] args) {
//
//        System.out.println("敏感词的数量:" + SensitiveWordUtil.sensitiveWordMap.size());
//        String string = "太多的伤感情怀也许只局限于饲养基地 荧幕中的情节。"
//                + "然后我们的扮演的角色就是跟随着主人公的喜红客联盟 怒哀乐而过于牵强的把自己的情感也附加于银幕情节中,然后感动就流泪,"
//                + "难过就躺在某一个人的怀里尽情的阐述心扉或者手机卡复制器一个贱人一杯红酒一部电影在夜 深人静的晚上,关上电话静静的发呆着。";
//        System.out.println("待检测语句字数:" + string.length());
//
//        //是否含有关键字
//        boolean result = SensitiveWordUtil.contains(string);
//        System.out.println(result);
//        result = SensitiveWordUtil.contains(string, SensitiveWordUtil.MinMatchTYpe);
//        System.out.println(result);
//
//        //获取语句中的敏感词
//        Set<String> set = SensitiveWordUtil.getSensitiveWord(string);
//        System.out.println("语句中包含敏感词的个数为:" + set.size() + "。包含:" + set);
//        set = SensitiveWordUtil.getSensitiveWord(string, SensitiveWordUtil.MinMatchTYpe);
//        System.out.println("语句中包含敏感词的个数为:" + set.size() + "。包含:" + set);
//
//        //替换语句中的敏感词
//        String filterStr = SensitiveWordUtil.replaceSensitiveWord(string, '*');
//        System.out.println(filterStr);
//        filterStr = SensitiveWordUtil.replaceSensitiveWord(string, '*', SensitiveWordUtil.MinMatchTYpe);
//        System.out.println(filterStr);
//
//        String filterStr2 = SensitiveWordUtil.replaceSensitiveWord(string, "[*敏感词*]");
//        System.out.println(filterStr2);
//        filterStr2 = SensitiveWordUtil.replaceSensitiveWord(string, "[*敏感词*]", SensitiveWordUtil.MinMatchTYpe);
//        System.out.println(filterStr2);
//    }
}

1.定义一个注解

在这里插入图片描述

2.引入依赖

在相应的pom.xml文件中,引入aop注解

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>

3.定义一个AOP切面

这里是引用

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import tech.niua.core.utils.sensitiveword.SensitiveWordUtil;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

@Aspect
@Component
@ConditionalOnProperty(name = "sensitiveWord.enable", havingValue = "true")
public class WordAspect {

    @Pointcut("@within(tech.niua.core.annotation.Word) || @annotation(tech.niua.core.annotation.Word)")
    public void sensitiveWordPointCut() {
    }
    @Around("sensitiveWordPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        boolean enableFilter = false;
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        Class<?> clazz = method.getDeclaringClass();
        Word methodSensitiveWordFilter = method.getAnnotation(Word.class);
        Word clazzSensitiveWordFilter = clazz.getAnnotation(Word.class);

        if(methodSensitiveWordFilter != null){//优先取方法上的注解
            enableFilter = methodSensitiveWordFilter.value();
        }else{//其次取类上的注解
            enableFilter = clazzSensitiveWordFilter.value();
        }

        Class<?>[] parameterTypes = method.getParameterTypes();
        Object[] paramValues = point.getArgs();
        if (enableFilter == true) {
            for (int i = 0; i < paramValues.length; i++) {
                Object value = paramValues[i];
                if (parameterTypes[i].isAssignableFrom(String.class)) {//String类型参数直接过滤
                    if(null != value){
                        value = SensitiveWordUtil.replaceSensitiveWord((String) value, '*', SensitiveWordUtil.MinMatchTYpe);
                    }
                } else if (!isBasicType(parameterTypes[i])) {//对象类型遍历参数,对String类型过滤
                    Field[] fields = value.getClass().getDeclaredFields();
                    for (Field field : fields) {
                        Class<?> type = field.getType();
                        if(type.isAssignableFrom(String.class)){
                            field.setAccessible(true);
                            String fieldValue = (String)field.get(value);
                            if(null != fieldValue){
                                fieldValue = SensitiveWordUtil.replaceSensitiveWord((String) fieldValue, '*', SensitiveWordUtil.MinMatchTYpe);
                                field.set(value,fieldValue);
                            }
                        }
                    }
                }
                paramValues[i] = value;
            }
        }
        return point.proceed(paramValues);
    }
    private boolean isBasicType(Class clazz) {
        if (clazz.isAssignableFrom(Integer.class) ||
                clazz.isAssignableFrom(Byte.class) ||
                clazz.isAssignableFrom(Long.class) ||
                clazz.isAssignableFrom(Double.class) ||
                clazz.isAssignableFrom(Float.class) ||
                clazz.isAssignableFrom(Character.class) ||
                clazz.isAssignableFrom(Short.class) ||
                clazz.isAssignableFrom(Boolean.class)) {
            return true;
        }
        return false;
    }

}

在application.yml配置文件中写入

 #敏感字过滤的配置
sensitiveWord:
  enable: true #是否开启敏感字过滤功能
  path: D:\test\sensitive-words #字库的加载路径

在目录下创建敏感词库

在这里插入图片描述

4.在需要过滤的方法上声明注解

在这里插入图片描述

创建测试类执行

在这里插入图片描述

相关文章

学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习...
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面...
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生...
Can’t connect to local MySQL server through socket \'/v...
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 ...
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服...