diff --git a/src/main/java/com/forte/qqrobot/BaseApplication.java b/src/main/java/com/forte/qqrobot/BaseApplication.java index 2a73ee1..eb7e026 100644 --- a/src/main/java/com/forte/qqrobot/BaseApplication.java +++ b/src/main/java/com/forte/qqrobot/BaseApplication.java @@ -64,7 +64,8 @@ * @date Created in 2019/3/29 10:18 * @since JDK1.8 **/ -public abstract class BaseApplication app, Register reg protected void afterDepend(CONFIG config, Application app, Register register, DependCenter dependCenter) { // 初始化http模板 initHttpTemplate(dependCenter); + // 初始化bot验证函数与路径拼接函数 //**************** 注册PathAssembler和VerifyFunction ****************// VerifyFunction verifyFunction = verifyBot(); dependCenter.load(verifyFunction); PathAssembler pathAssembler = config.getPathAssembler(); dependCenter.load(pathAssembler); + // 初始化bot管理中心 BotManager botManager = initBotManager(dependCenter); @@ -812,14 +815,14 @@ private DependCenter scanAndInject(CONFIG config, Application app) { /** * 有些事情需要连接之后才能做,例如加载定时任务,需要空函数送信器 */ - private void after(CONFIG config) { + private void after(CONFIG config, MsgSender defaultMsgSender) { // 注册定时任务 - registerTimeTask(); + registerTimeTask(defaultMsgSender); } - private void registerTimeTask(){ + private void registerTimeTask(MsgSender defaultMsgSender){ //注册定时任务 - this.register.registerTimeTask(this.defaultMsgSender); + this.register.registerTimeTask(defaultMsgSender); } /** @@ -991,7 +994,7 @@ public CONTEXT run(Application app, String... args) { //获取CQCodeUtil实例 CQCodeUtil cqCodeUtil = ResourceDispatchCenter.getCQCodeUtil(); - after(configuration); + after(configuration, startResult.getDefaultMsgSender()); long e = System.currentTimeMillis(); // 展示连接成功的信息 diff --git a/src/main/java/com/forte/qqrobot/anno/Catch.java b/src/main/java/com/forte/qqrobot/anno/Catch.java deleted file mode 100644 index 64c2b16..0000000 --- a/src/main/java/com/forte/qqrobot/anno/Catch.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.forte.qqrobot.anno; - -/** - * 在监听器中进行异常处理 - * 此注解标注在监听器类中,进行异常处理 - * 如果非静态方法,则与出现异常的监听类使用的是同一个类容器 - * - * @author ForteScarlet <[email]ForteScarlet@163.com> - * @since JDK1.8 - **/ -public @interface Catch { - - /** 捕获的异常类型,默认为{@link Exception}类型 */ - Class value() default java.lang.Exception.class; - - /** 当出现多个可处理方法的时候,根据这个来进行优先级配置,数越小优先级越高 */ - int level() default 0; - - -} diff --git a/src/main/java/com/forte/qqrobot/anno/ExceptionCatch.java b/src/main/java/com/forte/qqrobot/anno/ExceptionCatch.java new file mode 100644 index 0000000..5432a24 --- /dev/null +++ b/src/main/java/com/forte/qqrobot/anno/ExceptionCatch.java @@ -0,0 +1,24 @@ +package com.forte.qqrobot.anno; + +import com.forte.qqrobot.anno.depend.Beans; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 在监听器中进行异常处理, 标注在实现了ExceptionHandle接口的类上 + * + * @author ForteScarlet <[email]ForteScarlet@163.com> + * @since JDK1.8 + **/ +@Retention(RetentionPolicy.RUNTIME) //注解会在class字节码文件中存在,在运行时可以通过反射获取到 +@Target({ElementType.TYPE, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) //接口、类、枚举、注解、方法 +@Beans +public @interface ExceptionCatch { + + /** 捕获的异常类型,默认为{@link Exception}类型 */ + Class[] value() default java.lang.Exception.class; + +} diff --git a/src/main/java/com/forte/qqrobot/anno/data/Catch.java b/src/main/java/com/forte/qqrobot/anno/data/Catch.java deleted file mode 100644 index ae92e6c..0000000 --- a/src/main/java/com/forte/qqrobot/anno/data/Catch.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.forte.qqrobot.anno.data; - -/** - * 注解的参数类 - * @see com.forte.qqrobot.anno.Catch 此注解的参数封装类 - * @author ForteScarlet <[email]ForteScarlet@163.com> - * @since JDK1.8 - **/ -public class Catch { - - private final Class value; - private final int level; - - //**************** 默认值 ****************// - - private static final Class DEFAULT_VALUE = java.lang.Exception.class; - private static final int DEFAULT_LEVEL = 0; - - private static final Catch DEFAULT = new Catch( - DEFAULT_VALUE, - DEFAULT_LEVEL - ); - - private Catch(Class value, int level) { - this.value = value; - this.level = level; - } - - public static Catch build(Class value, int level){ - return new Catch(value, level); - } - - public static Catch build(com.forte.qqrobot.anno.Catch catchAnnotation){ - return build( - catchAnnotation.value(), - catchAnnotation.level() - ); - } - - public static Catch build(){ - return DEFAULT; - } - - public Class value(){ - return value; - } - - public int level(){ - return level; - } - -} diff --git a/src/main/java/com/forte/qqrobot/beans/cqcode/CQCode.java b/src/main/java/com/forte/qqrobot/beans/cqcode/CQCode.java index 6be8f24..afd0d99 100644 --- a/src/main/java/com/forte/qqrobot/beans/cqcode/CQCode.java +++ b/src/main/java/com/forte/qqrobot/beans/cqcode/CQCode.java @@ -11,7 +11,8 @@ import java.util.stream.Stream; /** - * CQCode参数 + *
 CQCode封装类
+ * 
 从核心1.10.2开始,不再进行权限认证。
  * @author ForteScarlet <[163邮箱地址]ForteScarlet@163.com>
  * @date Created in 2019/3/9 11:42
  * @since JDK1.8
@@ -223,29 +224,6 @@ public Stream> stream(){
     protected CQCode(CQCodeTypes cqCodeTypes, Map params){
         this.CqCodeType = cqCodeTypes;
         this.params = params;
-
-        //遍历参数,判断参数是否都是符合规范的
-        //需要的参数
-        String[] keys = cqCodeTypes.getKeys();
-        //获取可以忽略的参数
-        Set ignoreAbleKeys = cqCodeTypes.getIgnoreAbleKeys();
-        for (String key : keys) {
-            String getParams = params.get(key);
-            if(getParams == null && (!ignoreAbleKeys.contains(key))){
-                throw new CQParamsException("paramCannotIgnore", cqCodeTypes, key);
-            }
-
-            if(getParams != null){
-                String keyRegex = cqCodeTypes.getKeyRegex(key);
-                if(keyRegex != null && !getParams.matches(keyRegex)){
-                    throw new CQParamsException("paramMismatch", cqCodeTypes, key, keyRegex, getParams);
-                }
-
-            }
-
-
-        }
-
         updateToString();
     }
 
diff --git a/src/main/java/com/forte/qqrobot/beans/types/CQCodeTypes.java b/src/main/java/com/forte/qqrobot/beans/types/CQCodeTypes.java
index 64e01ec..18700bb 100644
--- a/src/main/java/com/forte/qqrobot/beans/types/CQCodeTypes.java
+++ b/src/main/java/com/forte/qqrobot/beans/types/CQCodeTypes.java
@@ -9,14 +9,13 @@
 import java.util.stream.Collectors;
 
 /**
- * 此枚举保存全部的CQCode类型。
- * 且提供了一个{@link com.forte.qqrobot.factory.CQCodeTypeFactory}来支持动态扩展此枚举。
- * 请不要使用任何非工厂创建的形式(例如自己通过反射或者其他工具)创建此类的实例对象。此类中维护一个function映射, - * 用于通过function值快速定位CQCodeType,并且提供了一个注册接口{@link #register(CQCodeTypes)} 来支持额外注册的实例对象,并对其进行验证。 - * 包括{@link com.forte.qqrobot.factory.CQCodeTypeFactory}中也提供了直接创建新枚举实例的相关接口,并提供参数验证。 - * 假如您使用了其他手段自己创建了一个额外的实例,可能会导致valueOf所取值与内部的function映射值不相符、参数冲突等一系列问题。 - *
- *
+ *
 此枚举保存全部的CQCode类型。
+ * 
 且提供了一个{@link com.forte.qqrobot.factory.CQCodeTypeFactory}来支持动态扩展此枚举。
+ * 
 请不要使用任何非工厂创建的形式(例如自己通过反射或者其他工具)创建此类的实例对象。此类中维护一个function映射,
+ * 
  用于通过function值快速定位CQCodeType,并且提供了一个注册接口{@link #register(CQCodeTypes)} 来支持额外注册的实例对象,并对其进行验证。
+ * 
 包括{@link com.forte.qqrobot.factory.CQCodeTypeFactory}中也提供了直接创建新枚举实例的相关接口,并提供参数验证。
+ * 
 假如您使用了其他手段自己创建了一个额外的实例,可能会导致valueOf所取值与内部的function映射值不相符、参数冲突等一系列问题。
+ * 
 从核心1.10.2开始,不再进行权限认证。
  *
  * @author ForteScarlet <[163邮箱地址]ForteScarlet@163.com>
  * @date Created in 2019/3/8 14:55
@@ -27,7 +26,7 @@ public enum CQCodeTypes {
     /**
      * 默认的未知类型,当无法获取或解析的时候将会使用此类型
      */
-    defaultType("", new String[0], new String[0], new String[0], -99),
+    defaultType("?", new String[0], new String[0], new String[0], -99),
 
     /**
      * [CQ:face,id={1}] - QQ表情
@@ -301,7 +300,7 @@ public static CQCodeTypes getTypeByFunction(String function) {
 
     /**
      * 根据类型和参数名称列表来获取一个具体的枚举类型对象实例
-     *
+     * 1.10.2: 如果function只存在一个,直接返回,否则再通过参数判断
      * @param function   function 值, 即类型,例如"image"或者"share"之类的
      * @param paramNames 参数列表值,需要保证顺序
      * @return CQCodeTypes实例对象, 如果没有则会返回defaultType
@@ -311,9 +310,10 @@ public static CQCodeTypes getTypeByFunctionAndParams(String function, String...
         CQCodeTypes[] cqCodeTypes = AllCQCodeTypeMap.get(function);
         if (cqCodeTypes == null || cqCodeTypes.length == 0) {
             return defaultType;
-        } else {
+        } else if(cqCodeTypes.length == 1) {
+            return cqCodeTypes[0];
+        } else{
             // 筛选paramNames
-
             // 如果不为null,则遍历并匹配参数match
             for (CQCodeTypes type : cqCodeTypes) {
                 // 根据type筛选params并匹配
@@ -527,7 +527,6 @@ public boolean match(String text) {
      * 查看某个字符串中是否存在此类型的CQ码
      */
     public boolean contains(String text) {
-//        return text.matches(".*" + this.matchRegex + ".*");
         return matchRegexPattern.matcher(text).find();
     }
 
@@ -581,6 +580,7 @@ public static CQCodeTypes[] getCQCodeTypesByFunction(String function) {
         return Arrays.copyOf(cqCodeTypes, cqCodeTypes.length);
     }
 
+
     /**
      * 判断是否为两个等值的CQ码
      */
diff --git a/src/main/java/com/forte/qqrobot/exception/ExceptionProcessException.java b/src/main/java/com/forte/qqrobot/exception/ExceptionProcessException.java
new file mode 100644
index 0000000..5cf4f5d
--- /dev/null
+++ b/src/main/java/com/forte/qqrobot/exception/ExceptionProcessException.java
@@ -0,0 +1,75 @@
+package com.forte.qqrobot.exception;
+
+/**
+ * 异常处理中心的异常
+ * exception.exceptionProcess
+ * @author  ForteScarlet 
+ */
+public class ExceptionProcessException extends RobotRuntimeException {
+
+    public ExceptionProcessException() {
+    }
+
+    public ExceptionProcessException(String message, Object... format) {
+        super(message, format);
+    }
+
+    public ExceptionProcessException(String message) {
+        super(message);
+    }
+
+    public ExceptionProcessException(String message, Throwable cause, Object... format) {
+        super(message, cause, format);
+    }
+
+    public ExceptionProcessException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public ExceptionProcessException(Throwable cause) {
+        super(cause);
+    }
+
+    public ExceptionProcessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+        super(message, cause, enableSuppression, writableStackTrace);
+    }
+
+    public ExceptionProcessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Object... format) {
+        super(message, cause, enableSuppression, writableStackTrace, format);
+    }
+
+    /**
+     * 不进行语言国际化转化的构造方法
+     *
+     * @param pointless 无意义参数,填任意值 pointless param
+     * @param message   信息正文
+     */
+    public ExceptionProcessException(int pointless, String message) {
+        super(pointless, message);
+    }
+
+    /**
+     * 不进行语言国际化转化的构造方法
+     *
+     * @param pointless 无意义参数,填任意值 pointless param
+     * @param message   信息正文
+     * @param cause     异常
+     */
+    public ExceptionProcessException(int pointless, String message, Throwable cause) {
+        super(pointless, message, cause);
+    }
+
+    /**
+     * 不进行语言国际化转化的构造方法
+     *
+     * @param pointless          无意义参数,填任意值 pointless param
+     * @param message            信息正文
+     * @param cause              异常
+     * @param enableSuppression  whether or not suppression is enabled
+     *                           or disabled
+     * @param writableStackTrace whether or not the stack trace should
+     */
+    public ExceptionProcessException(int pointless, String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+        super(pointless, message, cause, enableSuppression, writableStackTrace);
+    }
+}
diff --git a/src/main/java/com/forte/qqrobot/exception/NoSuchExceptionHandleException.java b/src/main/java/com/forte/qqrobot/exception/NoSuchExceptionHandleException.java
new file mode 100644
index 0000000..1135e01
--- /dev/null
+++ b/src/main/java/com/forte/qqrobot/exception/NoSuchExceptionHandleException.java
@@ -0,0 +1,73 @@
+package com.forte.qqrobot.exception;
+
+/**
+ * 没有合适的异常处理器的异常。
+ * @author  ForteScarlet 
+ */
+public class NoSuchExceptionHandleException extends RobotException {
+    public NoSuchExceptionHandleException() {
+    }
+
+    public NoSuchExceptionHandleException(String message) {
+        super(message);
+    }
+
+    public NoSuchExceptionHandleException(String message, Object... format) {
+        super(message, format);
+    }
+
+    public NoSuchExceptionHandleException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public NoSuchExceptionHandleException(String message, Throwable cause, Object... format) {
+        super(message, cause, format);
+    }
+
+    public NoSuchExceptionHandleException(Throwable cause) {
+        super(cause);
+    }
+
+    public NoSuchExceptionHandleException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+        super(message, cause, enableSuppression, writableStackTrace);
+    }
+
+    public NoSuchExceptionHandleException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Object... format) {
+        super(message, cause, enableSuppression, writableStackTrace, format);
+    }
+
+    /**
+     * 不进行语言国际化转化的构造方法
+     *
+     * @param pointless 无意义参数,填任意值 pointless param
+     * @param message   信息正文
+     */
+    public NoSuchExceptionHandleException(int pointless, String message) {
+        super(pointless, message);
+    }
+
+    /**
+     * 不进行语言国际化转化的构造方法
+     *
+     * @param pointless 无意义参数,填任意值 pointless param
+     * @param message   信息正文
+     * @param cause     异常
+     */
+    public NoSuchExceptionHandleException(int pointless, String message, Throwable cause) {
+        super(pointless, message, cause);
+    }
+
+    /**
+     * 不进行语言国际化转化的构造方法
+     *
+     * @param pointless          无意义参数,填任意值 pointless param
+     * @param message            信息正文
+     * @param cause              异常
+     * @param enableSuppression  whether or not suppression is enabled
+     *                           or disabled
+     * @param writableStackTrace whether or not the stack trace should
+     */
+    public NoSuchExceptionHandleException(int pointless, String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+        super(pointless, message, cause, enableSuppression, writableStackTrace);
+    }
+}
diff --git a/src/main/java/com/forte/qqrobot/listener/error/ExceptionHandle.java b/src/main/java/com/forte/qqrobot/listener/error/ExceptionHandle.java
new file mode 100644
index 0000000..cbd745c
--- /dev/null
+++ b/src/main/java/com/forte/qqrobot/listener/error/ExceptionHandle.java
@@ -0,0 +1,20 @@
+package com.forte.qqrobot.listener.error;
+
+import com.forte.qqrobot.beans.messages.msgget.MsgGet;
+import com.forte.qqrobot.sender.MsgSender;
+
+/**
+ * 异常处理器接口
+ * @author  ForteScarlet 
+ */
+public interface ExceptionHandle {
+
+    /**
+     * 进行异常处理
+     * @param context 异常捕获对象封装
+     * @return  异常处理后的响应结果。
+     */
+    Object handle(ExceptionHandleContext context);
+
+
+}
diff --git a/src/main/java/com/forte/qqrobot/listener/error/ExceptionHandleContext.java b/src/main/java/com/forte/qqrobot/listener/error/ExceptionHandleContext.java
new file mode 100644
index 0000000..399938e
--- /dev/null
+++ b/src/main/java/com/forte/qqrobot/listener/error/ExceptionHandleContext.java
@@ -0,0 +1,18 @@
+package com.forte.qqrobot.listener.error;
+
+import com.forte.qqrobot.beans.messages.msgget.MsgGet;
+import com.forte.qqrobot.sender.MsgSender;
+
+/**
+ * 异常处理上下文,提供一些捕获到异常时候的参数。其不属于常见的上下文对象。
+ * 提供一个实现类{@link ExceptionHandleContextImpl}
+ * @author  ForteScarlet 
+ */
+public interface ExceptionHandleContext {
+
+    String getId();
+    MsgGet getMsgGet();
+    MsgSender getMsgSender();
+    Exception getException();
+
+}
diff --git a/src/main/java/com/forte/qqrobot/listener/error/ExceptionHandleContextImpl.java b/src/main/java/com/forte/qqrobot/listener/error/ExceptionHandleContextImpl.java
new file mode 100644
index 0000000..85c093d
--- /dev/null
+++ b/src/main/java/com/forte/qqrobot/listener/error/ExceptionHandleContextImpl.java
@@ -0,0 +1,57 @@
+package com.forte.qqrobot.listener.error;
+
+import com.forte.qqrobot.beans.messages.msgget.MsgGet;
+import com.forte.qqrobot.sender.MsgSender;
+
+/**
+ * @author  ForteScarlet 
+ */
+public class ExceptionHandleContextImpl implements ExceptionHandleContext {
+    private String id;
+    private MsgGet msgGet;
+    private MsgSender msgSender;
+    private Exception exception;
+
+    public ExceptionHandleContextImpl(String id, MsgGet msgGet, MsgSender msgSender, Exception exception) {
+        this.id = id;
+        this.msgGet = msgGet;
+        this.msgSender = msgSender;
+        this.exception = exception;
+    }
+
+    @Override
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    @Override
+    public MsgGet getMsgGet() {
+        return msgGet;
+    }
+
+    public void setMsgGet(MsgGet msgGet) {
+        this.msgGet = msgGet;
+    }
+
+    @Override
+    public MsgSender getMsgSender() {
+        return msgSender;
+    }
+
+    public void setMsgSender(MsgSender msgSender) {
+        this.msgSender = msgSender;
+    }
+
+    @Override
+    public Exception getException() {
+        return exception;
+    }
+
+    public void setException(Exception exception) {
+        this.exception = exception;
+    }
+}
diff --git a/src/main/java/com/forte/qqrobot/listener/error/ExceptionProcessCenter.java b/src/main/java/com/forte/qqrobot/listener/error/ExceptionProcessCenter.java
new file mode 100644
index 0000000..bf56ec9
--- /dev/null
+++ b/src/main/java/com/forte/qqrobot/listener/error/ExceptionProcessCenter.java
@@ -0,0 +1,193 @@
+package com.forte.qqrobot.listener.error;
+
+import com.forte.qqrobot.anno.ExceptionCatch;
+import com.forte.qqrobot.beans.messages.msgget.MsgGet;
+import com.forte.qqrobot.exception.ExceptionProcessException;
+import com.forte.qqrobot.exception.NoSuchExceptionHandleException;
+import com.forte.qqrobot.log.QQLogLang;
+import com.forte.qqrobot.sender.MsgSender;
+import com.forte.qqrobot.utils.AnnotationUtils;
+import com.forte.qqrobot.utils.FieldUtils;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 
 异常处理中心
