当前位置: 首页 > news >正文

手写Mybatis:第8章-把反射用到出神入化

文章目录

  • 一、目标:元对象反射类
  • 二、设计:元对象反射类
  • 三、实现:元对象反射类
    • 3.1 工程结构
    • 3.2 元对象反射类关系图
    • 3.3 反射调用者
      • 3.3.1 统一调用者接口
      • 3.3.2 方法调用者
      • 3.3.3 getter 调用者
      • 3.3.4 setter 调用者
    • 3.4 属性命名和分解标记
      • 3.4.1 属性命名器
      • 3.4.2 属性分解标记
    • 3.5 反射器解耦对象
    • 3.6 元类包装反射器
    • 3.7 对象包装器 Wrapper
      • 3.7.1 对象包装器接口
      • 3.7.2 对象包装器抽象基类
      • 3.7.3 Bean 包装器
      • 3.7.4 Collection 包装器
      • 3.7.5 MapWrapper 包装器
    • 3.8 对象包装工厂
      • 3.8.1 对象包装工厂接口
      • 3.8.2 默认对象包装工厂实现类
    • 3.9 对象工厂
      • 3.9.1 对象工厂接口
      • 3.9.2 默认对象工厂实现类
    • 3.10 元对象封装
      • 3.10.1 元对象
      • 3.10.2 系统级别元对象
    • 3.11 数据源属性设置
      • 3.11.1 无池化数据源工厂
      • 3.11.2 有池化数据源工厂
  • 四、测试:元对象反射类
    • 4.1 单元测试
    • 4.2 反射类测试
      • 4.2.1 学生实体类
      • 4.2.2 老师实体类
      • 4.2.3 反射类测试
  • 五、总结:元对象反射类

一、目标:元对象反射类

💡 在实现数据源池化时,对于属性信息的获取,采用硬编码的方式。如何采取更好的方式呢?

在这里插入图片描述

  • 在数据源获取属性信息时,也就是通过 props.getproperty("driver") 等属性,都是通过手动编码的方式获取的。但是扩展性极差。
  • 如何增强扩张性呢。 Mybatis 的源码,在这里使用了 Mybatis 自己实现的元对象反射工具类,可以完成一个对象的属性的反射填充。
  • MetaObject 元对象反射工具类
    • 元对象、对象包装器、对象工厂、对象包装工厂、Reflector 反射器

二、设计:元对象反射类

💡 什么是元对象反射工具类?

  • 当需要对一个对象的所提供的属性进行统一的设置和获取值的操作。
  • 需要把当前这个被处理的对象解耦,提取出它所有的属性和方法,并按照不同的类型进行反射处理,从而包装成一个工具包。

在这里插入图片描述

  • 整个设计过程都围绕 如何拆解对象并提供反射操作 为主。
    • 对于一个对象来说,它所包括的有对象的构造函数、对象的属性、对象的方法。
    • 而对象的方法因为都是获取和设置值的操作,所以基本都是 get/set 处理,所以需要把这些方法在对象拆解的过程中摘取出来进行保存。
  • 当真正的开始操作时,则会依赖于已经实例化的对象,对其进行属性处理。
    • 而这些处理过程实际都是使用 JDK 所提供的 反射 进行操作的。
    • 而反射过程中的方法名称、入参类型都已经被我们拆解和处理了,最终使用的时候直接调用即可。

三、实现:元对象反射类

3.1 工程结构

mybatis-step-07
|-src|-main|	|-java|		|-com.lino.mybatis|			|-binding|			|	|-MapperMethod.java|			|	|-MapperProxy.java|			|	|-MapperProxyFactory.java|			|	|-MapperRegistry.java|			|-builder|			|	|-xml|			|	|	|-XMLConfigBuilder.java|			|	|-BaseBuilder.java|			|-datasource|			|	|-druid|			|	|	|-DruidDataSourceFacroty.java|			|	|-pooled|			|	|	|-PooledConnection.java|			|	|	|-PooledDataSource.java|			|	|	|-PooledDataSourceFacroty.java|			|	|	|-PoolState.java|			|	|-unpooled|			|	|	|-UnpooledDataSource.java|			|	|	|-UnpooledDataSourceFacroty.java|			|	|-DataSourceFactory.java|			|-executor|			|	|-resultset|			|	|	|-DefaultResultSetHandler.java|			|	|	|-ResultSetHandler.java|			|	|-statement|			|	|	|-BaseStatementHandler.java|			|	|	|-PreparedStatementHandler.java|			|	|	|-SimpleStatementHandler.java|			|	|	|-StatementHandler.java|			|	|-BaseExecutor.java|			|	|-Executor.java|			|	|-SimpleExecutor.java|			|-io|			|	|-Resources.java|			|-mapping|			|	|-BoundSql.java|			|	|-Environment.java|			|	|-MappedStatement.java|			|	|-ParameterMapping.java|			|	|-SqlCommandType.java|			|-reflection|			|	|-factory|			|	|	|-DefaultObjectFactory.java|			|	|	|-ObjectFactory.java|			|	|-invoker|			|	|	|-GetFieldInvoker.java|			|	|	|-Invoker.java|			|	|	|-MethodInvoker.java|			|	|	|-SetFieldInvoker.java|			|	|-property|			|	|	|-PropertyNamer.java|			|	|	|-PropertyTokenizer.java|			|	|-wrapper|			|	|	|-BaseWrapper.java|			|	|	|-BeanWrapper.java|			|	|	|-CollectionWrapper.java|			|	|	|-DefaultObjectWrapperFactory.java|			|	|	|-MapWrapper.java|			|	|	|-ObjectWrapper.java|			|	|	|-ObjectWrapperFactory.java|			|	|-MetaClass.java|			|	|-MetaObject.java|			|	|-Reflector.java|			|	|-SystemMetaObject.java|			|-session|			|	|-defaults|			|	|	|-DefaultSqlSession.java|			|	|	|-DefaultSqlSessionFactory.java|			|	|-Configuration.java|			|	|-ResultHandler.java|			|	|-SqlSession.java|			|	|-SqlSessionFactory.java|			|	|-SqlSessionFactoryBuilder.java|			|	|-TransactionIsolationLevel.java|			|-transaction|			|	|-jdbc|			|	|	|-JdbcTransaction.java|			|	|	|-JdbcTransactionFactory.java|			|	|-Transaction.java|			|	|-TransactionFactory.java|			|-type|			|	|-JdbcType.java|			|	|-TypeAliasRegistry.java|-test|-java|	|-com.lino.mybatis.test|	|-dao|	|	|-IUserDao.java|	|-po|	|	|-Student.java|	|	|-Teacher.java|	|	|-User.java|	|-ApiTest.java|	|-ReflectionTest.java|-resources|-mapper|	|-User_Mapper.xml|-mybatis-config-datasource.xml

3.2 元对象反射类关系图

在这里插入图片描述

  • Reflector 反射器类处理对象类中的 get/set 属性,包装为可调用的 Invoker 反射类。
    • 这样在对 get/set 方法反射调用时,使用方法名称获取对应的 invoke 即可 getGetInvoker(String propertyName)
  • 有了反射器的处理,之后就是对原对象的包装,由 SystemMetaObject 提供创建 MetaObject 元对象的方法。
    • 将我们需要处理的对象进行拆解和 ObjectWrapper 对象包装处理。
    • 因为一个对象的类型还需要进行一条细节的处理,以及属性信息的拆解。
    • 例如:班级[0].学生.成绩 这样的一个类中的关联类的属性,则需要进行递归的方式拆解处理后,才能设置和获取属性值。
  • 最终在 Mybatis 其他的地方,有需要属性值设定时,就可以使用反射工具包进行处理了。

3.3 反射调用者

  • 关于对象类中的属性值获取和设置可以分为 Field 字段的 get/set 还有普通的 Method 的调用。
  • 为了减少使用方的过多处理,这里可以把集中调用者的实现包装成调用策略,统一接口,不同策略,不同的实现类。

3.3.1 统一调用者接口

Invoker.java

package com.lino.mybatis.reflection.invoker;/*** @description: 调用者*/
public interface Invoker {/*** 调用** @param target 目标对象* @param args   对象数组* @return Object* @throws Exception 异常*/Object invoke(Object target, Object[] args) throws Exception;/*** 获取类的类型** @return Class<?>*/Class<?> getType();
}
  • 无论任何类型的反射调用,都离不开对象和入参,只要我们把这两个字段和返回结果定义的通用,就可以包住不同策略的实现类。

3.3.2 方法调用者

MethodInvoker.java

