目录
mybatis 提供了一个日志接口
- /**
- * mybatis 的日志接口,提供日志级别
- *
- *
- error
- *
- debug
- *
- trace
- *
- warn
- *
- *
通过自己定义的接口来实现各大日志框架的内容达到高可用
- * @author Clinton Begin
- */
- public interface Log {
-
- boolean isDebugEnabled();
-
- boolean isTraceEnabled();
-
- void error(String s, Throwable e);
-
- void error(String s);
-
- void debug(String s);
-
- void trace(String s);
-
- void warn(String s);
- }
有了日志接口必然有实现类,mybatis有log4j2 、 slf4j 等日志的相关实现 , 下面是Slf4j实现的代码,其他代码也是一样的模式进行初始化。
- public class Slf4jImpl implements Log {
-
- private Log log;
-
- /**
- * 创建日志实例
- * @param clazz
- */
- public Slf4jImpl(String clazz) {
- Logger logger = LoggerFactory.getLogger(clazz);
-
- if (logger instanceof LocationAwareLogger) {
- try {
- // check for slf4j >= 1.6 method signature
- logger.getClass().getMethod("log", Marker.class, String.class, int.class, String.class, Object[].class, Throwable.class);
- log = new Slf4jLocationAwareLoggerImpl((LocationAwareLogger) logger);
- return;
- } catch (SecurityException | NoSuchMethodException e) {
- // fail-back to Slf4jLoggerImpl
- }
- }
-
- // Logger is not LocationAwareLogger or slf4j version < 1.6
- log = new Slf4jLoggerImpl(logger);
- }
-
- @Override
- public boolean isDebugEnabled() {
- return log.isDebugEnabled();
- }
-
- @Override
- public boolean isTraceEnabled() {
- return log.isTraceEnabled();
- }
-
- @Override
- public void error(String s, Throwable e) {
- log.error(s, e);
- }
-
- @Override
- public void error(String s) {
- log.error(s);
- }
-
- @Override
- public void debug(String s) {
- log.debug(s);
- }
-
- @Override
- public void trace(String s) {
- log.trace(s);
- }
-
- @Override
- public void warn(String s) {
- log.warn(s);
- }
-
- }
通过上述方法来达到统一接口多个实现,这个在开发中也经常使用.多日志的实现方法有了还缺一个创建方法,创建方法由org.apache.ibatis.logging.LogFactory提供
- /**
- *
日志工厂,实现内容:
- *
- *
- org.slf4j.Logger 日志框架 slf4j
- *
- org.apache.commons.logging.Log 日志框架 apache
- *
- org.apache.logging.log4j.Logger 日志框架 log4j2
- *
- org.apache.log4j.Logger 日志框架 log4j
- *
- java.util.logging.Logger 日志框架,JDK的logger
- *
- *
- * @author Clinton Begin
- * @author Eduardo Macarron
- */
- public final class LogFactory {
-
- /**
- * Marker to be used by logging implementations that support markers.
- */
- public static final String MARKER = "MYBATIS";
-
- private static Constructor extends Log> logConstructor;
-
- /**
- * 日志的实现类的具体选择
- */
- static {
- // slf4j 日志
- tryImplementation(LogFactory::useSlf4jLogging);
- // apache 日志
- tryImplementation(LogFactory::useCommonsLogging);
- // log4j2 日志
- tryImplementation(LogFactory::useLog4J2Logging);
- // log4 日志
- tryImplementation(LogFactory::useLog4JLogging);
- // JDK 日志
- tryImplementation(LogFactory::useJdkLogging);
- // 空 日志
- tryImplementation(LogFactory::useNoLogging);
- }
-
- /**
- * 私有化构造方法,这是一个单例
- */
- private LogFactory() {
- // disable construction
- }
-
- public static Log getLog(Class> aClass) {
- return getLog(aClass.getName());
- }
-
- public static Log getLog(String logger) {
- try {
- return logConstructor.newInstance(logger);
- } catch (Throwable t) {
- throw new LogException("Error creating logger for logger " + logger + ". Cause: " + t, t);
- }
- }
-
- public static synchronized void useCustomLogging(Class extends Log> clazz) {
- setImplementation(clazz);
- }
-
- public static synchronized void useSlf4jLogging() {
- setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class);
- }
-
- public static synchronized void useCommonsLogging() {
- setImplementation(org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl.class);
- }
-
- public static synchronized void useLog4JLogging() {
- setImplementation(org.apache.ibatis.logging.log4j.Log4jImpl.class);
- }
-
- public static synchronized void useLog4J2Logging() {
- setImplementation(org.apache.ibatis.logging.log4j2.Log4j2Impl.class);
- }
-
- public static synchronized void useJdkLogging() {
- setImplementation(org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl.class);
- }
-
- public static synchronized void useStdOutLogging() {
- setImplementation(org.apache.ibatis.logging.stdout.StdOutImpl.class);
- }
-
- public static synchronized void useNoLogging() {
- setImplementation(org.apache.ibatis.logging.nologging.NoLoggingImpl.class);
- }
-
- /**
- * 选择具体的日志实现
- */
- private static void tryImplementation(Runnable runnable) {
- if (logConstructor == null) {
- try {
- // run()? 似乎违背了代码的语义, 看静态方法.静态方法多行同类型的操作我认为是一个多线程
- runnable.run();
- } catch (Throwable t) {
- // ignore
- }
- }
- }
-
- /**
- * 选择具体的日志实现
- */
- private static void setImplementation(Class extends Log> implClass) {
- try {
- Constructor extends Log> candidate = implClass.getConstructor(String.class);
- Log log = candidate.newInstance(LogFactory.class.getName());
- if (log.isDebugEnabled()) {
- log.debug("Logging initialized using '" + implClass + "' adapter.");
- }
- logConstructor = candidate;
- } catch (Throwable t) {
- throw new LogException("Error setting Log implementation. Cause: " + t, t);
- }
- }
-
- }
LogFactory是一个单例对象,对外公开getLog方法在使用时直接private static final Log log = LogFactory.getLog(CglibProxyFactory.class);即可
在 org.apache.ibatis.session.Configuration 中可以看到下面这些注册方法
- // 日志实现类
- typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
- typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
- typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
- typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
- typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
- typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
- typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
mybatis 的反射相关内容在org.apache.ibatis.reflection 下存放。主要讲解org.apache.ibatis.reflection.Reflector类,先看一下该类的属性
- public class Reflector {
-
- /**
- * 实体类.class
- */
- private final Class> type;
- /**
- * 可读 属性
- */
- private final String[] readablePropertyNames;
- /**
- * 可写 属性值
- */
- private final String[] writablePropertyNames;
- /**
- * set 方法列表
- */
- private final Map
setMethods = new HashMap<>(); - /**
- * get 方法列表
- */
- private final Map
getMethods = new HashMap<>(); - /**
- * set 的数据类型
- */
- private final Map
> setTypes = new HashMap<>(); - /**
- * get 的数据类型
- */
- private final Map
> getTypes = new HashMap<>(); - /**
- * 构造函数
- */
- private Constructor> defaultConstructor;
-
- /**
- * 缓存数据, 大写KEY
- */
- private Map
caseInsensitivePropertyMap = new HashMap<>(); -
- }
构造方法, 构造方法传入一个类的字节码,在构造方法中设置相关的属性值
- public class Reflector {
-
- /**
- * @param clazz 待解析类的字节码
- */
- public Reflector(Class> clazz) {
- type = clazz;
- // 构造方法
- addDefaultConstructor(clazz);
- // get 方法
- addGetMethods(clazz);
- // set 方法
- addSetMethods(clazz);
- // 字段值
- addFields(clazz);
- readablePropertyNames = getMethods.keySet().toArray(new String[0]);
- writablePropertyNames = setMethods.keySet().toArray(new String[0]);
- for (String propName : readablePropertyNames) {
- // 循环操作设置到缓存中,
- caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
- }
- for (String propName : writablePropertyNames) {
- caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
- }
- }
- }
addDefaultConstructor 方法
- private void addDefaultConstructor(Class> clazz) {
-
- // 获取类里面的所有构造方法
- Constructor>[] constructors = clazz.getDeclaredConstructors();
- // 过滤得到空参构造 constructor -> constructor.getParameterTypes().length == 0
- Arrays.stream(constructors).filter(constructor -> constructor.getParameterTypes().length == 0)
- .findAny().ifPresent(constructor -> {
- System.out.println("有空参构造");
- this.defaultConstructor = constructor;
- });
- }
测试类
- public class People {
- private String name;
-
- public People() {
- }
-
- public People(String name) {
- this.name = name;
- }
-
- @Override
- public String toString() {
- return "People{" +
- "name='" + name + '\'' +
- '}';
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
- }
- import org.junit.jupiter.api.Test;
-
- import java.lang.reflect.Constructor;
-
- class HfReflectorTest {
- @Test
- void getDefaultConstructorTest() throws Exception {
- Reflector reflector = new Reflector(People.class);
- // 获取空参构造方法
- Constructor> defaultConstructor = reflector.getDefaultConstructor();
- People o = (People) defaultConstructor.newInstance();
- o.setName("hhh");
-
- System.out.println(o);
- }
- }
org.apache.ibatis.reflection.Reflector#getDefaultConstructor方法, 该方法是获取构造函数的方法,如果构造函数没有就抛出异常
- public Constructor> getDefaultConstructor() {
- if (defaultConstructor != null) {
- return defaultConstructor;
- } else {
- // 如果没有空参构造抛出的异常
- throw new ReflectionException("There is no default constructor for " + type);
- }
- }
该方法获取了所有get和is开头的方法
- private void addGetMethods(Class> clazz) {
- // 反射方法
- Map
> conflictingGetters = new HashMap<>(); - Method[] methods = getClassMethods(clazz);
- // JDK8 filter 过滤get 开头的方法
- Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName()))
- .forEach(m -> addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m));
- resolveGetterConflicts(conflictingGetters);
- }
该方法中依旧使用了 JDK8 语法通过m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName())来判断是否是get或·is开头的内容。调用org.apache.ibatis.reflection.property.PropertyNamer
- public static boolean isGetter(String name) {
- // 在语义上 is 开头的也是get开头的
- return (name.startsWith("get") && name.length() > 3) || (name.startsWith("is") && name.length() > 2);
- }
org.apache.ibatis.reflection.Reflector#getClassMethods,该方法将传入对象的所有可见方法都获取到进行唯一标识处理成一个Map对象 添加方法为org.apache.ibatis.reflection.Reflector#addUniqueMethods
- private Method[] getClassMethods(Class> clazz) {
- // 方法唯一标识: 方法
- Map
uniqueMethods = new HashMap<>(); - Class> currentClass = clazz;
- while (currentClass != null && currentClass != Object.class) {
- // getDeclaredMethods 获取 public ,private , protcted 方法
- addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());
-
- // we also need to look for interface methods -
- // because the class may be abstract
- // 当前类是否继承别的类(实现接口)如果继承则需要进行操作
- Class>[] interfaces = currentClass.getInterfaces();
- for (Class> anInterface : interfaces) {
- // getMethods 获取本身和父类的 public 方法
- addUniqueMethods(uniqueMethods, anInterface.getMethods());
- }
-
- // 循环往上一层一层寻找最后回到 Object 类 的上级为null 结束
- currentClass = currentClass.getSuperclass();
- }
-
- Collection
methods = uniqueMethods.values(); -
- return methods.toArray(new Method[0]);
- }
org.apache.ibatis.reflection.Reflector#addUniqueMethods
- private void addUniqueMethods(Map
uniqueMethods, Method[] methods) { - for (Method currentMethod : methods) {
- // 桥接, 具体还不知道
- // TODO: 2019/12/9 JAVA 桥接方法
- if (!currentMethod.isBridge()) {
- // 方法的唯一标识
- String signature = getSignature(currentMethod);
- // check to see if the method is already known
- // if it is known, then an extended class must have
- // overridden a method
- if (!uniqueMethods.containsKey(signature)) {
- uniqueMethods.put(signature, currentMethod);
- }
- }
- }
- }
唯一标识方法org.apache.ibatis.reflection.Reflector#getSignature
- /**
- * 方法唯一标识,返回值类型#方法名称:参数列表
- *
- * @param method
- * @return
- */
- private String getSignature(Method method) {
- StringBuilder sb = new StringBuilder();
- Class> returnType = method.getReturnType();
- if (returnType != null) {
- sb.append(returnType.getName()).append('#');
- }
- sb.append(method.getName());
- Class>[] parameters = method.getParameterTypes();
- for (int i = 0; i < parameters.length; i++) {
- sb.append(i == 0 ? ':' : ',').append(parameters[i].getName());
- }
- return sb.toString();
- }
org.apache.ibatis.reflection.Reflector#resolveGetterConflicts
这个方法解决了get方法的冲突问题,同名方法不同返回值
- private void resolveGetterConflicts(Map
> conflictingGetters) { - for (Entry
> entry : conflictingGetters.entrySet()) { - Method winner = null;
- String propName = entry.getKey();
- boolean isAmbiguous = false;
- for (Method candidate : entry.getValue()) {
- if (winner == null) {
- winner = candidate;
- continue;
- }
- Class> winnerType = winner.getReturnType();
- Class> candidateType = candidate.getReturnType();
- if (candidateType.equals(winnerType)) {
- if (!boolean.class.equals(candidateType)) {
- isAmbiguous = true;
- break;
- } else if (candidate.getName().startsWith("is")) {
- winner = candidate;
- }
- } else if (candidateType.isAssignableFrom(winnerType)) {
- // OK getter type is descendant
- } else if (winnerType.isAssignableFrom(candidateType)) {
- winner = candidate;
- } else {
- isAmbiguous = true;
- break;
- }
- }
- addGetMethod(propName, winner, isAmbiguous);
- }
- }
org.apache.ibatis.reflection.Reflector#addFields
获取类的所有字段没什么好说的直接递归就可以获取了
- private void addFields(Class> clazz) {
- Field[] fields = clazz.getDeclaredFields();
- for (Field field : fields) {
- if (!setMethods.containsKey(field.getName())) {
- // issue #379 - removed the check for final because JDK 1.5 allows
- // modification of final fields through reflection (JSR-133). (JGB)
- // pr #16 - final static can only be set by the classloader
- int modifiers = field.getModifiers();
- if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
- addSetField(field);
- }
- }
- if (!getMethods.containsKey(field.getName())) {
- addGetField(field);
- }
- }
- if (clazz.getSuperclass() != null) {
- addFields(clazz.getSuperclass());
- }
- }
org.apache.ibatis.datasource.DataSourceFactory
- /**
- * 数据源工厂
- * @author Clinton Begin
- */
- public interface DataSourceFactory {
-
- /**
- * 设置 dataSource 属性
- * @param props
- */
- void setProperties(Properties props);
-
- /**
- * 获取 dataSource
- * @return {@link DataSource}
- */
- DataSource getDataSource();
-
- }
setProperties会将下列标签放入datasource中
- <dataSource type="POOLED">
- <property name="driver" value="com.mysql.jdbc.Driver"/>
- <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false"/>
- <property name="username" value="mybatis"/>
- <property name="password" value="mybatis"/>
- dataSource>
在org.apache.ibatis.session.Configuration中有配置下面三个信息
- typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
- typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
- typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
- /**
- * @author Clinton Begin
- */
- public class JndiDataSourceFactory implements DataSourceFactory {
-
- public static final String INITIAL_CONTEXT = "initial_context";
- public static final String DATA_SOURCE = "data_source";
- public static final String ENV_PREFIX = "env.";
-
- /**
- * 直接 java 数据源
- */
- private DataSource dataSource;
-
- /**
- * 获取数据源的配置信息
- * @param allProps
- * @return
- */
- private static Properties getEnvProperties(Properties allProps) {
- final String PREFIX = ENV_PREFIX;
- Properties contextProperties = null;
- for (Entry
- String key = (String) entry.getKey();
- String value = (String) entry.getValue();
- // 只获取前缀`env`
- if (key.startsWith(PREFIX)) {
- if (contextProperties == null) {
- contextProperties = new Properties();
- }
- // 放入数据
- contextProperties.put(key.substring(PREFIX.length()), value);
- }
- }
- return contextProperties;
- }
-
- /**
- * 设置数据源属性
- * @param properties
- */
- @Override
- public void setProperties(Properties properties) {
- try {
- InitialContext initCtx;
- Properties env = getEnvProperties(properties);
- if (env == null) {
- initCtx = new InitialContext();
- } else {
- initCtx = new InitialContext(env);
- }
-
- if (properties.containsKey(INITIAL_CONTEXT)
- && properties.containsKey(DATA_SOURCE)) {
- // 如果包含`initial_context`和`data_source`
- Context ctx = (Context) initCtx.lookup(properties.getProperty(INITIAL_CONTEXT));
- dataSource = (DataSource) ctx.lookup(properties.getProperty(DATA_SOURCE));
- } else if (properties.containsKey(DATA_SOURCE)) {
- dataSource = (DataSource) initCtx.lookup(properties.getProperty(DATA_SOURCE));
- }
-
- } catch (NamingException e) {
- throw new DataSourceException("There was an error configuring JndiDataSourceTransactionPool. Cause: " + e, e);
- }
- }
-
- @Override
- public DataSource getDataSource() {
- return dataSource;
- }
-
- }
- protected int poolMaximumActiveConnections = 10;
- protected int poolMaximumIdleConnections = 5;
- protected int poolMaximumCheckoutTime = 20000;
- protected int poolTimeToWait = 20000;
- protected int poolMaximumLocalBadConnectionTolerance = 3;
- protected String poolPingQuery = "NO PING QUERY SET";
- protected boolean poolPingEnabled;
- protected int poolPingConnectionsNotUsedFor;
- public class PooledDataSourceFactory extends UnpooledDataSourceFactory {
-
-
- public PooledDataSourceFactory() {
- this.dataSource = new PooledDataSource();
- }
-
- }
-
- // 初始化
- public PooledDataSource() {
- dataSource = new UnpooledDataSource();
- }
- @Override
- public void setProperties(Properties properties) {
- Properties driverProperties = new Properties();
- //metaDataSource 现在是一个dataSource
- MetaObject metaDataSource = SystemMetaObject.forObject(dataSource);
- for (Object key : properties.keySet()) {
- String propertyName = (String) key;
- if (propertyName.startsWith(DRIVER_PROPERTY_PREFIX)) {
- // 如果是 driver. 前缀开头
- String value = properties.getProperty(propertyName);
- driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value);
- } else if (metaDataSource.hasSetter(propertyName)) {
- String value = (String) properties.get(propertyName);
- Object convertedValue = convertValue(metaDataSource, propertyName, value);
- // 通过 metaDataSource 来对 dataSource 进行设置属性
- metaDataSource.setValue(propertyName, convertedValue);
- } else {
- throw new DataSourceException("Unknown DataSource property: " + propertyName);
- }
- }
- if (driverProperties.size() > 0) {
- metaDataSource.setValue("driverProperties", driverProperties);
- }
- }
org.apache.ibatis.datasource.unpooled.UnpooledDataSource主要定义数据库连接相关的一些属性,以及与数据库的链接对象创建
- // 一些配置信息
- private ClassLoader driverClassLoader;
- private Properties driverProperties;
- private String driver;
- private String url;
- private String username;
- private String password;
- private Boolean autoCommit;
- private Integer defaultTransactionIsolationLevel;
- private Integer defaultNetworkTimeout;
初始化连接对象
- /**
- * 加载链接驱动 如 mysql 链接驱动
- * @throws SQLException
- */
- private synchronized void initializeDriver() throws SQLException {
- if (!registeredDrivers.containsKey(driver)) {
- Class> driverType;
- try {
- if (driverClassLoader != null) {
- driverType = Class.forName(driver, true, driverClassLoader);
- } else {
- driverType = Resources.classForName(driver);
- }
- // DriverManager requires the driver to be loaded via the system ClassLoader.
- // http://www.kfu.com/~nsayer/Java/dyn-jdbc.html
- Driver driverInstance = (Driver) driverType.getDeclaredConstructor().newInstance();
- DriverManager.registerDriver(new DriverProxy(driverInstance));
- registeredDrivers.put(driver, driverInstance);
- } catch (Exception e) {
- throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
- }
- }
- }
设置连接对象的属性
- /**
- * 设置连接对象 , 超时时间,是否自动提交事物
- * @param conn
- * @throws SQLException
- */
- private void configureConnection(Connection conn) throws SQLException {
- if (defaultNetworkTimeout != null) {
- conn.setNetworkTimeout(Executors.newSingleThreadExecutor(), defaultNetworkTimeout);
- }
- if (autoCommit != null && autoCommit != conn.getAutoCommit()) {
- conn.setAutoCommit(autoCommit);
- }
- if (defaultTransactionIsolationLevel != null) {
- conn.setTransactionIsolation(defaultTransactionIsolationLevel);
- }
- }
获取连接对象
- /**
- * 获取链接对象
- * @param username
- * @param password
- * @return
- * @throws SQLException
- */
- private Connection doGetConnection(String username, String password) throws SQLException {
- Properties props = new Properties();
- if (driverProperties != null) {
- props.putAll(driverProperties);
- }
- if (username != null) {
- props.setProperty("user", username);
- }
- if (password != null) {
- props.setProperty("password", password);
- }
- return doGetConnection(props);
- }
在 xml 解析的过程中会执行DataSourceFactory相关内容
- /**
- * 解析 dataSourceElement 标签
- *
- *
- *
- *
- *
- *
- *
- * @param context
- * @return
- * @throws Exception
- */
- private DataSourceFactory dataSourceElement(XNode context) throws Exception {
- if (context != null) {
- String type = context.getStringAttribute("type");
- Properties props = context.getChildrenAsProperties();
- //org.apache.ibatis.session.Configuration.Configuration()
- DataSourceFactory factory = (DataSourceFactory) resolveClass(type).getDeclaredConstructor().newInstance();
-
- // PooledDataSourceFactory -> UnpooledDataSourceFactory
- factory.setProperties(props);
- return factory;
- }
- throw new BuilderException("Environment declaration requires a DataSourceFactory.");
- }