+ * 
 当{@link com.forte.qqrobot.listener.invoker.ListenerMethod} 执行method出现异常的时候,便会尝试捕获并处理。
+ * 
 每一种异常类型只能注册一个处理器,未知的异常会尝试获取一个父类异常,如果能获取,则处理,否则最终不做处理。
+ * 
 如果存在{@link Exception} 与 {@link RuntimeException} 处理器,则它们永远是最后被尝试使用的。
+ * 
 理论上,此类在构建好后,内部的处理器不应再发生变动。
+ *
+ * @author  ForteScarlet 
+ */
+public class ExceptionProcessCenter {
+
+    private static final QQLogLang LOG_LANG = new QQLogLang("exception.process.center");
+
+    /**
+     * Exception类的处理器, 如果没有则为null
+     */
+    private ExceptionHandle exHandle;
+
+    /**
+     * RuntimeException类的处理器, 如果没有则为null
+     */
+    private ExceptionHandle runtimeExHandle;
+
+    /**
+     * 其他 handles 异常处理器记录
+     */
+    private final Map, ExceptionHandle> HANDLES;
+
+    /**
+     * 缓存记录尝试获取的时候获取不到的类型。由于此类的HANDLES不变,则获取一次没有获取到的则必然不会存在
+     */
+    private final Set> NULL_TYPE;
+
+    /**
+     * 构造
+     *
+     * @param handles 处理器合集
+     */
+    private ExceptionProcessCenter(Map, ExceptionHandle> handles) {
+        this.HANDLES = new ConcurrentHashMap<>(handles);
+        this.NULL_TYPE = new HashSet<>(4);
+        // 尝试取出ex handle
+        this.exHandle = HANDLES.remove(Exception.class);
+        // 尝试取出run ex handle
+        this.runtimeExHandle = HANDLES.remove(RuntimeException.class);
+    }
+
+    /**
+     * 工厂方法,直接使用构造
+     *
+     * @param handles handles
+     * @return 实例对象
+     */
+    public static ExceptionProcessCenter getInstance(Map, ExceptionHandle> handles) {
+        return new ExceptionProcessCenter(handles);
+    }
+
+    /**
+     * 构建一个空的实例
+     *
+     * @return 实例对象
+     */
+    public static ExceptionProcessCenter getInstance() {
+        return new ExceptionProcessCenter(new HashMap<>(1));
+    }
+
+    /**
+     * 根据ExceptionHandle集来构建结果。
+     * 需要Exception Handle上存在{@link com.forte.qqrobot.anno.ExceptionCatch} 注解
+     * 如果不存在,则默认为处理{@link Exception}异常
+     * 一般来讲数量不会很多
+     *
+     * @param handles handles
+     * @return 实例对象
+     */
+    public static ExceptionProcessCenter getInstance(ExceptionHandle... handles) {
+        if (handles.length == 0) {
+            return getInstance();
+        }
+        // 准备一个数据储存
+        Map, ExceptionHandle> map = new HashMap<>(handles.length >> 1);
+
+        Class[] defaultClasses = new Class[]{Exception.class};
+
+        // 遍历, 获取注解
+        for (ExceptionHandle handle : handles) {
+            final ExceptionCatch catchAnnotation = AnnotationUtils.getAnnotation(handle.getClass(), ExceptionCatch.class);
+            // 获取classes
+            Class[] classes = catchAnnotation == null ? defaultClasses : catchAnnotation.value();
+
+            // 遍历并添加
+            for (Class clazz : classes) {
+                map.merge(clazz, handle, (old, val) -> {
+                    throw new ExceptionProcessException("exists", clazz, old.getClass(), val.getClass());
+                });
+            }
+        }
+
+        // 返回结果
+        return new ExceptionProcessCenter(map);
+    }
+
+
+    /**
+     * 获取某个类型的异常处理器,如果不存在则返回null
+     * 如果是某个类型的子类,但是没有直接的类,最终在获取后会将此类型也进行缓存
+     *
+     * @param exType 异常类型
+     */
+    public ExceptionHandle getHandle(Class exType) {
+        Objects.requireNonNull(exType);
+
+        // 如果是记录在案的NULL,则直接返回null
+        if (NULL_TYPE.contains(exType)) {
+            return null;
+        }
+
+        // 如果就是ex
+        if (exType == Exception.class) {
+            return exHandle;
+        }
+        // 如果是runtime类型
+        if (exType == RuntimeException.class) {
+            if (runtimeExHandle != null) {
+                return runtimeExHandle;
+            } else if (exHandle != null) {
+                // 缓存并返回
+                return runtimeExHandle = exHandle;
+            }
+        }
+
+        // 直接获取
+        final ExceptionHandle handle = HANDLES.get(exType);
+        if (handle != null) {
+            return handle;
+        }
+
+        final Set> keys = HANDLES.keySet();
+        for (Class handleTypes : keys) {
+            if (FieldUtils.isChild(exType, handleTypes)) {
+                // 找到的第一个父类类型,记录缓存并返回
+                final ExceptionHandle exceptionHandle = HANDLES.get(handleTypes);
+                HANDLES.put(exType, exceptionHandle);
+                return exceptionHandle;
+            }
+        }
+
+        // 没有, 看看有没有runtime
+        if (runtimeExHandle != null && FieldUtils.isChild(exType, RuntimeException.class)) {
+            // 是runtime类型的,返回runtime
+            HANDLES.put(exType, runtimeExHandle);
+            return runtimeExHandle;
+        }
+        // 最后,如果存在Ex,直接返回,毕竟所有人的父类
+        if (exHandle != null) {
+            HANDLES.put(exType, exHandle);
+            return exHandle;
+        }
+
+        // 没有,记录一下
+        synchronized (NULL_TYPE) {
+            NULL_TYPE.add(exType);
+        }
+
+        return null;
+    }
+
+    /**
+     * 对一个异常信息进行处理。如果无法获取到合适的handle,则会抛出一个异常。
+     */
+    public Object doHandle(Exception e, ExceptionHandleContext context) throws NoSuchExceptionHandleException {
+        final ExceptionHandle handle = getHandle(e.getClass());
+        if(handle == null){
+            throw new NoSuchExceptionHandleException(1, e.getClass().toString());
+        }else{
+            return handle.handle(context);
+        }
+    }
+}
diff --git a/src/main/java/com/forte/qqrobot/listener/invoker/ListenerFilter.java b/src/main/java/com/forte/qqrobot/listener/invoker/ListenerFilter.java
index c29f855..a9149fc 100644
--- a/src/main/java/com/forte/qqrobot/listener/invoker/ListenerFilter.java
+++ b/src/main/java/com/forte/qqrobot/listener/invoker/ListenerFilter.java
@@ -11,9 +11,13 @@
 import com.forte.qqrobot.listener.Filterable;
 import com.forte.qqrobot.listener.ListenContext;
 import com.forte.qqrobot.log.QQLogLang;