package com.lino.mybatis.reflection.invoker;import java.lang.reflect.Method;/*** @description: 方法调用者*/
public class MethodInvoker implements Invoker {private Class<?> type;private Method method;public MethodInvoker(Method method) {this.method = method;// 如果只有一个参数,返回参数类型,否则返回 return 类型if (method.getParameterTypes().length == 1) {type = method.getParameterTypes()[0];} else {type = method.getReturnType();}}@Overridepublic Object invoke(Object target, Object[] args) throws Exception {return method.invoke(target, args);}@Overridepublic Class<?> getType() {return type;}
}
  • 提供方法反射调用处理,构造函数会传入对应的方法类型。

3.3.3 getter 调用者

GetFieldInvoker.java

package com.lino.mybatis.reflection.invoker;import java.lang.reflect.Field;/*** @description: getter 调用者*/
public class GetFieldInvoker implements Invoker {private Field field;public GetFieldInvoker(Field field) {this.field = field;}@Overridepublic Object invoke(Object target, Object[] args) throws Exception {return field.get(target);}@Overridepublic Class<?> getType() {return field.getType();}
}
  • getter 方法的调用者处理,因为 get 是有返回值的,所以直接对 Field 字段操作完后直接返回结果。

3.3.4 setter 调用者

SetFieldInvoker.java

package com.lino.mybatis.reflection.invoker;import java.lang.reflect.Field;/*** @description: setter 调用者*/
public class SetFieldInvoker implements Invoker {private Field field;public SetFieldInvoker(Field field) {this.field = field;}@Overridepublic Object invoke(Object target, Object[] args) throws Exception {field.set(target, args);return null;}@Overridepublic Class<?> getType() {return field.getType();}
}
  • setter 方法的调用者处理,因为 set 只是设置值,所以这里就是返回一个 null 就可以。

3.4 属性命名和分解标记

3.4.1 属性命名器

PropertyNamer.java

package com.lino.mybatis.reflection.property;import java.util.Locale;/*** @description: 属性命名器*/
public class PropertyNamer {private PropertyNamer() {}/*** 方法转换为属性** @param name 方法名称* @return String*/public static String methodToProperty(String name) {if (name.startsWith("is")) {name = name.substring(2);} else if (name.startsWith("get") || name.startsWith("set")) {name = name.substring(3);} else {throw new RuntimeException("Error parsing property name '" + name + "'. Didn't start with 'is', 'get', 'set'.");}/*如果只有1个字母,转换为小写如果大于1个字母,第二个字母非大写,转换为小写*/if (name.length() == 1 || (name.length() > 1 && !Character.isUpperCase(name.charAt(1)))) {name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);}return name;}/*** 开头判断get/set/is** @param name 名称* @return boolean*/public static boolean isProperty(String name) {return name.startsWith("get") || name.startsWith("set") || name.startsWith("is");}/*** 是否为getter** @param name 名称* @return boolean*/public static boolean isGetter(String name) {return name.startsWith("get") || name.startsWith("is");}/*** 是否为setter** @param name 名称* @return boolean*/public static boolean isSetter(String name) {return name.startsWith("set");}
}

3.4.2 属性分解标记

PropertyTokenizer.java

package com.lino.mybatis.reflection.property;import java.util.Iterator;/*** @description: 属性分解标记*/
public class PropertyTokenizer implements Iterable<PropertyTokenizer>, Iterator<PropertyTokenizer> {// 例子:班级[0].学生.成绩/*** 属性名称:班级*/private String name;/*** 属性对象名称:班级[0]*/private String indexedName;/*** 属性索引:0*/private String index;/*** 子属性:学生*/private String children;public PropertyTokenizer(String fullName) {// 班级[0].学生.成绩:找.int delim = fullName.indexOf(".");if (delim > -1) {name = fullName.substring(0, delim);children = fullName.substring(delim + 1);} else {// 找不到.的话,取全部部分name = fullName;children = null;}indexedName = name;// 把中括号里的数字解析出来delim = name.indexOf("[");if (delim > -1) {index = name.substring(delim + 1, name.length() - 1);name = name.substring(0, delim);}}@Overridepublic Iterator<PropertyTokenizer> iterator() {return this;}@Overridepublic boolean hasNext() {return children != null;}@Overridepublic PropertyTokenizer next() {return new PropertyTokenizer(children);}public String getName() {return name;}public String getIndexedName() {return indexedName;}public String getIndex() {return index;}public String getChildren() {return children;}
}

3.5 反射器解耦对象

  • Reflector 反射器专门用于解耦对象信息,只有把一个对象信息所包含的属性、方法以及关联的类都以此解析出来,才能满足后续对属性值的设置和获取。

Reflector.java

package com.lino.mybatis.reflection;import com.lino.mybatis.reflection.invoker.GetFieldInvoker;
import com.lino.mybatis.reflection.invoker.Invoker;
import com.lino.mybatis.reflection.invoker.MethodInvoker;
import com.lino.mybatis.reflection.invoker.SetFieldInvoker;
import com.lino.mybatis.reflection.property.PropertyNamer;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;/*** @description: 反射器,属性 get/set 的映射器*/
public class Reflector {private static boolean classCacheEnabled = true;private static final String[] EMPTY_STRING_ARRAY = new String[0];/*** 线程安全的缓存*/private static final Map<Class<?>, Reflector> REFLECTOR_MAP = new ConcurrentHashMap<>();private Class<?> type;/*** get 属性列表*/private String[] readablePropertyNames = EMPTY_STRING_ARRAY;/*** set 属性列表*/private String[] writeablePropertyNames = EMPTY_STRING_ARRAY;/*** set 方法列表*/private Map<String, Invoker> setMethods = new HashMap<>(16);/*** get 方法列表*/private Map<String, Invoker> getMethods = new HashMap<>(16);/*** set 类型列表*/private Map<String, Class<?>> setTypes = new HashMap<>(16);/*** get 类型列表*/private Map<String, Class<?>> getTypes = new HashMap<>(16);/*** 构造函数*/private Constructor<?> defaultConstructor;private Map<String, String> caseInsensitivePropertyMap = new HashMap<>(16);public Reflector(Class<?> clazz) {this.type = clazz;// 加入构造函数addDefaultConstructor(clazz);// 加入getteraddGetMethods(clazz);// 加入setteraddSetMethods(clazz);// 加入字段addFields(clazz);readablePropertyNames = getMethods.keySet().toArray(new String[0]);writeablePropertyNames = setMethods.keySet().toArray(new String[0]);for (String propertyName : readablePropertyNames) {caseInsensitivePropertyMap.put(propertyName.toUpperCase(Locale.ENGLISH), propertyName);}for (String propertyName : writeablePropertyNames) {caseInsensitivePropertyMap.put(propertyName.toUpperCase(Locale.ENGLISH), propertyName);}}/*** 加入构造函数** @param clazz 对象类型*/private void addDefaultConstructor(Class<?> clazz) {Constructor<?>[] constes = clazz.getDeclaredConstructors();for (Constructor<?> constructor : constes) {if (constructor.getParameterTypes().length == 0) {if (canAccessPrivateMethods()) {try {constructor.setAccessible(true);} catch (Exception ignore) {}}if (constructor.isAccessible()) {this.defaultConstructor = constructor;}}}}/*** 加入getter方法** @param clazz 对象类型*/private void addGetMethods(Class<?> clazz) {Map<String, List<Method>> conflictingGetters = new HashMap<>(16);Method[] methods = getClassMethods(clazz);for (Method method : methods) {String name = method.getName();if (name.startsWith("get") && name.length() > 3) {if (method.getParameterTypes().length == 0) {name = PropertyNamer.methodToProperty(name);addMethodConflict(conflictingGetters, name, method);}} else if (name.startsWith("is") && name.length() > 2) {if (method.getParameterTypes().length == 0) {name = PropertyNamer.methodToProperty(name);addMethodConflict(conflictingGetters, name, method);}}}resolveGetterConflicts(conflictingGetters);}/*** 加入setter方法** @param clazz 对象类型*/private void addSetMethods(Class<?> clazz) {Map<String, List<Method>> conflictingSetters = new HashMap<>(16);Method[] methods = getClassMethods(clazz);for (Method method : methods) {String name = method.getName();if (name.startsWith("set") && name.length() > 3) {if (method.getParameterTypes().length == 1) {name = PropertyNamer.methodToProperty(name);addMethodConflict(conflictingSetters, name, method);}}}resolveSetterConflicts(conflictingSetters);}/*** 加入字段** @param clazz 对象类型*/private void addFields(Class<?> clazz) {Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {if (canAccessPrivateMethods()) {try {field.setAccessible(true);} catch (Exception e) {}}if (field.isAccessible()) {if (!setMethods.containsKey(field.getName())) {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());}}private boolean canAccessPrivateMethods() {try {SecurityManager securityManager = System.getSecurityManager();if (null != securityManager) {securityManager.checkPermission(new ReflectPermission("suppressAccessChecks"));}} catch (SecurityException e) {return false;}return true;}private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {for (String propName : conflictingGetters.keySet()) {List<Method> getters = conflictingGetters.get(propName);Iterator<Method> iterator = getters.iterator();Method firstMethod = iterator.next();if (getters.size() == 1) {addGetMethod(propName, firstMethod);} else {Method getter = firstMethod;Class<?> getterType = firstMethod.getReturnType();while (iterator.hasNext()) {Method method = iterator.next();Class<?> methodType = method.getReturnType();if (methodType.equals(getterType)) {throw new RuntimeException("Illegal overloaded getter method with ambiguous type for property " + propName + " in class " + firstMethod.getDeclaringClass() + ".  This breaks the JavaBeans " + "specification and can cause unpredicatble results.");} else if (methodType.isAssignableFrom(getterType)) {// OK getter type is descendant} else if (getterType.isAssignableFrom(methodType)) {getter = method;getterType = methodType;} else {throw new RuntimeException("Illegal overloaded getter method with ambiguous type for property " + propName + " in class " + firstMethod.getDeclaringClass() + ".  This breaks the JavaBeans " + "specification and can cause unpredicatble results.");}}addGetMethod(propName, getter);}}}private void resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) {for (String propName : conflictingSetters.keySet()) {List<Method> setters = conflictingSetters.get(propName);Method firstMethod = setters.get(0);if (setters.size() == 1) {addSetMethod(propName, firstMethod);} else {Class<?> expectedType = getTypes.get(propName);if (expectedType == null) {throw new RuntimeException("Illegal overloaded setter method with ambiguous type for property " + propName + " in class " + firstMethod.getDeclaringClass() + ".  This breaks the JavaBeans " + "specification and can cause unpredicatble results.");} else {Iterator<Method> methods = setters.iterator();Method setter = null;while (methods.hasNext()) {Method method = methods.next();if (method.getParameterTypes().length == 1 && expectedType.equals(method.getParameterTypes()[0])) {setter = method;break;}}if (setter == null) {throw new RuntimeException("Illegal overloaded setter method with ambiguous type for property " + propName + " in class " + firstMethod.getDeclaringClass() + ".  This breaks the JavaBeans " + "specification and can cause unpredicatble results.");}addSetMethod(propName, setter);}}}}private boolean isValidPropertyName(String name) {return !(name.startsWith("$") || "serialVersionUID".equals(name) || "class".equals(name));}private void addSetMethod(String name, Method method) {if (isValidPropertyName(name)) {setMethods.put(name, new MethodInvoker(method));setTypes.put(name, method.getParameterTypes()[0]);}}private void addGetMethod(String name, Method method) {if (isValidPropertyName(name)) {getMethods.put(name, new MethodInvoker(method));getTypes.put(name, method.getReturnType());}}private void addSetField(Field field) {if (isValidPropertyName(field.getName())) {setMethods.put(field.getName(), new SetFieldInvoker(field));setTypes.put(field.getName(), field.getType());}}private void addGetField(Field field) {if (isValidPropertyName(field.getName())) {getMethods.put(field.getName(), new GetFieldInvoker(field));getTypes.put(field.getName(), field.getType());}}private Method[] getClassMethods(Class<?> clazz) {Map<String, Method> uniqueMethods = new HashMap<>(16);Class<?> currentClass = clazz;while (currentClass != null) {addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());// 循环判断类是否是抽象类Class<?>[] interfaces = currentClass.getInterfaces();for (Class<?> anInterface : interfaces) {addUniqueMethods(uniqueMethods, anInterface.getDeclaredMethods());}currentClass = currentClass.getSuperclass();}Collection<Method> methods = uniqueMethods.values();return methods.toArray(new Method[0]);}private void addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) {for (Method currentMethod : methods) {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 methodif (!uniqueMethods.containsKey(signature)) {if (canAccessPrivateMethods()) {try {currentMethod.setAccessible(true);} catch (Exception e) {}}uniqueMethods.put(signature, currentMethod);}}}}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++) {if (i == 0) {sb.append(":");} else {sb.append(",");}sb.append(parameters[i].getName());}return sb.toString();}private void addMethodConflict(Map<String, List<Method>> conflictingGetters, String name, Method method) {List<Method> list = conflictingGetters.computeIfAbsent(name, k -> new ArrayList<>());list.add(method);}public Class<?> getType() {return type;}public Constructor<?> getDefaultConstructor() {if (defaultConstructor != null) {return defaultConstructor;} else {throw new RuntimeException("There is no default constructor for " + type);}}public boolean hasDefaultConstructor() {return defaultConstructor != null;}public Invoker getGetInvoker(String propertyName) {Invoker method = getMethods.get(propertyName);if (method == null) {throw new RuntimeException("There is no getter for property named '" + propertyName + "' in '" + type + "'");}return method;}public Invoker getSetInvoker(String propertyName) {Invoker method = setMethods.get(propertyName);if (method == null) {throw new RuntimeException("There is no setter for property named '" + propertyName + "' in '" + type + "'");}return method;}public Class<?> getSetterType(String propertyName) {Class<?> clazz = setTypes.get(propertyName);if (clazz == null) {throw new RuntimeException("There is no setter for property named '" + propertyName + "' in '" + type + "'");}return clazz;}public Class<?> getGetterType(String propertyName) {Class<?> clazz = getTypes.get(propertyName);if (clazz == null) {throw new RuntimeException("There is no getter for property named '" + propertyName + "' in '" + type + "'");}return clazz;}public String[] getGetablePropertyNames() {return readablePropertyNames;}public String[] getSetablePropertyNames() {return writeablePropertyNames;}public boolean hasSetter(String propertyName) {return setMethods.keySet().contains(propertyName);}public boolean hasGetter(String propertyName) {return getMethods.keySet().contains(propertyName);}public String findPropertyName(String name) {return caseInsensitivePropertyMap.get(name.toUpperCase(Locale.ENGLISH));}public static Reflector forClass(Class<?> clazz) {if (classCacheEnabled) {// 对于每个类来说,我们假设它是不会变的,这样可以考虑将这个类的信息(构造函数,getter,setter,字段)加入缓存,以提高速度Reflector cached = REFLECTOR_MAP.get(clazz);if (cached == null) {cached = new Reflector(clazz);REFLECTOR_MAP.put(clazz, cached);}return cached;} else {return new Reflector(clazz);}}public static boolean isClassCacheEnabled() {return classCacheEnabled;}public static void setClassCacheEnabled(boolean classCacheEnabled) {Reflector.classCacheEnabled = classCacheEnabled;}
}
  • Reflector 反射器类中提供了各类属性、方法、类型以及构造函数的保存操作。
  • 当调用反射器时会通过构造函数的处理,逐步从对象类中拆解出这些属性信息,便于后续反射使用。

3.6 元类包装反射器

