目录
当你看清了一个人而不揭穿,你就懂得了格局的意义,当你讨厌一个人而不翻脸,你就明白了释然的重要性。活着,总有你看不惯的事,也有看不惯的人。 茶不过两个姿态,沉,浮,饮茶人不过两种姿态,拿起,放下。人生如茶,沉时坦然,浮时淡然,拿的起,也要放的下。
—— 杨绛
日志
Mybatis提供了一个日志接口
/**
* mybatis 的日志接口,提供日志级别
* <ol>
* <li>error</li>
* <li>debug</li>
* <li>trace</li>
* <li>warn</li>
* </ol>
* <p>通过自己定义的接口来实现各大日志框架的内容达到高可用</p>
*/
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
提供
/**
* <p>日志工厂,实现内容:</p>
* <ol>
* <li>org.slf4j.Logger 日志框架 slf4j</li>
* <li>org.apache.commons.logging.Log 日志框架 apache</li>
* <li>org.apache.logging.log4j.Logger 日志框架 log4j2</li>
* <li>org.apache.log4j.Logger 日志框架 log4j </li>
* <li>java.util.logging.Logger 日志框架,JDK的logger</li>
*
* </ol>
* @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
是一个单例对象,使用方式:
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<String, Invoker> setMethods = new HashMap<>();
/**
* get 方法列表
*/
private final Map<String, Invoker> getMethods = new HashMap<>();
/**
* set 的数据类型
*/
private final Map<String, Class<?>> setTypes = new HashMap<>();
/**
* get 的数据类型
*/
private final Map<String, Class<?>> getTypes = new HashMap<>();
/**
* 构造函数
*/
private Constructor<?> defaultConstructor;
/**
* 缓存数据, 大写KEY
*/
private Map<String, String> 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);
}
}
addGetMethods
该方法获取了所有get
和is
开头的方法
private void addGetMethods(Class<?> clazz) {
// 反射方法
Map<String, List<Method>> 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);
}
getClassMethods
org.apache.ibatis.reflection.Reflector#getClassMethods
,该方法将传入对象的所有可见方法都获取到进行唯一标识处理成一个Map
对象,添加方法为org.apache.ibatis.reflection.Reflector#addUniqueMethods
private Method[] getClassMethods(Class<?> clazz) {
// 方法唯一标识: 方法
Map<String, Method> 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<Method> methods = uniqueMethods.values();
return methods.toArray(new Method[0]);
}
org.apache.ibatis.reflection.Reflector#addUniqueMethods
private void addUniqueMethods(Map<String, Method> 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();
}
resolveGetterConflicts
org.apache.ibatis.reflection.Reflector#resolveGetterConflicts
这个方法解决了get
方法的冲突问题,同名方法不同返回值
private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
for (Entry<String, List<Method>> 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);
}
}
addFields
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());
}
}
DataSourceFactory
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);
JndiDataSourceFactory
/**
* @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<Object, Object> entry : allProps.entrySet()) {
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;
}
}
PooledDataSource
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;
PooledDataSourceFactory
public class PooledDataSourceFactory extends UnpooledDataSourceFactory {
public PooledDataSourceFactory() {
this.dataSource = new PooledDataSource();
}
}
// 初始化
public PooledDataSource() {
dataSource = new UnpooledDataSource();
}
UnpooledDataSourceFactory
@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);
}
}
UnpooledDataSource
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 标签
* <dataSource type="POOLED">
* <property name="driver" value="com.mysql.jdbc.Driver"/>
* <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
* <property name="username" value="root"/>
* <property name="password" value="root"/>
* </dataSource>
*
* @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.");
}