+import com.forte.qqrobot.utils.CQCodeUtil;
 
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Function;
+import java.util.function.UnaryOperator;
 import java.util.regex.Pattern;
 
 /**
@@ -35,6 +39,36 @@ private static QQLogLang getLog(){
      */
     private static final Map DIY_FILTERS = new ConcurrentHashMap<>(4);
 
+    /**
+     * at判断器, 默认情况下即使用CQ码作为判断
+     */
+    private static volatile AtomicReference> AT_DETECTION_FUNCTION = new AtomicReference<>(msg -> () -> CQCodeUtil.build().isAt(msg));
+
+
+    /**
+     * 获取当前的at判断函数
+     * @return 当前的at判断函数
+     */
+    public static Function getAtDetectionFunction(){
+        return AT_DETECTION_FUNCTION.get();
+    }
+
+    /**
+     * 注册一个新的at判断函数,替换当前函数
+     * @param atDetectionFunction at判断函数
+     */
+    public static void registerAtDetectionFunction(Function atDetectionFunction){
+        AT_DETECTION_FUNCTION.updateAndGet(old -> atDetectionFunction);
+    }
+
+    /**
+     * 根据当前的at判断函数来更新一个at判断函数
+     * @param updateFunction at判断函数的更新函数
+     */
+    public static void updateAtDetectionFunction(UnaryOperator> updateFunction){
+        AT_DETECTION_FUNCTION.updateAndGet(updateFunction);
+    }
+
     /**
      * 注册一个自定义的filter
      *
diff --git a/src/main/java/com/forte/qqrobot/listener/invoker/ListenerManager.java b/src/main/java/com/forte/qqrobot/listener/invoker/ListenerManager.java
index 763d49f..dfcd0c0 100644
--- a/src/main/java/com/forte/qqrobot/listener/invoker/ListenerManager.java
+++ b/src/main/java/com/forte/qqrobot/listener/invoker/ListenerManager.java
@@ -226,7 +226,9 @@ private Object[] getParams(MsgGet msgGet){
         CQCodeUtil cqCodeUtil = ResourceDispatchCenter.getCQCodeUtil();
         //判断是否at自己
         //获取本机QQ号
-        AtDetection atDetection = () -> cqCodeUtil.isAt(msg, code);
+        // 获取AT判断函数
+        final Function atDetectionFunction = ListenerFilter.getAtDetectionFunction();
+        AtDetection atDetection = atDetectionFunction.apply(msgGet);
         //0 : msgGet
         plist.add(msgGet);
         //1 : codeUtil
diff --git a/src/main/java/com/forte/qqrobot/utils/CQCodeUtil.java b/src/main/java/com/forte/qqrobot/utils/CQCodeUtil.java
index dc397ea..4214d55 100644
--- a/src/main/java/com/forte/qqrobot/utils/CQCodeUtil.java
+++ b/src/main/java/com/forte/qqrobot/utils/CQCodeUtil.java
@@ -3,6 +3,7 @@
 import com.forte.qqrobot.beans.cqcode.AppendList;
 import com.forte.qqrobot.beans.cqcode.CQAppendList;
 import com.forte.qqrobot.beans.cqcode.CQCode;
+import com.forte.qqrobot.beans.messages.msgget.MsgGet;
 import com.forte.qqrobot.beans.types.CQCodeTypes;
 import com.forte.qqrobot.exception.CQParseException;
 
@@ -58,9 +59,9 @@ private static StringJoiner getCQCodeJoiner(){
      */
     public String escapeOutCQCode(String msgOutCQCode){
         return msgOutCQCode == null ? null : msgOutCQCode
-                .replaceAll("\\&" , "&")
-                .replaceAll("\\[" , "[")
-                .replaceAll("\\]" , "]")
+                .replace("&" , "&")
+                .replace("[" , "[")
+                .replace("]" , "]")
                 ;
     }
 