  • Reflector 反射器类提供的是最基础的核心功能,很多方法都是私有的,为了更方便使用,还需要做一层元类的包装。
  • 在元类 MetaClass 提供必要的创建反射器以及使用反射器获取 get/setInvoker 反射方法。

MetaClass.java

package com.lino.mybatis.reflection;import com.lino.mybatis.reflection.invoker.GetFieldInvoker;
import com.lino.mybatis.reflection.invoker.Invoker;
import com.lino.mybatis.reflection.invoker.MethodInvoker;
import com.lino.mybatis.reflection.property.PropertyTokenizer;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;/*** @description: 元类*/
public class MetaClass {private Reflector reflector;public MetaClass(Class<?> type) {this.reflector = Reflector.forClass(type);}public static MetaClass forClass(Class<?> type) {return new MetaClass(type);}public static boolean isClassCacheEnabled() {return Reflector.isClassCacheEnabled();}public static void setClassCacheEnabled(boolean classCacheEnabled) {Reflector.setClassCacheEnabled(classCacheEnabled);}public MetaClass metaClassForProperty(String name) {Class<?> propType = reflector.getGetterType(name);return MetaClass.forClass(propType);}public MetaClass metaClassForProperty(PropertyTokenizer prop) {Class<?> propType = getGetterType(prop);return MetaClass.forClass(propType);}private StringBuilder buildProperty(String name, StringBuilder builder) {PropertyTokenizer prop = new PropertyTokenizer(name);if (prop.hasNext()) {String propertyName = reflector.findPropertyName(prop.getName());if (propertyName != null) {builder.append(propertyName);builder.append(".");MetaClass metaProp = metaClassForProperty(propertyName);metaProp.buildProperty(prop.getChildren(), builder);}} else {String propertyName = reflector.findPropertyName(name);if (propertyName != null) {builder.append(propertyName);}}return builder;}public String findProperty(String name) {StringBuilder prop = buildProperty(name, new StringBuilder());return prop.length() > 0 ? prop.toString() : null;}public String findProperty(String name, boolean useCameCaseMapping) {if (useCameCaseMapping) {name = name.replace("_", "");}return findProperty(name);}public String[] getGetterNames() {return reflector.getGetablePropertyNames();}public String[] getSetterNames() {return reflector.getSetablePropertyNames();}public Class<?> getSetterType(String name) {PropertyTokenizer prop = new PropertyTokenizer(name);if (prop.hasNext()) {MetaClass metaProp = metaClassForProperty(prop.getName());return metaProp.getSetterType(prop.getChildren());} else {return reflector.getSetterType(prop.getName());}}public Class<?> getGetterType(String name) {PropertyTokenizer prop = new PropertyTokenizer(name);if (prop.hasNext()) {MetaClass metaProp = metaClassForProperty(prop);return metaProp.getGetterType(prop.getChildren());}// 解析集合对象中的类型return getGetterType(prop);}private Class<?> getGetterType(PropertyTokenizer prop) {Class<?> type = reflector.getGetterType(prop.getName());if (prop.getIndex() != null && Collection.class.isAssignableFrom(type)) {Type returnType = getGenericGetterType(prop.getName());if (returnType instanceof ParameterizedType) {Type[] actualTypeArguments = ((ParameterizedType) returnType).getActualTypeArguments();if (actualTypeArguments != null && actualTypeArguments.length == 1) {returnType = actualTypeArguments[0];if (returnType instanceof Class) {type = (Class<?>) returnType;} else if (returnType instanceof ParameterizedType) {type = (Class<?>) ((ParameterizedType) returnType).getRawType();}}}}return type;}private Type getGenericGetterType(String propertyName) {try {Invoker invoker = reflector.getGetInvoker(propertyName);if (invoker instanceof MethodInvoker) {Field _method = MethodInvoker.class.getDeclaredField("method");_method.setAccessible(true);Method method = (Method) _method.get(invoker);return method.getGenericReturnType();} else if (invoker instanceof GetFieldInvoker) {Field _field = GetFieldInvoker.class.getDeclaredField("field");_field.setAccessible(true);Field field = (Field) _field.get(invoker);return field.getGenericType();}} catch (NoSuchFieldException | IllegalAccessException ignored) {}return null;}public boolean hasSetter(String name) {PropertyTokenizer prop = new PropertyTokenizer(name);if (prop.hasNext()) {if (reflector.hasSetter(prop.getName())) {MetaClass metaProp = metaClassForProperty(prop.getName());return metaProp.hasSetter(prop.getChildren());} else {return false;}} else {return reflector.hasSetter(prop.getName());}}public boolean hasGetter(String name) {PropertyTokenizer prop = new PropertyTokenizer(name);if (prop.hasNext()) {if (reflector.hasGetter(prop.getName())) {MetaClass metaProp = metaClassForProperty(prop);return metaProp.hasGetter(prop.getChildren());} else {return false;}} else {return reflector.hasGetter(prop.getName());}}public Invoker getGetInvoker(String name) {return reflector.getGetInvoker(name);}public Invoker getSetInvoker(String name) {return reflector.getSetInvoker(name);}public boolean hasDefaultConstructor() {return reflector.hasDefaultConstructor();}
}
  • MetaClass 元类相当于是对我们需要处理对象的包装,解耦一个原对象,包装出一个元类、对象包装器以及对象工厂等,再组合出一个元对象。
  • 相当于说这些元类和元对象都是对我们需要操作的原对象解耦后的封装。有了这样的操作,就可以让我们处理每一个属性或者方法。

3.7 对象包装器 Wrapper

