001/* 002 * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.mybatisflex.core.audit; 017 018import com.mybatisflex.core.FlexConsts; 019import org.apache.ibatis.mapping.BoundSql; 020import org.apache.ibatis.mapping.ParameterMapping; 021import org.apache.ibatis.mapping.ParameterMode; 022import org.apache.ibatis.reflection.MetaObject; 023import org.apache.ibatis.reflection.ParamNameResolver; 024import org.apache.ibatis.session.Configuration; 025import org.apache.ibatis.type.TypeHandlerRegistry; 026 027import java.sql.SQLException; 028import java.util.Collection; 029import java.util.List; 030import java.util.Map; 031 032/** 033 * 审计管理器,统一执行如何和配置入口 034 */ 035public class AuditManager { 036 037 private static MessageFactory messageFactory = new DefaultMessageFactory(); 038 039 private static boolean auditEnable = false; 040 private static Clock clock = System::currentTimeMillis; 041 private AuditManager() {} 042 private static MessageCollector messageCollector = new ScheduledMessageCollector(); 043 044 public static boolean isAuditEnable() { 045 return auditEnable; 046 } 047 048 public static void setAuditEnable(boolean auditEnable) { 049 AuditManager.auditEnable = auditEnable; 050 } 051 052 public static Clock getClock() { 053 return clock; 054 } 055 056 public static void setClock(Clock clock) { 057 AuditManager.clock = clock; 058 } 059 060 public static MessageFactory getMessageFactory() { 061 return messageFactory; 062 } 063 064 public static void setMessageFactory(MessageFactory messageFactory) { 065 AuditManager.messageFactory = messageFactory; 066 } 067 068 public static MessageCollector getMessageCollector() { 069 return messageCollector; 070 } 071 072 073 public static void setMessageReporter(MessageReporter messageReporter) { 074 MessageCollector newMessageCollector = new ScheduledMessageCollector(10, messageReporter); 075 setMessageCollector(newMessageCollector); 076 } 077 078 public static void setMessageCollector(MessageCollector messageCollector) { 079 MessageCollector temp = AuditManager.messageCollector; 080 AuditManager.messageCollector = messageCollector; 081 releaseScheduledMessageCollector(temp); 082 083 } 084 085 private static void releaseScheduledMessageCollector(MessageCollector messageCollector) { 086 if (messageCollector instanceof ScheduledMessageCollector) { 087 ((ScheduledMessageCollector) messageCollector).release(); 088 } 089 } 090 091 public static <T> T startAudit(AuditRunnable<T> supplier, BoundSql boundSql, Configuration configuration) throws SQLException { 092 AuditMessage auditMessage = messageFactory.create(); 093 if (auditMessage == null) { 094 return supplier.execute(); 095 } 096 auditMessage.setQueryTime(clock.getTick()); 097 try { 098 T result = supplier.execute(); 099 if (result instanceof Collection) { 100 auditMessage.setQueryCount(((Collection) result).size()); 101 } else if (result != null) { 102 auditMessage.setQueryCount(1); 103 } 104 return result; 105 } finally { 106 auditMessage.setElapsedTime(clock.getTick() - auditMessage.getQueryTime()); 107 auditMessage.setQuery(boundSql.getSql()); 108 Object parameter = boundSql.getParameterObject(); 109 110 /** parameter 的组装请查看 getNamedParams 方法 111 * @see ParamNameResolver#getNamedParams(Object[]) 112 */ 113 if (parameter instanceof Map) { 114 TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); 115 if (((Map<?, ?>) parameter).containsKey(FlexConsts.SQL_ARGS)) { 116 auditMessage.addParams(((Map<?, ?>) parameter).get(FlexConsts.SQL_ARGS)); 117 } else if (((Map<?, ?>) parameter).containsKey("collection")) { 118 Collection collection = (Collection) ((Map<?, ?>) parameter).get("collection"); 119 auditMessage.addParams(collection.toArray()); 120 } else if (((Map<?, ?>) parameter).containsKey("array")) { 121 auditMessage.addParams(((Map<?, ?>) parameter).get("array")); 122 } else { 123 List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); 124 for (ParameterMapping parameterMapping : parameterMappings) { 125 if (parameterMapping.getMode() != ParameterMode.OUT) { 126 Object value; 127 String propertyName = parameterMapping.getProperty(); 128 if (boundSql.hasAdditionalParameter(propertyName)) { 129 value = boundSql.getAdditionalParameter(propertyName); 130 } else if (typeHandlerRegistry.hasTypeHandler(parameter.getClass())) { 131 value = parameter; 132 } else { 133 MetaObject metaObject = configuration.newMetaObject(parameter); 134 value = metaObject.getValue(propertyName); 135 } 136 auditMessage.addParams(value); 137 } 138 } 139 } 140 } 141 messageCollector.collect(auditMessage); 142 } 143 } 144 145 146 @FunctionalInterface 147 public interface AuditRunnable<T> { 148 T execute() throws SQLException; 149 } 150 151}