@@ -73,9 +74,9 @@ public String escapeOutCQCode(String msgOutCQCode){
      */
     public String escapeOutCQCodeDecode(String noCQCodeMsg){
         return noCQCodeMsg == null ? null : noCQCodeMsg
-                .replaceAll("\\&mp\\;" , "&")
-                .replaceAll("\\&91\\;" , "[")
-                .replaceAll("\\&93\\;" , "]")
+                .replace("∓" , "&")
+                .replace("&91;" , "[")
+                .replace("&93;" , "]")
                 ;
     }
 
@@ -93,10 +94,10 @@ public String escapeOutCQCodeDecode(String noCQCodeMsg){
      */
     public String escapeValue(String value){
         return value == null ? null : value
-                .replaceAll("\\&" , "&")
-                .replaceAll("\\[" , "[")
-                .replaceAll("\\]" , "]")
-                .replaceAll("\\," , ",")
+                .replace("&" , "&")
+                .replace("[" , "[")
+                .replace("]" , "]")
+                .replace("," , ",")
                 ;
     }
 
@@ -107,10 +108,10 @@ public String escapeValue(String value){
      */
     public String escapeValueDecode(String value){
         return value == null ? null : value
-                .replaceAll("\\&" , "&")
-                .replaceAll("\\[" , "[")
-                .replaceAll("\\]" , "]")
-                .replaceAll("\\," , ",")
+                .replace("&" , "&")
+                .replace("[" , "[")
+                .replace("]" , "]")
+                .replace("," , ",")
                 ;
     }
 
@@ -664,6 +665,20 @@ public boolean isAt(String msg, String qq){
         return msg.contains(getCQCode_at(qq));
     }
 
+    /**
+     * 判断是否存在at当前code。
+     * 如果其中的thisCode为null,则永远返回false
+     * @return 是否at了某个qq
+     */
+    public boolean isAt(MsgGet msg){
+        if(msg == null || msg.getThisCode() == null){
+            return false;
+        }
+        //如果存在at的CQ码并且参数‘qq’是某个qq
+        final String at = getCQCode_at(msg.getThisCode());
+        return msg.getMsg().contains(at);
+    }
+
     /**
      * 判断是否存在at某个qq
      * @return 是否at了某个qq
diff --git a/src/main/resources/lang/core/en_US.lang b/src/main/resources/lang/core/en_US.lang
index 5948426..c8eed88 100644
--- a/src/main/resources/lang/core/en_US.lang
+++ b/src/main/resources/lang/core/en_US.lang
@@ -2,6 +2,9 @@ lang.init.finished=language has already init finished. All non-Chinese languages
 
 
 # 异常相关
+# exception.exceptionProcess
+exception.exceptionProcess.exists=Processing already exists for exception type [{0}]! There cannot be more than one exception handler of the same type: {1}, {2}
+
 
 # RobotRunException相关
 exception.robotRun.moreDepends=Scan to multiple [{0}] implementation classes.
diff --git a/src/main/resources/lang/core/zh_CN.lang b/src/main/resources/lang/core/zh_CN.lang
index c8d67da..0cd849c 100644
--- a/src/main/resources/lang/core/zh_CN.lang
+++ b/src/main/resources/lang/core/zh_CN.lang
@@ -2,6 +2,9 @@ lang.init.finished=语言已初始化完毕。
 
 # 异常相关
 
+# exception.exceptionProcess
+exception.exceptionProcess.exists=已存在针对异常类型[{0}]的处理!同一个类型的异常处理不能存在多个: {1}, {2}
+
 # RobotRunException相关
 exception.robotRun.moreDepends=扫描到多个[ {0} ]的实现类。
 exception.robotRun.runtime.init.failed=Runtime初始化异常
diff --git a/update.md b/update.md
index 05c5b94..1bf7ab4 100644
--- a/update.md
+++ b/update.md
@@ -1,5 +1,8 @@
 ## 版本更新记录
 
+## 1.10.2
+- 
+
 ## 1.10.1
 - 修复由于`@Filter`过滤机制的变动而导致`KeywordMatchTypeFactory`工厂创建的枚举无法使用的BUG。