  • 对象包装器相当于是更加进一步反射调用包装处理,同时也为不同的对象类型提供不同的包装策略。
  • 再对象包装器接口中定义了更加明确的需要使用的方法,包括定义出 get/set 标准的通用方法、获取 get/set 属性名称和属性类型,以及添加属性等。

3.7.1 对象包装器接口

ObjectWrapper.java

package com.lino.mybatis.reflection.wrapper;import com.lino.mybatis.reflection.MetaObject;
import com.lino.mybatis.reflection.factory.ObjectFactory;
import com.lino.mybatis.reflection.property.PropertyTokenizer;
import java.util.List;/*** @description: 对象包装器*/
public interface ObjectWrapper {/*** get获取** @param prop 属性分解标记* @return Object*/Object get(PropertyTokenizer prop);/*** set设置** @param prop  属性分解标记* @param value 值*/void set(PropertyTokenizer prop, Object value);/*** 查找属性** @param name               属性名称* @param useCameCaseMapping 是否使用强制映射* @return 属性*/String findProperty(String name, boolean useCameCaseMapping);/*** 取得getter的名字列表** @return getter的名字列表*/String[] getGetterNames();/*** 取得setter的名字列表** @return setter的名字列表*/String[] getSetterNames();/*** 取得setter的类型** @param name 属性名称* @return setter的类型*/Class<?> getSetterType(String name);/*** 取得getter的类型** @param name 属性名称* @return getter的类型*/Class<?> getGetterType(String name);/*** 是否有指定的setter** @param name 属性名* @return 是否有指定的setter*/boolean hasSetter(String name);/*** 是否有指定的getter** @param name 属性名* @return 是否有指定的getter*/boolean hasGetter(String name);/*** 实例化属性** @param name          属性名* @param prop          属性分解标记* @param objectFactory 对象工厂接口* @return 元对象*/MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory);/*** 是否是集合** @return boolean*/boolean isCollection();/*** 添加属性** @param element 属性对象*/void add(Object element);/*** 添加属性集合** @param elements 属性对象集合* @param <E>      属性泛型*/<E> void addAll(List<E> elements);
}
  • 后续所有实现了对象包装器接口的实现类,都需要提供这些方法实现,基本有了这些方法,也就能非常容易的处理一个对象的反射操作。
  • 方法:设置属性、获取属性、获取字段列表,获取字段类型

3.7.2 对象包装器抽象基类

BaseWrapper.java

package com.lino.mybatis.reflection.wrapper;import com.lino.mybatis.reflection.MetaObject;
import com.lino.mybatis.reflection.property.PropertyTokenizer;import java.util.List;
import java.util.Map;/*** @description: 对象包装器抽象基类,提供一些工具方法* @author: lingjian* @createDate: 2022/11/9 9:55*/
public abstract class BaseWrapper implements ObjectWrapper {protected static final Object[] NO_ARGUMENTS = new Object[0];protected MetaObject metaObject;public BaseWrapper(MetaObject metaObject) {this.metaObject = metaObject;}/*** 解析集合** @param prop   属性分解标记* @param object 对象* @return Object 对象*/protected Object resolveCollection(PropertyTokenizer prop, Object object) {if ("".equals(prop.getName())) {return object;} else {return metaObject.getValue(prop.getName());}}/*** 取集合的值** @param prop       属性分解标记* @param collection 对象{Map,List/Array}* @return Object 属性对象*/protected Object getCollectionValue(PropertyTokenizer prop, Object collection) {if (collection instanceof Map) {// map['name']return ((Map) collection).get(prop.getIndex());} else {int i = Integer.parseInt(prop.getIndex());if (collection instanceof List) {// list[0]return ((List) collection).get(i);} else if (collection instanceof Object[]) {return ((Object[]) collection)[i];} else if (collection instanceof char[]) {return ((char[]) collection)[i];} else if (collection instanceof boolean[]) {return ((boolean[]) collection)[i];} else if (collection instanceof byte[]) {return ((byte[]) collection)[i];} else if (collection instanceof double[]) {return ((double[]) collection)[i];} else if (collection instanceof float[]) {return ((float[]) collection)[i];} else if (collection instanceof int[]) {return ((int[]) collection)[i];} else if (collection instanceof long[]) {return ((long[]) collection)[i];} else if (collection instanceof short[]) {return ((short[]) collection)[i];} else {throw new RuntimeException("The '" + prop.getName() + "' property of " + collection + "is not a List or Array.");}}}/*** 设置集合的值** @param prop       属性分解标记* @param collection 对象{Map,List/Array}* @param value      值*/protected void setCollectionValue(PropertyTokenizer prop, Object collection, Object value) {if (collection instanceof Map) {// map['name']((Map) collection).put(prop.getIndex(), value);} else {int i = Integer.parseInt(prop.getIndex());if (collection instanceof List) {((List) collection).set(i, value);} else if (collection instanceof Object[]) {((Object[]) collection)[i] = value;} else if (collection instanceof char[]) {((char[]) collection)[i] = (Character) value;} else if (collection instanceof boolean[]) {((boolean[]) collection)[i] = (Boolean) value;} else if (collection instanceof byte[]) {((byte[]) collection)[i] = (Byte) value;} else if (collection instanceof double[]) {((double[]) collection)[i] = (Double) value;} else if (collection instanceof float[]) {((float[]) collection)[i] = (Float) value;} else if (collection instanceof int[]) {((int[]) collection)[i] = (Integer) value;} else if (collection instanceof long[]) {((long[]) collection)[i] = (Long) value;} else if (collection instanceof short[]) {((short[]) collection)[i] = (Short) value;} else {throw new RuntimeException("The '" + prop.getName() + "' property of " + collection + "is not a List or Array.");}}}
}

3.7.3 Bean 包装器

BeanWrapper.java

package com.lino.mybatis.reflection.wrapper;import com.lino.mybatis.reflection.MetaClass;
import com.lino.mybatis.reflection.MetaObject;
import com.lino.mybatis.reflection.SystemMetaObject;
import com.lino.mybatis.reflection.factory.ObjectFactory;
import com.lino.mybatis.reflection.invoker.Invoker;
import com.lino.mybatis.reflection.property.PropertyTokenizer;
import java.util.List;/*** @description: Bean 包装器*/
public class BeanWrapper extends BaseWrapper {/*** 原来的对象*/private Object object;/*** 元类*/private MetaClass metaClass;public BeanWrapper(MetaObject metaObject, Object object) {super(metaObject);this.object = object;this.metaClass = MetaClass.forClass(object.getClass());}@Overridepublic Object get(PropertyTokenizer prop) {// 如果有index(有中括号),说明是集合,那就要解析集合,调用的是 BaseWrapper.resolveCollection 和 getCollectionValueif (prop.getIndex() != null) {Object collection = resolveCollection(prop, object);return getCollectionValue(prop, collection);} else {// 否则 getBeanPropertyreturn getBeanProperty(prop, object);}}private Object getBeanProperty(PropertyTokenizer prop, Object object) {try {// 得到getter方法,然后调用Invoker method = metaClass.getGetInvoker(prop.getName());return method.invoke(object, NO_ARGUMENTS);} catch (RuntimeException e) {throw e;} catch (Throwable t) {throw new RuntimeException("Count not get property '" + prop.getName() + "' from " + object.getClass() + ". Cause: " + t, t);}}@Overridepublic void set(PropertyTokenizer prop, Object value) {// 如果有index(有中括号),说明是集合,那就要解析集合,调用的是 BaseWrapper.resolveCollection 和 getCollectionValueif (prop.getIndex() != null) {Object collection = resolveCollection(prop, object);setCollectionValue(prop, collection, value);} else {// 否则 setBeanPropertysetBeanProperty(prop, object, value);}}private void setBeanProperty(PropertyTokenizer prop, Object object, Object value) {try {// 得到getter方法,然后调用Invoker method = metaClass.getSetInvoker(prop.getName());Object[] params = {value};method.invoke(object, params);} catch (Throwable t) {throw new RuntimeException("Count not set property '" + prop.getName() + "' of '" + object.getClass() + "' with value '" + value + "' Cause: " + t, t);}}@Overridepublic String findProperty(String name, boolean useCameCaseMapping) {return metaClass.findProperty(name, useCameCaseMapping);}@Overridepublic String[] getGetterNames() {return metaClass.getGetterNames();}@Overridepublic String[] getSetterNames() {return metaClass.getSetterNames();}@Overridepublic Class<?> getSetterType(String name) {PropertyTokenizer prop = new PropertyTokenizer(name);if (prop.hasNext()) {MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName());if (metaValue == SystemMetaObject.NULL_META_OBJECT) {return metaClass.getSetterType(name);} else {return metaValue.getSetterType(prop.getChildren());}} else {return metaClass.getSetterType(name);}}@Overridepublic Class<?> getGetterType(String name) {PropertyTokenizer prop = new PropertyTokenizer(name);if (prop.hasNext()) {MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName());if (metaValue == SystemMetaObject.NULL_META_OBJECT) {return metaClass.getGetterType(name);} else {return metaValue.getGetterType(prop.getChildren());}} else {return metaClass.getGetterType(name);}}@Overridepublic boolean hasSetter(String name) {PropertyTokenizer prop = new PropertyTokenizer(name);if (prop.hasNext()) {if (metaObject.hasSetter(prop.getIndexedName())) {MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName());if (metaValue == SystemMetaObject.NULL_META_OBJECT) {return metaClass.hasSetter(name);} else {return metaValue.hasSetter(prop.getChildren());}} else {return false;}} else {return metaClass.hasSetter(name);}}@Overridepublic boolean hasGetter(String name) {PropertyTokenizer prop = new PropertyTokenizer(name);if (prop.hasNext()) {if (metaObject.hasGetter(prop.getIndexedName())) {MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName());if (metaValue == SystemMetaObject.NULL_META_OBJECT) {return metaClass.hasGetter(name);} else {return metaValue.hasGetter(prop.getChildren());}} else {return false;}} else {return metaClass.hasGetter(name);}}@Overridepublic MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory) {MetaObject metaValue;Class<?> type = getSetterType(prop.getName());try {Object newObject = objectFactory.create(type);metaValue = MetaObject.forObject(newObject, metaObject.getObjectFactory(), metaObject.getObjectWrapperFactory());set(prop, newObject);} catch (Exception e) {throw new RuntimeException("Cannot set value of property '" + name + "' because '" + name + "' is null and cannot be instantiated on " +"instance of " + type.getName() + ". Cause: " + e, e);}return metaValue;}@Overridepublic boolean isCollection() {return false;}@Overridepublic void add(Object element) {throw new UnsupportedOperationException();}@Overridepublic <E> void addAll(List<E> elements) {throw new UnsupportedOperationException();}
}

3.7.4 Collection 包装器

CollectionWrapper.java

package com.lino.mybatis.reflection.wrapper;import com.lino.mybatis.reflection.MetaObject;
import com.lino.mybatis.reflection.factory.ObjectFactory;
import com.lino.mybatis.reflection.property.PropertyTokenizer;
import java.util.Collection;
import java.util.List;/*** @description: Collection 包装器*/
public class CollectionWrapper implements ObjectWrapper {/*** 原始对象*/private Collection<Object> object;public CollectionWrapper(MetaObject metaObject, Collection<Object> object) {this.object = object;}@Overridepublic Object get(PropertyTokenizer prop) {throw new UnsupportedOperationException();}@Overridepublic void set(PropertyTokenizer prop, Object value) {throw new UnsupportedOperationException();}@Overridepublic String findProperty(String name, boolean useCameCaseMapping) {throw new UnsupportedOperationException();}@Overridepublic String[] getGetterNames() {throw new UnsupportedOperationException();}@Overridepublic String[] getSetterNames() {throw new UnsupportedOperationException();}@Overridepublic Class<?> getSetterType(String name) {throw new UnsupportedOperationException();}@Overridepublic Class<?> getGetterType(String name) {throw new UnsupportedOperationException();}@Overridepublic boolean hasSetter(String name) {throw new UnsupportedOperationException();}@Overridepublic boolean hasGetter(String name) {throw new UnsupportedOperationException();}@Overridepublic MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory) {throw new UnsupportedOperationException();}@Overridepublic boolean isCollection() {return true;}@Overridepublic void add(Object element) {object.add(element);}@Overridepublic <E> void addAll(List<E> elements) {object.addAll(elements);}
}

3.7.5 MapWrapper 包装器

MapWrapper.java

package com.lino.mybatis.reflection.wrapper;import com.lino.mybatis.reflection.MetaObject;
import com.lino.mybatis.reflection.SystemMetaObject;
import com.lino.mybatis.reflection.factory.ObjectFactory;
import com.lino.mybatis.reflection.property.PropertyTokenizer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @description: Map 包装器*/
public class MapWrapper extends BaseWrapper {/*** 原始对象*/private Map<String, Object> map;public MapWrapper(MetaObject metaObject, Map<String, Object> map) {super(metaObject);this.map = map;}@Overridepublic Object get(PropertyTokenizer prop) {// 如果有index(有中括号),说明是集合,那就要解析集合,调用的是 BaseWrapper.resolveCollection 和 getCollectionValueif (prop.getIndex() != null) {Object collection = resolveCollection(prop, map);return getCollectionValue(prop, collection);} else {return map.get(prop.getName());}}@Overridepublic void set(PropertyTokenizer prop, Object value) {// 如果有index(有中括号),说明是集合,那就要解析集合,调用的是 BaseWrapper.resolveCollection 和 getCollectionValueif (prop.getIndex() != null) {Object collection = resolveCollection(prop, map);setCollectionValue(prop, collection, value);} else {// 否则 setBeanPropertymap.put(prop.getName(), value);}}@Overridepublic String findProperty(String name, boolean useCameCaseMapping) {return name;}@Overridepublic String[] getGetterNames() {return map.keySet().toArray(new String[0]);}@Overridepublic String[] getSetterNames() {return map.keySet().toArray(new String[0]);}@Overridepublic Class<?> getSetterType(String name) {PropertyTokenizer prop = new PropertyTokenizer(name);if (prop.hasNext()) {MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName());if (metaValue == SystemMetaObject.NULL_META_OBJECT) {return Object.class;} else {return metaValue.getSetterType(prop.getChildren());}} else {if (map.get(name) != null) {return map.get(name).getClass();} else {return Object.class;}}}@Overridepublic Class<?> getGetterType(String name) {PropertyTokenizer prop = new PropertyTokenizer(name);if (prop.hasNext()) {MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName());if (metaValue == SystemMetaObject.NULL_META_OBJECT) {return Object.class;} else {return metaValue.getGetterType(prop.getChildren());}} else {if (map.get(name) != null) {return map.get(name).getClass();} else {return Object.class;}}}@Overridepublic boolean hasSetter(String name) {return true;}@Overridepublic boolean hasGetter(String name) {PropertyTokenizer prop = new PropertyTokenizer(name);if (prop.hasNext()) {if (map.containsKey(prop.getIndexedName())) {MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName());if (metaValue == SystemMetaObject.NULL_META_OBJECT) {return true;} else {return metaValue.hasGetter(prop.getChildren());}} else {return false;}} else {return map.containsKey(prop.getName());}}@Overridepublic MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory) {HashMap<String, Object> map = new HashMap<>(16);set(prop, map);return MetaObject.forObject(map, metaObject.getObjectFactory(), metaObject.getObjectWrapperFactory());}@Overridepublic boolean isCollection() {return false;}@Overridepublic void add(Object element) {throw new UnsupportedOperationException();}@Overridepublic <E> void addAll(List<E> elements) {throw new UnsupportedOperationException();}
}

3.8 对象包装工厂

  • 通过包装工厂获取包装器

3.8.1 对象包装工厂接口

ObjectWrapperFactory.java

package com.lino.mybatis.reflection.wrapper;import com.lino.mybatis.reflection.MetaObject;/*** @description: 对象包装工厂*/
public interface ObjectWrapperFactory {/*** 判断有没有包装器** @param object 对象* @return boolean*/boolean hasWrapperFor(Object object);/*** 获取包装器** @param metaObject 元对象* @param object     对象* @return 包装器*/ObjectWrapper getWrapperFor(MetaObject metaObject, Object object);
}

3.8.2 默认对象包装工厂实现类

DefaultObjectWrapperFactory.java

package com.lino.mybatis.reflection.wrapper;import com.lino.mybatis.reflection.MetaObject;/*** @description: 默认对象包装器*/
public class DefaultObjectWrapperFactory implements ObjectWrapperFactory {@Overridepublic boolean hasWrapperFor(Object object) {return false;}@Overridepublic ObjectWrapper getWrapperFor(MetaObject metaObject, Object object) {throw new RuntimeException("The DefaultObjectWrapperFactory should never be called to provide an ObjectWrapper.");}
}

3.9 对象工厂

3.9.1 对象工厂接口

ObjectFactory.java

package com.lino.mybatis.reflection.factory;import java.util.List;
import java.util.Properties;/*** @description: 对象工厂接口*/
public interface ObjectFactory {/*** 设置属性** @param properties 属性配置*/void setProperties(Properties properties);/*** 生产对象** @param type 对象类型* @param <T>  泛型* @return <T> 泛型对象*/<T> T create(Class<T> type);/*** 生产对象,使用指定的构造函数和构造函数参数** @param type                对象类型* @param constructorArgTypes 构造函数* @param constructorArgs     构造函数参数* @param <T>                 泛型* @return <T> 泛型对象*/<T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs);/*** 返回这个对象是否是集合** @param type 对象类型* @param <T>  泛型* @return 是否是集合*/<T> boolean isCollection(Class<T> type);
}

3.9.2 默认对象工厂实现类

DefaultObjectFactory.java

package com.lino.mybatis.reflection.factory;import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.util.*;/*** @description: 默认对象工厂,所有的对象都由工厂来生成*/
public class DefaultObjectFactory implements ObjectFactory, Serializable {private static final long serialVersionUID = -8855120656740914948L;@Overridepublic void setProperties(Properties properties) {}@Overridepublic <T> T create(Class<T> type) {return create(type, null, null);}@SuppressWarnings("unchecked")@Overridepublic <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {// 1.解析接口Class<?> classToCreate = resolveInterface(type);// 2.类实例化return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);}private <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {try {Constructor<T> constructor;// 如果没有传入constructor,调用空构造函数,核心是调用 Constructor.newInstanceif (constructorArgTypes == null || constructorArgs == null) {constructor = type.getDeclaredConstructor();if (!constructor.isAccessible()) {constructor.setAccessible(true);}return constructor.newInstance();}// 如果传入constructor,调用传入的构造函数,核心是调用 Constructor.newInstanceconstructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[0]));if (!constructor.isAccessible()) {constructor.setAccessible(true);}return constructor.newInstance(constructorArgs.toArray(new Object[0]));} catch (Exception e) {// 如果出错,包装一下,重新抛出自己的异常StringBuilder argTypes = new StringBuilder();if (constructorArgTypes != null) {for (Class<?> argType : constructorArgTypes) {argTypes.append(argType.getSimpleName());argTypes.append(",");}}StringBuilder argValues = new StringBuilder();if (constructorArgs != null) {for (Object argType : constructorArgs) {argValues.append(argType);argTypes.append(",");}}throw new RuntimeException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). " + "Cause: " + e, e);}}/*** 解析接口,将 interface 转换为实际的 class类** @param type 对象类型* @return Class<?> 实现类*/private Class<?> resolveInterface(Class<?> type) {Class<?> classToCreate;if (type == List.class || type == Collection.class || type == Iterable.class) {// List/Collection/Iterable ---> ArrayListclassToCreate = ArrayList.class;} else if (type == Map.class) {// Map ---> HashMapclassToCreate = HashMap.class;} else if (type == SortedSet.class) {// SortedSet ---> TreeSetclassToCreate = TreeSet.class;} else if (type == Set.class) {// Set ---> HashSetclassToCreate = HashSet.class;} else {// 除此之外,就用原来的原型classToCreate = type;}return classToCreate;}@Overridepublic <T> boolean isCollection(Class<T> type) {return Collection.class.isAssignableFrom(type);}
}

3.10 元对象封装

  • 在有了反射器、元类、对象包装器以后,再使用对象工厂和对象包装工厂,就可以组合出一个完整的元对象操作类。
  • 对元对象的管理,包括:包装器策略、包装工程、统一的方法处理。

3.10.1 元对象

MetaObject.java

package com.lino.mybatis.reflection;import com.lino.mybatis.reflection.factory.ObjectFactory;
import com.lino.mybatis.reflection.property.PropertyTokenizer;
import com.lino.mybatis.reflection.wrapper.*;
import java.util.Collection;
import java.util.List;
import java.util.Map;/*** @description: 元对象*/
public class MetaObject {/*** 原始对象*/private Object originalObject;/*** 对象包装器*/private ObjectWrapper objectWrapper;/*** 对象工厂*/private ObjectFactory objectFactory;/*** 对象包装工厂*/private ObjectWrapperFactory objectWrapperFactory;public MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory) {this.originalObject = object;this.objectFactory = objectFactory;this.objectWrapperFactory = objectWrapperFactory;if (object instanceof ObjectWrapper) {// 如果对象本身已经是ObjectWrapper类型,则直接赋给objectWrapperthis.objectWrapper = (ObjectWrapper) object;} else if (objectWrapperFactory.hasWrapperFor(object)) {// 如果有包装器,调用objectWrapperFactory.getWrapperForthis.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);} else if (object instanceof Map) {// 如果是Map类型,返回MapWrapperthis.objectWrapper = new MapWrapper(this, (Map) object);} else if (object instanceof Collection) {// 如果是Collection类型,返回CollectionWrapperthis.objectWrapper = new CollectionWrapper(this, (Collection) object);} else {// 除此之外,返回 BeanWrapperthis.objectWrapper = new BeanWrapper(this, object);}}/*** 返回元对象** @param object               原始对象* @param objectFactory        对象工厂* @param objectWrapperFactory 对象包装工厂* @return MetaObject 元对象*/public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory) {if (object == null) {// 处理一下null,将null包装起来return SystemMetaObject.NULL_META_OBJECT;} else {return new MetaObject(object, objectFactory, objectWrapperFactory);}}public Object getOriginalObject() {return originalObject;}public ObjectWrapper getObjectWrapper() {return objectWrapper;}public ObjectFactory getObjectFactory() {return objectFactory;}public ObjectWrapperFactory getObjectWrapperFactory() {return objectWrapperFactory;}/* 以下属性委派给 ObjectWrapper *//*** 查找属性*/public String findProperty(String propName, boolean useCameCaseNapping) {return objectWrapper.findProperty(propName, useCameCaseNapping);}/*** 取得getter的名字列表*/public String[] getGetterNames() {return objectWrapper.getGetterNames();}/*** 取得setter的名字列表*/public String[] getSetterNames() {return objectWrapper.getSetterNames();}/*** 取得setter的类型*/public Class<?> getSetterType(String children) {return objectWrapper.getSetterType(children);}/*** 取得getter的类型*/public Class<?> getGetterType(String children) {return objectWrapper.getGetterType(children);}/*** 是否有指定的setter*/public boolean hasSetter(String indexedName) {return objectWrapper.hasSetter(indexedName);}/*** 是否有指定的getter*/public boolean hasGetter(String children) {return objectWrapper.hasGetter(children);}/*** 是否是集合*/public boolean isCollection() {return objectWrapper.isCollection();}/*** 添加属性*/public void add(Object element) {objectWrapper.add(element);}/*** 添加属性集合*/public <E> void addAll(List<E> elements) {objectWrapper.addAll(elements);}/*** 获取值** @param name 属性名称* @return 值*/public Object getValue(String name) {PropertyTokenizer prop = new PropertyTokenizer(name);if (prop.hasNext()) {MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());if (metaValue == SystemMetaObject.NULL_META_OBJECT) {// 如果上层是null,那就结束,返回nullreturn null;} else {// 否则继续看下一层,递归调用getValuereturn metaValue.getValue(prop.getChildren());}} else {return objectWrapper.get(prop);}}/*** 设置值** @param name  属性名* @param value 属性值*/public void setValue(String name, Object value) {PropertyTokenizer prop = new PropertyTokenizer(name);if (prop.hasNext()) {MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());if (metaValue == SystemMetaObject.NULL_META_OBJECT) {if (value == null && prop.getChildren() != null) {// 如果上层是null,还得看有没有子对象,没有就结束return;} else {// 否则还得 new 一个,委派给 objectWrapper.instantiatePropertyValuemetaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);}}// 递归调用setValuemetaValue.setValue(prop.getChildren(), value);} else {// 到最后层了,所以委派给 objectWrapper.setobjectWrapper.set(prop, value);}}/*** 为属性生成元对象** @param name 属性名* @return 元对象*/public MetaObject metaObjectForProperty(String name) {// 递归调用Object value = getValue(name);return MetaObject.forObject(value, objectFactory, objectWrapperFactory);}}
  • MetaObject 元对象算是整个服务的包装,在构造函数中提供各类对象的包装器类型的创建。
    • 包括这里提供的 getValue(String name)、setValue(String name, Object value) 等。
    • 其中当一些对象中的属性信息不是一个层次,班级[0].学生.成绩 需要被拆解后才能获取到对应的对象和属性值。

3.10.2 系统级别元对象

SystemMetaObject.java

package com.lino.mybatis.reflection;import com.lino.mybatis.reflection.factory.DefaultObjectFactory;
import com.lino.mybatis.reflection.factory.ObjectFactory;
import com.lino.mybatis.reflection.wrapper.DefaultObjectWrapperFactory;
import com.lino.mybatis.reflection.wrapper.ObjectWrapperFactory;/*** @description: 系统级别元对象*/
public class SystemMetaObject {public static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();public static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();public static final MetaObject NULL_META_OBJECT = MetaObject.forObject(NullObject.class, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY);private SystemMetaObject() {}private static class NullObject {}public static MetaObject forObject(Object object) {return MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY);}
}
  • 使用 SystemMetaObject#forObject 提供元对象的获取。

3.11 数据源属性设置

  • 现在有了我们实现的属性反射操作工具包,那么对于数据源中属性信息的设置,就可以使用反射来设置和获取。

3.11.1 无池化数据源工厂

UnpooledDataSourceFactory.java

package com.lino.mybatis.datasource.unpooled;import com.lino.mybatis.datasource.DataSourceFactory;
import com.lino.mybatis.reflection.MetaObject;
import com.lino.mybatis.reflection.SystemMetaObject;
import javax.sql.DataSource;
import java.util.Properties;/*** @description: 无池化数据源工厂*/
public class UnpooledDataSourceFactory implements DataSourceFactory {protected DataSource dataSource;public UnpooledDataSourceFactory() {this.dataSource = new UnpooledDataSource();}@Overridepublic void setProperties(Properties props) {MetaObject metaObject = SystemMetaObject.forObject(dataSource);for (Object key : props.keySet()) {String propertyName = (String) key;if (metaObject.hasSetter(propertyName)) {String value = (String) props.get(propertyName);Object convertedValue = convertValue(metaObject, propertyName, value);metaObject.setValue(propertyName, convertedValue);}}}@Overridepublic DataSource getDataSource() {return dataSource;}/*** 根据setter的类型,将配置文件中的值强转成相应的类型** @param metaObject   元对象* @param propertyName 属性名* @param value        属性值* @return Object 转化后的对象*/private Object convertValue(MetaObject metaObject, String propertyName, String value) {Object convertedValue = value;Class<?> targetType = metaObject.getSetterType(propertyName);if (targetType == Integer.class || targetType == int.class) {convertedValue = Integer.valueOf(value);} else if (targetType == Long.class || targetType == long.class) {convertedValue = Long.valueOf(value);} else if (targetType == Boolean.class || targetType == boolean.class) {convertedValue = Boolean.valueOf(value);}return convertedValue;}
}
  • setProperties 方法中使用 SystemMetaObject.forObject(dataSource) 获取 DataSource 的元对象,也就是通过反射设置属性值。

3.11.2 有池化数据源工厂

PooledDataSourceFactory.java

package com.lino.mybatis.datasource.pooled;import com.lino.mybatis.datasource.unpooled.UnpooledDataSourceFactory;
import javax.sql.DataSource;/*** @description: 有连接池的数据源工厂*/
public class PooledDataSourceFactory extends UnpooledDataSourceFactory {public PooledDataSourceFactory() {this.dataSource = new PooledDataSource();}
}
  • 设置属性:继承无池化数据源工厂的属性设置。

四、测试:元对象反射类

4.1 单元测试

ApiTest.java

@Test
public void test_SqlSessionFactoryExecutor() throws IOException {// 1.从SqlSessionFactory中获取SqlSessionSqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config-datasource.xml"));SqlSession sqlSession = sqlSessionFactory.openSession();// 2.获取映射器对象IUserDao userDao = sqlSession.getMapper(IUserDao.class);// 3.测试验证User user = userDao.queryUserInfoById(1L);logger.info("测试结果:{}", JSON.toJSONString(user));
}

测试结果

09:17:52.915 [main] INFO  c.l.m.d.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
09:17:53.782 [main] INFO  c.l.m.d.pooled.PooledDataSource - Created connention 2104028992.
09:17:53.891 [main] INFO  com.lino.mybatis.test.ApiTest - 测试结果:{"id":1,"userHead":"1_04","userId":"10001","userName":"小灵哥"}

在这里插入图片描述

  • 根据单元测试和调试的截图,可以看到属性值通过反射的方式设置到对象中,满足了我们在创建数据源时的使用。

4.2 反射类测试

4.2.1 学生实体类

Student.java

package com.lino.mybatis.test.po;/*** @description: 学生类*/
public class Student {private String id;public String getId() {return id;}public void setId(String id) {this.id = id;}
}

4.2.2 老师实体类

Teacher.java

package com.lino.mybatis.test.po;import java.util.List;/*** @description: 教师类*/
public class Teacher {private String name;private double price;private List<Student> students;private Student student;public String getName() {return name;}public void setName(String name) {this.name = name;}public double getPrice() {return price;}public void setPrice(double price) {this.price = price;}public List<Student> getStudents() {return students;}public void setStudents(List<Student> students) {this.students = students;}public Student getStudent() {return student;}public void setStudent(Student student) {this.student = student;}
}

4.2.3 反射类测试

ReflectorTest.java

package com.lino.mybatis.test;import com.alibaba.fastjson.JSON;
import com.lino.mybatis.io.Resources;
import com.lino.mybatis.reflection.MetaObject;
import com.lino.mybatis.reflection.SystemMetaObject;
import com.lino.mybatis.session.SqlSession;
import com.lino.mybatis.session.SqlSessionFactory;
import com.lino.mybatis.session.SqlSessionFactoryBuilder;
import com.lino.mybatis.test.dao.IUserDao;
import com.lino.mybatis.test.po.Student;
import com.lino.mybatis.test.po.Teacher;
import com.lino.mybatis.test.po.User;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;/*** @description: 反射测试*/
public class ReflectionTest {private Logger logger = LoggerFactory.getLogger(ReflectionTest.class);@Testpublic void test_reflection() {Teacher teacher = new Teacher();List<Student> list = new ArrayList<>();list.add(new Student());teacher.setName("小零哥");teacher.setStudents(list);MetaObject metaObject = SystemMetaObject.forObject(teacher);logger.info("getGetterNames:{}", JSON.toJSONString(metaObject.getGetterNames()));logger.info("getSetterNames:{}", JSON.toJSONString(metaObject.getSetterNames()));logger.info("name的get方法返回值:{}", JSON.toJSONString(metaObject.getGetterType("name")));logger.info("students的set方法参数值:{}", JSON.toJSONString(metaObject.getGetterType("students")));logger.info("name的hasGetter:{}", metaObject.hasGetter("name"));logger.info("student.id(属性为对象)的hasGetter:{}", metaObject.hasGetter("student.id"));logger.info("获取name的属性值:{}", metaObject.getValue("name"));// 重新设置属性值metaObject.setValue("name", "哆啦A梦");logger.info("设置name的属性值:{}", metaObject.getValue("name"));// 设置属性(集合)的元素值metaObject.setValue("students[0].id", "001");logger.info("获取students集合的第一个元素的属性值:{}", JSON.toJSONString(metaObject.getValue("students[0].id")));logger.info("对象的序列化:{}", JSON.toJSONString(teacher));}
}

测试结果

09:23:10.154 [main] INFO  com.lino.mybatis.test.ReflectionTest - getGetterNames:["student","price","name","students"]
09:23:10.156 [main] INFO  com.lino.mybatis.test.ReflectionTest - getSetterNames:["student","price","name","students"]
09:23:10.156 [main] INFO  com.lino.mybatis.test.ReflectionTest - name的get方法返回值:"java.lang.String"
09:23:10.156 [main] INFO  com.lino.mybatis.test.ReflectionTest - students的set方法参数值:"java.util.List"
09:23:10.156 [main] INFO  com.lino.mybatis.test.ReflectionTest - name的hasGetter:true
09:23:10.157 [main] INFO  com.lino.mybatis.test.ReflectionTest - student.id(属性为对象)的hasGetter:true
09:23:10.157 [main] INFO  com.lino.mybatis.test.ReflectionTest - 获取name的属性值:小零哥
09:23:10.157 [main] INFO  com.lino.mybatis.test.ReflectionTest - 设置name的属性值:哆啦A09:23:10.157 [main] INFO  com.lino.mybatis.test.ReflectionTest - 获取students集合的第一个元素的属性值:"001"
09:23:10.192 [main] INFO  com.lino.mybatis.test.ReflectionTest - 对象的序列化:{"name":"哆啦A梦","price":0.0,"students":[{"id":"001"}]}
  • 从测试结果看,我们拿到了对于的属性信息,并可以设置以及修改属性值,无论是单个属性还是对象属性,都可以操作。

五、总结:元对象反射类

  • 关于反射工具类的实现中,使用了大量的 JDK 所提供的关于反射一些操作,也包括可以获取一个 Class 类中的属性、字段、方法的信息。
  • 有了这些信息以后就可以按照功能流程进行解耦,把属性、反射、包装,都依次拆分出来,并按照设计原则,逐步包装让外界更少的知道内部的处理。

相关文章:

手写Mybatis:第8章-把反射用到出神入化

文章目录 一、目标&#xff1a;元对象反射类二、设计&#xff1a;元对象反射类三、实现&#xff1a;元对象反射类3.1 工程结构3.2 元对象反射类关系图3.3 反射调用者3.3.1 统一调用者接口3.3.2 方法调用者3.3.3 getter 调用者3.3.4 setter 调用者 3.4 属性命名和分解标记3.4.1 …...

基于AI智能分析网关EasyCVR视频汇聚平台关于能源行业一体化监控平台可实施应用方案

随着数字经济时代的到来&#xff0c;实体经济和数字技术深度融合已成为经济发展的主流思路。传统能源行业在运营管理方面也迎来了新的考验和机遇。许多大型能源企业已开始抓住机遇&#xff0c;逐步将视频监控、云计算、大数据和人工智能技术广泛应用于生产、维护、运输、配送等…...

《Flink学习笔记》——第四章 Flink运行时架构

4.1 系统架构 Flink运行时架构 Flink 运行时由两种类型的进程组成&#xff1a;一个 JobManager 和一个或者多个 TaskManager。 1、作业管理器&#xff08;JobManager&#xff09; JobManager是一个Flink集群中任务管理和调度的核心&#xff0c;是控制应用执行的主进程。也就…...

vue3使用Elementplus 动态显示菜单icon不生效

1.问题描述 菜单icon由后端提供&#xff0c;直接用的字符串返回&#xff0c;前端使用遍历显示&#xff0c;发现icon不会显示 {id: 8, path:/userManagement, authName: "用户管理", icon: User, rights:[view]}, <el-menu-item :index"menu.path" v-f…...

升级iOS17后iPhone无法连接App Store怎么办?

最近很多用户反馈&#xff0c;升级最新iOS 17系统后打开App Store提示"无法连接"&#xff0c;无法正常打开下载APP。 为什么升级后无法连接到App Store&#xff1f;可能是以下问题导致&#xff1a; 1.网络问题导致App Store无法正常打开 2.网络设置问题 3.App Sto…...

antd日期选择禁止

1、年月日——日期禁止当天之前的&#xff0c;不包括当天的(带有时间的除外) 2、年月日——日期禁用当天之前的(包括当天的) 部分代码如下:...

数据结构--树4.1

目录 一、树的定义 二、结点的分类 三、结点间的关系 四、结点的层次 五、树的存储结构 一、树的定义 树&#xff08;Tree&#xff09;是n&#xff08;n>0&#xff09;个结点的有限集。当n0时称为空树&#xff0c;在任意一个非空树中&#xff1a; ——有且仅有一个特定的…...

webpack(二)webpack介绍与基础配置

什么是webpack webpack最初的目标是实现前端项目模块化&#xff0c;旨在更高效的管理和维护项目中的每一个资源。 可以看做是模块打包机&#xff0c;分析你的项目结构&#xff0c;找到javascript模块以及其它一些浏览器不能直接运行的拓展语言&#xff08;Scss、TypeScript等&…...

RabbitMQ | 在ubuntu中使用apt-get安装高版本RabbitMQ

目录 一、官方脚本 二、彻底卸载 三、重新安装 1.安装高版本Erlang 2.安装RabbitMQ 一、官方脚本 直接使用apt安装的rabbitmq版本较低&#xff0c;甚至可能无法使用死信队列等插件。首先提供一个 官方 的安装脚本&#xff1a; #!/usr/bin/sh sudo apt-get install curl …...

springboot集成es 插入和查询的简单使用

第一步&#xff1a;引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId><version>2.2.5.RELEASE</version></dependency>第二步&#xff1a;…...

liunx下ubuntu基础知识学习记录

使用乌班图 命令安装使用安装网络相关工具安装dstat抓包工具需要在Ubuntu内安装openssh-server gcc安装vim安装hello word输出1. 首先安装gcc 安装这个就可以把gcc g一起安装2. 安装VIM3.编译运行代码 解决ubuntu与主机不能粘贴复制 命令安装使用 安装网络相关工具 使用ifconf…...

基于Googlenet深度学习网络的螺丝瑕疵检测matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ....................................................................................% 获…...

keepalived + lvs (DR)

目录 一、概念 二、实验流程命令 三、实验的目的 四、实验步骤 一、概念 Keepalived和LVS&#xff08;Linux Virtual Server&#xff09;可以结合使用来实现双机热备和负载均衡。 Keepalived负责监控主备服务器的可用性&#xff0c;并在主服务器发生故障时&#xff0c;将…...

微服务框架 go-zero 快速实战

对于咱们快速了解和将 go-zero 使用起来&#xff0c;我们需要具备如下能力&#xff1a; 基本的环境安装和看文档的能力 Golang 的基本知识 Protobuf 的基本知识 web&#xff0c;rpc 的基本知识 基本的 mysql 知识 其实这些能力&#xff0c;很基础&#xff0c;不需要多么深入&a…...

mysql基础面经之三:事务

6 事务 6.1 说一下事务的ACID和隔离级别 1 讲解了AID三个特性都是为了C&#xff08;一致性&#xff09;服务的。一般数据库需要使用事务保证数据库的一致性。 正确情况下最好详细讲讲&#xff1a; ACID是用来描述数据库事务的四个关键特性的首字母缩写&#xff0c;具体包括&a…...

JavaScript基本数组操作

在JavaScript中&#xff0c;内置了很多函数让我们可以去对数组进行操作&#xff0c;本文我们就来学习这些函数吧 添加元素 push ● push可以让我们在数组后面再添加一个数据&#xff0c;例如 const friends ["张三", "李四", "王五"]; frie…...

C#---第21: partial修饰类的特性及应用

0.知识背景 局部类型适用于以下情况&#xff1a; 类型特别大&#xff0c;不宜放在一个文件中实现。一个类型中的一部分代码为自动化工具生成的代码&#xff0c;不宜与我们自己编写的代码混合在一起。需要多人合作编写一个类 局部类型的限制: 局部类型只适用于类、接口、结构&am…...

SQL 语句继续学习之记录三

一&#xff0c;数据的插入&#xff08;insert 语句的使用方法&#xff09; 使用insert语句可以向表中插入数据(行)。原则上&#xff0c;insert语句每次执行一行数据的插入。 列名和值用逗号隔开&#xff0c;分别扩在&#xff08;&#xff09;内&#xff0c;这种形式称为清单。…...

Nexus仓库介绍以及maven deploy配置

一 、Nexus仓库介绍 首先介绍一下Nexus的四个仓库的结构&#xff1a; maven-central 代理仓库&#xff0c;代理了maven的中央仓库&#xff1a;https://repo1.maven.org/maven2/&#xff1b; maven-public 仓库组&#xff0c;另外三个仓库都归属于这个组&#xff0c;所以我们的…...

A Survey on Knowledge-Enhanced Pre-trained Language Models

摘要 自然语言处理(NLP)已经通过使用BERT等预训练语言模型(plm)发生了革命性的变化。尽管几乎在每个NLP任务中都创造了新的记录,但plm仍然面临许多挑战,包括可解释性差,推理能力弱,以及在应用于下游任务时需要大量昂贵的注释数据。通过将外部知识集成到plm中,知识增强预训…...

SQL求解用户连续登录天数

数据分析面试过程中&#xff0c;一般都逃不掉对SQL的考察&#xff0c;可能是笔试的形式&#xff0c;也可能是面试过程中面试官当场提问&#xff0c;当场在纸上写出&#xff0c;或者简单说一下逻辑。 今天&#xff0c;就来分享一道面试中常常被问到的一类SQL问题&#xff1a;连…...

掌握逻辑漏洞复现技术,保护您的数字环境

环境准备 这篇文章旨在用于网络安全学习&#xff0c;请勿进行任何非法行为&#xff0c;否则后果自负。 1、支付逻辑漏洞 攻击相关介绍 介绍&#xff1a; 支付逻辑漏洞是指攻击者利用支付系统的漏洞&#xff0c;突破系统的限制&#xff0c;完成非法的支付操作。攻击者可以采…...

windows系统服务器在不解锁屏幕不输入密码的前提下,电脑通电开机启动程序。

在控制面板中找到“管理工具”中的 “任务计划程序”&#xff0c;打开“任务计划程序”窗口。如图&#xff1a; 双击打开任务计划程序&#xff0c;空白出右键创建基本任务&#xff0c;或者点击最右侧的创建基本任务。 输入名称&#xff0c;点击下一步。 先选择计算机启动时&a…...

spring cloud seata集成

目录 一、seata使用场景 二、seata组成 三、seata服务端搭建 四、客户端使用seata 4.1 客户端增加undo_log表 4.2 客户端增加seata相关配置 4.3 客户端使用注解 五、测试 一、seata使用场景 微服务中&#xff0c;一个业务涉及到多个微服务系统&#xff0c;每个微服务…...

HTTP 常⻅的状态码有哪些,以及适⽤场景

⼀、HTTP状态码 HT T P 状态码&#xff08; HT T P S t a t u s Co d e &#xff09;是⽤来表示⽹⻚服务器超⽂本传输协议响应状态的 3 位数字代 码。它由 RFC 2 6 1 6 规范定义&#xff0c;并得到 RFC 2 5 1 8 、 RFC 2 8 1 7 、 RFC 2 2 9 5 、 RFC 2 7 7 4 与 RFC 4 9 1 8…...

后端给前端传参数忽略空属性

JsonInclude JsonInclude注解用于指定在对象序列化为JSON字符串时&#xff0c;哪些属性应该被包含进去&#xff0c;哪些属性应该被忽略掉。 JsonInclude注解有以下几个常用选项&#xff1a; JsonInclude(JsonInclude.Include.NON_NULL)&#xff1a;表示只有属性值不为null的属…...

SPSS教程:如何绘制带误差的折线图

SPSS教程&#xff1a;如何绘制带误差的折线图 1、问题与数据 研究者想研究45-65岁健康男性中&#xff0c;静坐时长和血胆固醇水平的关系&#xff0c;故招募100名研究对象询问其每天静坐时长&#xff08;time&#xff09;&#xff0c;并检测其血液中胆固醇水平&#xff08;cho…...

积分商城小程序如何精细化运营

随着移动互联网的发展&#xff0c;积分商城小程序成为了企业私域营销的重要组成部分。通过积分商城&#xff0c;企业可以激励用户参与、增加用户粘性&#xff0c;实现更好的用户互动和忠诚度提升。然而&#xff0c;要取得成功&#xff0c;积分商城小程序需要经过精细化的运营。…...

企业网络日志管理:EventLog Analyzer的卓越之处

企业网络日志管理对于维护网络安全、监控系统性能和合规性非常重要。随着网络规模和复杂性的增加&#xff0c;管理日志变得越来越具有挑战性。幸运的是&#xff0c;有一些优秀的工具可以帮助企业实现高效的日志管理。其中一款值得一提的工具是EventLog Analyzer。 一、EventLo…...

Python算法——滑动窗口问题

关于滑动窗口的概念&#xff0c;请自行到网上搜索相关资料&#xff0c;了解清楚再看本博客。 一、子组数最大平均数 LeetCode 第643题&#xff1a;https://leetcode.cn/problems/maximum-average-subarray-i/ 给你一个由 n 个元素组成的整数数组 nums 和一个整数 k 。 请你…...