通过mycat来学习java了^^。
接前一篇:http://blog.csdn.net/john_chang11/article/details/78734051
上一篇了解了XML解析的四种方式,并对MyCat的源码进行了修改,这一篇接着往下看:
dtd = XMLRuleLoader.class.getResourceAsStream(dtdFile);
xml = XMLRuleLoader.class.getResourceAsStream(xmlFile);
// 读取出语意树
Element root = ConfigUtil.getDocument(dtd, xml).getRootElement();
// 加载Function
loadFunctions(root);
// 加载TableRule
loadTableRules(root);
private void loadFunctions(Element root)
throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {
List list = root.elements("function");
for (Element e : list) {
// 获取name标签
String name = e.attributeValue("name");
// 如果Map已有,则function重复
if (functions.containsKey(name)) {
throw new ConfigException("rule function " + name + " duplicated!");
}
// 获取class标签
String clazz = e.attributeValue("class");
// 根据class利用反射新建分片算法
AbstractPartitionAlgorithm function = createFunction(name, clazz);
// 根据读取参数配置分片算法
ParameterMapping.mapping(function, ConfigUtil.loadElements(e));
// 每个AbstractPartitionAlgorithm可能会实现init来初始化
function.init();
// 放入functions map
functions.put(name, function);
}
}
这一段代码完成从XML文件里读出所有的function元素,对function类进行加载,并初始化,最后放到functions Map数组里供以后使用。这就涉及到JAVA的又一个知识点,类的动态加载和初始化。
先看一下MyCat是如何完成类的动态加载和初始化。
第一步:
createFunction(name, clazz);
看一下内容:
private AbstractPartitionAlgorithm createFunction(String name, String clazz)
throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {
Class> clz = Class.forName(clazz);
// 判断是否继承AbstractPartitionAlgorithm
if (!AbstractPartitionAlgorithm.class.isAssignableFrom(clz)) {
throw new IllegalArgumentException(
"rule function must implements " + AbstractPartitionAlgorithm.class.getName() + ", name=" + name);
}
return (AbstractPartitionAlgorithm) clz.newInstance();
}
该方法先用Class.forName()加载clazz参数指定的类,然后判断加载的类clz是不是AbstractPartitionAlgorithm类的子类,最后使用newInstance()方法初始化一个类对象。
第二步:
ParameterMapping.mapping(function, ConfigUtil.loadElements(e));
看一下内容:
public static void mapping(Object object, Map parameter) throws IllegalAccessException,
InvocationTargetException {
//获取用于导出clazz这个JavaBean的所有属性的PropertyDescriptor
PropertyDescriptor[] pds = getDescriptors(object.getClass());
for (int i = 0; i < pds.length; i++) {
PropertyDescriptor pd = pds[i];
Object obj = parameter.get(pd.getName());
Object value = obj;
Class> cls = pd.getPropertyType();
//类型转换
if (obj instanceof String) {
String string = (String) obj;
if (!StringUtil.isEmpty(string)) {
string = ConfigUtil.filter(string);
}
if (isPrimitiveType(cls)) {
value = convert(cls, string);
}
} else if (obj instanceof BeanConfig) {
value = createBean((BeanConfig) obj);
} else if (obj instanceof BeanConfig[]) {
List
private static PropertyDescriptor[] getDescriptors(Class> clazz) {
//PropertyDescriptor类表示JavaBean类通过存储器导出一个属性
PropertyDescriptor[] pds;
List list;
PropertyDescriptor[] pds2 = descriptors.get(clazz);
//该clazz是否第一次加载
if (null == pds2) {
try {
BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
pds = beanInfo.getPropertyDescriptors();
list = new ArrayList();
//加载每一个类型不为空的property
for (int i = 0; i < pds.length; i++) {
if (null != pds[i].getPropertyType()) {
list.add(pds[i]);
}
}
pds2 = new PropertyDescriptor[list.size()];
list.toArray(pds2);
} catch (IntrospectionException ie) {
LOGGER.error("ParameterMappingError", ie);
pds2 = new PropertyDescriptor[0];
}
}
descriptors.put(clazz, pds2);
return (pds2);
}
上面的方法就是先获得类的属性,再为属性赋值。看起来有点神乎,一会再看到底是怎么会事。先来看MyCat类加载的最后一步。
第三步:
function.init();
这一步就是调用加载的类的对象的init()方法,该方法不同类有不同内容,以io.mycat.route.function.PartitionByLong类为例,init()方法内容为:
private static int[] toIntArray(String string) {
String[] strs = io.mycat.util.SplitUtil.split(string, ',', true);
int[] ints = new int[strs.length];
for (int i = 0; i < strs.length; ++i) {
ints[i] = Integer.parseInt(strs[i]);
}
return ints;
}
该方法的内容,就完成和业务有关了,或者说和该分表函数的特点有关了。
MyCat通过上面三步,完成分表函数的动态加载和初始化,一共涉及到三个Java知识点:类的动态加载(也就是类的运行时加载)、Java内省、Java反射机制,现在就来看看Java的这部分内容。
本文部分内容引用网络文章:http://wiki.jikexueyuan.com/project/java-vm/java-debug.html
本图片引自网络文章:http://wiki.jikexueyuan.com/project/java-vm/java-debug.html
加载过程中会先检查类是否被已加载,检查顺序是自底向上,从 Custom ClassLoader 到 BootStrap ClassLoader 逐层检查,只要某个 Classloader 已加载就视为已加载此类,保证此类只所有 ClassLoader加载一次。而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。
代码验证:
package io.mycat.test.clss;
public class ClssLdr {
public static void main(String[] args) throws ClassNotFoundException {
// 获取默认的系统类加载器,即应用程序类加载器:App ClassLoader
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
System.out.println("应用程序类加载器:" + classLoader);
// 获取应用类加载器的父类加载器,即扩展类加载器:Extension ClassLoader
classLoader = classLoader.getParent();
System.out.println("扩展类加载器:" + classLoader);
// 获取扩展类加载器的父类加载器,即启动类加载器:Bootstrap ClassLoader
classLoader = classLoader.getParent();
System.out.println("启动类加载器:" + classLoader); // 输出为Null,无法被Java程序直接引用
// 获取当前类的类加载器,应该是应用程序类加载器
classLoader = ClssLdr.class.getClassLoader();
System.out.println("当前类的类加载器:" + classLoader);
classLoader = Class.forName("io.mycat.test.clss.ClssLdr").getClassLoader();
System.out.println("当前类的类加载器:" + classLoader);
// 获取JDK提供的Object类的类加载器,应该启动类加载器
classLoader = Class.forName("java.lang.Object").getClassLoader();
System.out.println("JDK提供的Object类的类加载器:" + classLoader); // 输出为Null
// 启动类加载器BootstrapLoader寻找类的路径
System.out.println("启动类加载器寻找类的路径:" + System.getProperty("sun.boot.class.path"));
// 扩展类加载器ExtClassLoader寻找类的路径
System.out.println("扩展类加载器寻找类的路径:" + System.getProperty("java.ext.dirs"));
// 应用程序类加载器AppClassLoader寻找类的路径
System.out.println("应用程序类加载器寻找类的路径:" + System.getProperty("java.class.path"));
}
}
输出结果:
应用程序类加载器:sun.misc.Launcher$AppClassLoader@1d16e93
扩展类加载器:sun.misc.Launcher$ExtClassLoader@1db9742
启动类加载器:null
当前类的类加载器:sun.misc.Launcher$AppClassLoader@1d16e93
当前类的类加载器:sun.misc.Launcher$AppClassLoader@1d16e93
JDK提供的Object类的类加载器:null
启动类加载器寻找类的路径:D:\jre1.8\lib\resources.jar;D:\jre1.8\lib\rt.jar;...
扩展类加载器寻找类的路径:D:\jre1.8\lib\ext;C:\Windows\Sun\Java\lib\ext
应用程序类加载器寻找类的路径:E:\java\mycat1.6\bin;E:\java\mycat1.6\lib\ehcache-core-2.6.11.jar;...
扩展阅读:类的加载过程
该部分内容引自网络文章:http://wiki.jikexueyuan.com/project/java-vm/java-debug.html,略有修正
类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载七个阶段。它们开始的顺序如下图所示:
其中类加载的过程包括了加载、验证、准备、解析、初始化五个阶段。在这五个阶段中,加载、验证、准备和初始化这四个阶段发生的顺序是确定的,而解析阶段则不一定,它在某些情况下可以在初始化阶段之后开始,这是为了支持 Java 语言的运行时绑定(也成为动态绑定或晚期绑定)。另外注意这里的几个阶段是按顺序开始,而不是按顺序进行或完成,因为这些阶段通常都是互相交叉地混合进行的,通常在一个阶段执行的过程中调用或激活另一个阶段。
这里简要说明下 Java 中的绑定:绑定指的是把一个方法的调用与方法所在的类(方法主体)关联起来,对 Java 来说,绑定分为静态绑定和动态绑定:
下面详细讲述类加载过程中每个阶段所做的工作。
加载时类加载过程的第一个阶段,在加载阶段,虚拟机需要完成以下三件事情:
注意,这里第 1 条中的二进制字节流并不只是单纯地从 Class 文件中获取,比如它还可以从 Jar 包中获取、从网络中获取(最典型的应用便是 Applet)、由其他文件生成(JSP 应用)等。
相对于类加载的其他阶段而言,加载阶段(准确地说,是加载阶段获取类的二进制字节流的动作)是可控性最强的阶段,因为开发人员既可以使用系统提供的类加载器来完成加载,也可以自定义自己的类加载器来完成加载。
加载阶段完成后,虚拟机外部的 二进制字节流就按照虚拟机所需的格式存储在方法区之中,而且在 Java 堆中也创建一个 java.lang.Class 类的对象,这样便可以通过该对象访问方法区中的这些数据。
说到加载,不得不提到类加载器,下面就具体讲述下类加载器。
类加载器虽然只用于实现类的加载动作,但它在 Java 程序中起到的作用却远远不限于类的加载阶段。对于任意一个类,都需要由它的类加载器和这个类本身一同确定其在就 Java 虚拟机中的唯一性,也就是说,即使两个类来源于同一个 Class 文件,只要加载它们的类加载器不同,那这两个类就必定不相等。这里的“相等”包括了代表类的 Class 对象的 equals()、isAssignableFrom()、isInstance()等方法的返回结果,也包括了使用 instanceof 关键字对对象所属关系的判定结果。
站在 Java 虚拟机的角度来讲,只存在两种不同的类加载器:
这种层次关系称为类加载器的双亲委派模型。我们把每一层上面的类加载器叫做当前层类加载器的父加载器,当然,它们之间的父子关系并不是通过继承关系来实现的,而是使用组合关系来复用父加载器中的代码。该模型在 JDK1.2 期间被引入并广泛应用于之后几乎所有的 Java 程序中,但它并不是一个强制性的约束模型,而是 Java 设计者们推荐给开发者的一种类的加载器实现方式。
双亲委派模型的工作流程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。
使用双亲委派模型来组织类加载器之间的关系,有一个很明显的好处,就是 Java 类随着它的类加载器(说白了,就是它所在的目录)一起具备了一种带有优先级的层次关系,这对于保证 Java 程序的稳定运作很重要。例如,类java.lang.Object 类存放在JDK\jre\lib下的 rt.jar 之中,因此无论是哪个类加载器要加载此类,最终都会委派给启动类加载器进行加载,这便保证了 Object 类在程序中的各种类加载器中都是同一个类
准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中分配。对于该阶段有以下几点需要注意:
public static int value = 3;
那么变量 value 在准备阶段过后的初始值为 0,而不是 3,因为这时候尚未开始执行任何 Java 方法,而把 value 赋值为 3 的 putstatic 指令是在程序编译后,存放于类构造器 ()方法之中的,所以把 value 赋值为 3 的动作将在初始化阶段才会执行。
public static final int value = 3;
class Super{
public static int m = 11;
static{
System.out.println("执行了super类静态语句块");
}
}
class Father extends Super{
public static int m = 33;
static{
System.out.println("执行了父类静态语句块");
}
}
class Child extends Father{
static{
System.out.println("执行了子类静态语句块");
}
}
public class StaticTest{
public static void main(String[] args){
System.out.println(Child.m);
}
}
执行结果如下:
执行了super类静态语句块
执行了父类静态语句块
33
如果注释掉 Father 类中对 m 定义的那一行,则输出结果如下:
执行了super类静态语句块
11
static 变量发生在静态解析阶段,也即是初始化之前,此时已经将字段的符号引用转化为了内存引用,也便将它与对应的类关联在了一起,由于在子类中没有查找到与 m 相匹配的字段,那么 m 便不会与子类关联在一起,因此并不会触发子类的初始化。
StaticTest.java:24: 对 m 的引用不明确,Father 中的 变量 m 和 Super 中的 变量 m
都匹配
System.out.println(Child.m);
^
1 错误
class Father{
public static int a = 1;
static{
a = 2;
}
}
class Child extends Father{
public static int b = a;
}
public class ClinitTest{
public static void main(String[] args){
System.out.println(Child.b);
}
}
执行上面的代码,会打印出 2,也就是说 b 的值被赋为了 2。方法摘要 | ||
---|---|---|
|
asSubclass(Class clazz) 强制转换该 Class 对象,以表示指定的 class 对象所表示的类的一个子类。 |
|
T |
cast(Object obj) 将一个对象强制转换成此 Class 对象所表示的类或接口。 |
|
boolean |
desiredAssertionStatus() 如果要在调用此方法时将要初始化该类,则返回将分配给该类的断言状态。 |
|
static Class> |
forName(String className) 返回与带有给定字符串名的类或接口相关联的 Class 对象。 |
|
static Class> |
forName(String name, boolean initialize, ClassLoader loader) 使用给定的类加载器,返回与带有给定字符串名的类或接口相关联的 Class 对象。 |
|
|
getAnnotation(Class annotationClass) 如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null。 |
|
Annotation[] |
getAnnotations() 返回此元素上存在的所有注释。 |
|
String |
getCanonicalName() 返回 Java Language Specification 中所定义的底层类的规范化名称。 |
|
Class>[] |
getClasses() 返回一个包含某些 Class 对象的数组,这些对象表示属于此 Class 对象所表示的类的成员的所有公共类和接口。 |
|
ClassLoader |
getClassLoader() 返回该类的类加载器。 |
|
Class> |
getComponentType() 返回表示数组组件类型的 Class 。 |
|
Constructor |
getConstructor(Class>... parameterTypes) 返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。 |
|
Constructor>[] |
getConstructors() 返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。 |
|
Annotation[] |
getDeclaredAnnotations() 返回直接存在于此元素上的所有注释。 |
|
Class>[] |
getDeclaredClasses() 返回 Class 对象的一个数组,这些对象反映声明为此 Class 对象所表示的类的成员的所有类和接口。 |
|
Constructor |
getDeclaredConstructor(Class>... parameterTypes) 返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。 |
|
Constructor>[] |
getDeclaredConstructors() 返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。 |
|
Field |
getDeclaredField(String name) 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。 |
|
Field[] |
getDeclaredFields() 返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。 |
|
Method |
getDeclaredMethod(String name, Class>... parameterTypes) 返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。 |
|
Method[] |
getDeclaredMethods() 返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。 |
|
Class> |
getDeclaringClass() 如果此 Class 对象所表示的类或接口是另一个类的成员,则返回的 Class 对象表示该对象的声明类。 |
|
Class> |
getEnclosingClass() 返回底层类的立即封闭类。 |
|
Constructor> |
getEnclosingConstructor() 如果该 Class 对象表示构造方法中的一个本地或匿名类,则返回 Constructor 对象,它表示底层类的立即封闭构造方法。 |
|
Method |
getEnclosingMethod() 如果此 Class 对象表示某一方法中的一个本地或匿名类,则返回 Method 对象,它表示底层类的立即封闭方法。 |
|
T[] |
getEnumConstants() 如果此 Class 对象不表示枚举类型,则返回枚举类的元素或 null。 |
|
Field |
getField(String name) 返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。 |
|
Field[] |
getFields() 返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。 |
|
Type[] |
getGenericInterfaces() 返回表示某些接口的 Type,这些接口由此对象所表示的类或接口直接实现。 |
|
Type |
getGenericSuperclass() 返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的直接超类的 Type。 |
|
Class>[] |
getInterfaces() 确定此对象所表示的类或接口实现的接口。 |
|
Method |
getMethod(String name, Class>... parameterTypes) 返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。 |
|
Method[] |
getMethods() 返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。 |
|
int |
getModifiers() 返回此类或接口以整数编码的 Java 语言修饰符。 |
|
String |
getName() 以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。 |
|
Package |
getPackage() 获取此类的包。 |
|
ProtectionDomain |
getProtectionDomain() 返回该类的 ProtectionDomain 。 |
|
URL |
getResource(String name) 查找带有给定名称的资源。 |
|
InputStream |
getResourceAsStream(String name) 查找具有给定名称的资源。 |
|
Object[] |
getSigners() 获取此类的标记。 |
|
String |
getSimpleName() 返回源代码中给出的底层类的简称。 |
|
Class super T> |
getSuperclass() 返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class 。 |
|
TypeVariable |
getTypeParameters() 按声明顺序返回 TypeVariable 对象的一个数组,这些对象表示用此 GenericDeclaration 对象所表示的常规声明来声明的类型变量。 |
|
boolean |
isAnnotation() 如果此 Class 对象表示一个注释类型则返回 true。 |
|
boolean |
isAnnotationPresent(Class extends Annotation> annotationClass) 如果指定类型的注释存在于此元素上,则返回 true,否则返回 false。 |
|
boolean |
isAnonymousClass() 当且仅当底层类是匿名类时返回 true。 |
|
boolean |
isArray() 判定此 Class 对象是否表示一个数组类。 |
|
boolean |
isAssignableFrom(Class> cls) 判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口。 |
|
boolean |
isEnum() 当且仅当该类声明为源代码中的枚举时返回 true。 |
|
boolean |
isInstance(Object obj) 判定指定的 Object 是否与此 Class 所表示的对象赋值兼容。 |
|
boolean |
isInterface() 判定指定的 Class 对象是否表示一个接口类型。 |
|
boolean |
isLocalClass() 当且仅当底层类是本地类时返回 true。 |
|
boolean |
isMemberClass() 当且仅当底层类是成员类时返回 true。 |
|
boolean |
isPrimitive() 判定指定的 Class 对象是否表示一个基本类型。 |
|
boolean |
isSynthetic() 如果此类是复合类,则返回 true,否则 false。 |
|
T |
newInstance() 创建此 Class 对象所表示的类的一个新实例。 |
|
String |
toString() 将对象转换为字符串。 |
package io.mycat.route.function;
import io.mycat.config.model.rule.RuleAlgorithm;
import io.mycat.route.parser.util.Pair;
import io.mycat.route.util.PartitionUtil;
import io.mycat.util.StringUtil;
public final class PartitionByString extends AbstractPartitionAlgorithm implements RuleAlgorithm {
private int hashSliceStart = 0;
private int hashSliceEnd = 8;
protected int[] count;
protected int[] length;
protected PartitionUtil partitionUtil;
public PartitionByString() {
super();
}
public PartitionByString(int hashSliceStart, int hashSliceEnd, int[] count, int[] length) {
super();
this.hashSliceStart = hashSliceStart;
this.hashSliceEnd = hashSliceEnd;
this.count = count;
this.length = length;
}
public void setPartitionCount(String partitionCount) {
this.count = toIntArray(partitionCount);
}
public void setPartitionLength(String partitionLength) {
this.length = toIntArray(partitionLength);
}
public void setHashLength(int hashLength) {
setHashSlice(String.valueOf(hashLength));
}
public void setHashSlice(String hashSlice) {
Pair p = sequenceSlicing(hashSlice);
hashSliceStart = p.getKey();
hashSliceEnd = p.getValue();
}
public static Pair sequenceSlicing(String slice) {
...
return new Pair(start, end);
}
@Override
public void init() {
partitionUtil = new PartitionUtil(count, length);
}
private static int[] toIntArray(String string) {
String[] strs = io.mycat.util.SplitUtil.split(string, ',', true);
int[] ints = new int[strs.length];
for (int i = 0; i < strs.length; ++i) {
ints[i] = Integer.parseInt(strs[i]);
}
return ints;
}
@Override
public Integer calculate(String key) {
int start = hashSliceStart >= 0 ? hashSliceStart : key.length() + hashSliceStart;
int end = hashSliceEnd > 0 ? hashSliceEnd : key.length() + hashSliceEnd;
long hash = StringUtil.hash(key, start, end);
return partitionUtil.partition(hash);
}
@Override
public int getPartitionNum() {
int nPartition = 0;
for (int i = 0; i < count.length; i++) {
nPartition += count[i];
}
return nPartition;
}
}
package io.mycat.test.clss;
public class Reflection {
public static void main(String[] args)
throws ClassNotFoundException, InstantiationException, IllegalAccessException {
// String className=从配置文件读取类名;
// 加载
// 假设从配置文件读到的类名为io.mycat.route.function.PartitionByString
Class> clazz = Class.forName("io.mycat.route.function.PartitionByString");// 由于类在程序启动时不存在,所以只能用这种方式加载
}
}
package io.mycat.test.clss;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import io.mycat.config.util.ConfigException;
public class Reflection {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException,
IllegalAccessException, IllegalArgumentException, InvocationTargetException {x
Class> clazz = Class.forName("io.mycat.route.function.PartitionByString");// 由于类在程序启动时不存在,所以只能用这种方式加载
// 获取构造方法
Constructor[] constructors = clazz.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
if (constructor.getParameterCount() == 0) {
System.out.println("无参构造方法。");
Object obj = constructor.newInstance();
System.out.println("无参构造实例完成。");
} else {
System.out.println("有参构造方法。");
Parameter[] pts = constructor.getParameters();
String strValue;
Object[] values = new Object[constructor.getParameterCount()];
int idx = 0;
for (Parameter pt : pts) {
System.out.println(" 参数类型:" + pt.getParameterizedType().getTypeName() + " 参数名称:" + pt.getName());
Class> type = pt.getType();
String name = pt.getName();
strValue = "0";// 可以根据参数名称pt.getName(),从配置文件等地方获得参数值,为了重点突出,这里不再提供相关代码,而直接用字符串"0"代替
if (type.equals(String.class)) {
values[idx++] = strValue;
} else if (type.equals(Boolean.TYPE)) {
values[idx++] = Boolean.valueOf(strValue);
} else if (type.equals(Byte.TYPE)) {
values[idx++] = Byte.valueOf(strValue);
} else if (type.equals(Short.TYPE)) {
values[idx++] = Short.valueOf(strValue);
} else if (type.equals(Integer.TYPE)) {
values[idx++] = Integer.valueOf(strValue);
} else if (type.equals(Long.TYPE)) {
values[idx++] = Long.valueOf(strValue);
} else if (type.equals(Double.TYPE)) {
values[idx++] = Double.valueOf(strValue);
} else if (type.equals(Float.TYPE)) {
values[idx++] = Float.valueOf(strValue);
} else if (type.equals(Class.class)) {
try {
values[idx++] = Class.forName(strValue);
} catch (ClassNotFoundException e) {
throw new ConfigException(e);
}
} else {
values[idx++] = null;
}
}
Object obj = constructor.newInstance(values);
System.out.println("有参构造实例完成。");
}
}
// 无参构造实例快捷方式
// 实例化一个类对象,由于不知道是个什么,但不管是什么类,必然继承自java.lang.Object类,所以可以用Object类型变量接收实例化的对象
Object obj = clazz.newInstance();// 这种方式实例化,调用的是类的无参构造器,所以PartitionByString类必须有公开的无参构造方法
// 1、获取字段
// 1.1 获取Field的数组,私有字段也能获取
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println(Modifier.toString(field.getModifiers()) + " " + field.getGenericType().getTypeName()
+ " " + field.getName());
}
Object obj2 = clazz.newInstance(0,0);
}
}
public io.mycat.route.function.PartitionByString()
无参构造方法。
无参构造实例完成。
public io.mycat.route.function.PartitionByString(int,int)
有参构造方法。
参数类型:int 参数名称:hashSliceStart
参数类型:int 参数名称:hashSliceEnd
有参构造实例完成。
package io.mycat.test.clss;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import io.mycat.config.util.ConfigException;
public class Reflection {
public static void main(String[] args)
throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException,
InvocationTargetException, NoSuchMethodException, SecurityException, NoSuchFieldException {
Class> clazz = Class.forName("io.mycat.route.function.PartitionByString");// 由于类在程序启动时不存在,所以只能用这种方式加载
// 实例化一个对象
Constructor constr = clazz.getConstructor(int.class, int.class);
Object obj2 = constr.newInstance(10, 20);
// 获取字段
// 获取Field的数组,私有字段也能获取
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println(Modifier.toString(field.getModifiers()) + " " + field.getGenericType().getTypeName()
+ " " + field.getName());
}
// 获取指定字段,假设已经知道了字段名
Field field = clazz.getDeclaredField("hashSliceStart");
field.setAccessible(true); // 由于是私有字段,需要先为可访问,否则无法读和写
Object val = field.get(obj2);
System.out.println("获取指定对象字段'hashSliceStart'的Field的值: " + val);
// 修改字段值
field.set(obj2, 30);
val = field.get(obj2);
System.out.println("修改后指定对象字段'hashSliceStart'的Field的值: " + val);
}
}
执行结果:
private int hashSliceStart
private int hashSliceEnd
protected int[] count
protected int[] length
protected io.mycat.route.util.PartitionUtil partitionUtil
获取指定对象字段'hashSliceStart'的Field的值: 10
修改后指定对象字段'hashSliceStart'的Field的值: 30
package io.mycat.test.clss;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import io.mycat.config.util.ConfigException;
public class Reflection {
public static void main(String[] args)
throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException,
InvocationTargetException, NoSuchMethodException, SecurityException, NoSuchFieldException {
Class> clazz = Class.forName("io.mycat.route.function.PartitionByString");// 由于类在程序启动时不存在,所以只能用这种方式加载
// 实例化一个对象
Constructor constr = clazz.getConstructor(int.class, int.class);
Object obj2 = constr.newInstance(10, 20);
// 获取本类声明的所有方法(包括private方法,但不包括父类方法)
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println(Modifier.toString(method.getModifiers()) + " " + method.getReturnType().getSimpleName()
+ " " + method.getName());
}
// 获取指定的方法,假设已经知道了方法名
Method method = clazz.getDeclaredMethod("getHashSliceStart");// 第一个参数是方法名,后面的是方法里的参数
method.setAccessible(true); // 由于是私有方法,需要先设置为可访问,否则无法调用
Object value = method.invoke(obj2);
System.out.println("set方法调用前hashSliceStart : " + value);
Method method2 = clazz.getDeclaredMethod("setHashSliceStart", int.class);// 第一个参数是方法名,后面的是方法里的参数
// method2.setAccessible(true);// 由于是公有方法,无需先设置为可访问
method2.invoke(obj2, 30);
value = method.invoke(obj2);
System.out.println("set方法调用后hashSliceStart : " + value);
}
}
执行结果为:
public void init
private static int[] toIntArray
public void setHashLength
public void setHashSlice
public static Pair sequenceSlicing
public Integer calculate
public int getPartitionNum
private int getHashSliceStart
public void setHashSliceStart
public void setPartitionCount
public void setPartitionLength
set方法调用前hashSliceStart : 10
set方法调用后hashSliceStart : 30
- @Override:限定重写父类方法,该注释只能用于方法
- @Deprecated:用于表示某个程序元素(类,方法等)已过时
- @SuppressWarnings:抑制编译器警告.
package io.mycat.test.annotation;
import java.util.Date;
@Table("ruser")
public class User {
@Field(name = "id", type = "bigint")
private long id;
@Field(name = "user_name", type = "varchar")
private String name;
@Field(name = "user_name", type = "datetime")
private Date regDate;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getRegDate() {
return regDate;
}
public void setRegDate(Date regDate) {
this.regDate = regDate;
}
}
上面代码里用到两个Annotation(注解):Table和Field,这两个注解的定义如下:
package io.mycat.test.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.ElementType;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Table {
public String value() default ""; //表名
}
注解Field:
package io.mycat.test.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.ElementType;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Field {
public String name() default ""; // 字段名称
public String type() default ""; // 字段类型
}
这两个都是自定义注解,里面有一些奇怪的格式和内容,下面就来看看如何自定义注解。在定义自己的注解之前,先要了解元注解和相关定义注解的语法。
@Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。
作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
取值(ElementType)有:
- CONSTRUCTOR:用于描述构造器
- FIELD:用于描述域
- LOCAL_VARIABLE:用于描述局部变量
- METHOD:用于描述方法
- PACKAGE:用于描述包
- PARAMETER:用于描述参数
- TYPE:用于描述类、接口(包括注解类型) 或enum声明
默认值:未标注则表示可修饰所有。
@Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation,有些可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个元注解可以对 Annotation的“生命周期”限制。
作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)
取值(RetentionPoicy)有:
- SOURCE:在源文件中有效(即源文件保留),值为 SOURCE 大都为 Mark Annotation,这类 Annotation 大都用来校验,比如 Override, Deprecated, SuppressWarnings
- CLASS:在class文件中有效(即class保留)
- RUNTIME:在运行时有效(即运行时保留)
默认值:CLASS
@Documented用于表示该Annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。@Documented是一个标记注解,没有成员。
@Inherited用于表示该Annotation是可以被继承的。如果一个使用了@Inherited修饰的Annotation类型被用于一个class,则这个Annotation将被用于该class的子类。注意:@Inherited Annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承Annotation,方法并不从它所重载的方法继承annotation。当@Inherited Annotation类型的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited Annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。@Inherited 元注解是一个标记注解,没有成员。
public @interface 注解名 {定义体}
定义体里定义注解的参数,参数名为方法名,且:
- 所有方法没有方法体,没有参数,只允许用public或默认(default)这两个访问权修饰,不允许抛异常。例如,String value();这里把方法设为defaul默认类型
- 方法返回值只能是int,float,boolean,byte,double,char,long,short,String, Class, annotation, enumeration 或者是他们的一维数组
- 若只有一个参数成员,最好使用 value() 函数。一个属性都没有表示该 Annotation 为 Mark Annotation
- 可以加 default 表示默认值
package io.mycat.test.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.ElementType;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Field {
public String name() default ""; // 字段名称
public String type() default ""; // 字段类型
}
package io.mycat.test.annotation;
import java.lang.reflect.Field;
public class UserAnn {
public static void main(String[] args) throws ClassNotFoundException {
// 获得类
// Class extends User> clazz = new User().getClass(); //获取类的第一种方式
Class clazz = User.class; // 获取类的第二种方式
// Class> clazz = Class.forName("io.mycat.test.annotation.User"); //获取类的第三种方式
// 读取类的注释
// Annotation[] anns = obj.getClass().getAnnotations(); //得到所有注释
Table table = clazz.getAnnotation(Table.class); // 取得指定注释
System.out.println("类【" + clazz.getSimpleName() + "】注释(表名): " + table.value());
// 读取属性的注释
Field[] fields = clazz.getDeclaredFields();
for (Field f : fields) {
// Annotation[] anns2 = f.getAnnotations(); //得到所有注释
Column column = f.getAnnotation(Column.class);// 取得指定注释
if (column != null) {
System.out.println("属性【" + f.getName() + "】注释(列名--类型): " + column.name() + " -- " + column.type());
}
}
}
}
执行结果:
类【User】注释(表名): ruser
属性【id】注释(列名--类型): id -- bigint
属性【name】注释(列名--类型): user_name -- varchar
属性【regDate】注释(列名--类型): user_name -- datetime
现在应该稍微理解了一点注解的用途了吧,例如上面的User类与数据库的ruser表对应,类的属性与表字段对应,如果数据库的表名修改了,或者字段名修改了,那么类及其属性要重新与数据库表及其字段对应,如果使用注解,则只需要修改注解即可,不用修改程序代码。从这一方面来看,注解就相当于配置文件一样的存在,只是不再需要单独的配置文件,而是将配置信息与Java代码合并在一起,更易理解和维护。
package io.mycat.test.annotation;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
@SupportedAnnotationTypes({ "io.mycat.test.annotation.User" })
public class UserProcessor extends AbstractProcessor {
@Override
public boolean process(Set extends TypeElement> annotations, RoundEnvironment env) {
for (TypeElement te : annotations) {
for (Element element : env.getElementsAnnotatedWith(te)) {
Table table = element.getAnnotation(Table.class);
System.out.println(element.getEnclosingElement().toString() + " : " + table.value());
}
}
return true;
}
}
package io.mycat.test.bean;
public class Partition {
private int start;
private int end;
public int getStart() {
return start;
}
public void setStart(int start) {
this.start = start;
}
public int getEnd() {
return end;
}
public void setEnd(int end) {
this.end = end;
}
}
看一下内省的使用:
package io.mycat.test.bean;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class PartitionParse {
public static void main(String[] args)
throws IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
BeanInfo beanInfo = Introspector.getBeanInfo(Partition.class); // 通过Introspector取得BeanInfo
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors(); // 通过BeanInfo取得PropertyDescriptors(所有属性)
Partition partition = new Partition(); // 实例化一个对象,方法调用时会用到
for (PropertyDescriptor pd : pds) { // 循环每一个属性
if (pd.getName().equals("start")) { // 找到名为"start"的属性
Method rMethod = pd.getReadMethod(); // 通过PropertyDescriptor取得get方法
System.out.println("start修改前值:" + rMethod.invoke(partition, null)); // 调用get方法
Method wMethod = pd.getWriteMethod(); // 通过PropertyDescriptor取得set方法
wMethod.invoke(partition, 10); // 调用set方法
System.out.println("start修改后值:" + rMethod.invoke(partition, null)); // 再次调用get方法,查看修改后的值
}
}
}
}
start修改前值:0
start修改后值:10
通过上面的代码可知,内省可以获取类的属性,并通过属性获取get/set方法,但方法的调用依然需要用到反射机制。内省是根据什么获取属性的呢,是不是根据反射后的Field获取的呢,接着看,这次我们把Partition类的所有字段通过内省都打印出来:
package io.mycat.test.bean;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.MethodDescriptor;
import java.beans.PropertyDescriptor;
public class PartitionParse {
public static void main(String[] args) throws IntrospectionException {
BeanInfo beanInfo = Introspector.getBeanInfo(Partition.class);
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : pds) {
System.out.println(pd.getPropertyType().getName() + " " + pd.getName()); //打印所有属性
}
}
}
招行结果:
java.lang.Class class
int end
int start
多了一个class属性,上面定义的Partition类里只有start、end两个属性,怎么会多出一个class,再来看一下通过内省获取的所有方法:
package io.mycat.test.bean;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.MethodDescriptor;
import java.beans.PropertyDescriptor;
public class PartitionParse {
public static void main(String[] args) throws IntrospectionException {
BeanInfo beanInfo = Introspector.getBeanInfo(Partition.class);
MethodDescriptor[] mds = beanInfo.getMethodDescriptors();
for (MethodDescriptor md : mds) {
System.out.println(md.getMethod().getReturnType().getSimpleName() + " " + md.getName()); // 打印所有方法
}
}
}
招行结果:
Class getClass
int getStart
void setStart
void setEnd
int getEnd
void wait
void notifyAll
void notify
void wait
int hashCode
void wait
boolean equals
String toString
上面结果里get/set方法共有五个:getClass、getStart、setStart、getEnd、setEnd,将相同名称合并后只有三个:class、start、end,这三个正好与上面的通过内省获取的属性一致,上面属性里多出的class,正好对应下面方法里的getClass。所以,
通过内省获得的属性,是通过对get/set方法的解析取得的,并不一定真正是类的一个属性。实际上也的确是这样,通过查看内省的源码可知,只要有get/set方法里的任意一个,就会把这个方法名(去掉get/set后)提取成一个属性,如果get/set两个方法都存在,就合并成一个属性。下面来验证一下,把Partition类修改成如下:
package io.mycat.test.bean;
public class Partition {
private int middle;
public int getStart() {
return 1;
}
public void setEnd(int middle) {
}
public int middle() {
return 1;
}
}
package io.mycat.test.bean;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.MethodDescriptor;
import java.beans.PropertyDescriptor;
public class PartitionParse {
public static void main(String[] args) throws IntrospectionException {
BeanInfo beanInfo = Introspector.getBeanInfo(Partition.class);
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : pds) {
System.out.println(pd.getPropertyType().getName() + " " + pd.getName()); // 打印所有属性
}
}
}
招行结果:
java.lang.Class class
int end
int start
依然解析出start、end两个参数,且类型为int。由此可知,
通过内省获取属性,是通过解析get/set方法取得的,属性类型是通过get方法的返回类型或set方法的参数类型来确定的,而与类里是否真正定义过这个属性无关。
这样看内省似乎又违背Bean的规则,Bean要求属性和get/set方法并存,而内省是只考虑方法,不考虑真正定义过什么属性。
package io.mycat.test.bean;
public class Partition {
public void getStart() {
// return 1;
}
public void setEnd() {
}
public void setMiddle(int m, int n) {
}
}
class java.lang.Class class
null middle
start、end已经消失,而middle(注意方法里加上set前缀)虽是属性但没有类型(类型为null)。可见,内省只有通过标准的Bean方法才能解析出标准的属性来。
private AbstractPartitionAlgorithm createFunction(String name, String clazz)
throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {
Class> clz = Class.forName(clazz);
// 判断是否继承AbstractPartitionAlgorithm
if (!AbstractPartitionAlgorithm.class.isAssignableFrom(clz)) {
throw new IllegalArgumentException(
"rule function must implements " + AbstractPartitionAlgorithm.class.getName() + ", name=" + name);
}
return (AbstractPartitionAlgorithm) clz.newInstance();
}
先通过Class.forName(clazz)加载类,再使用Class的isAssignableFrom(clz)来判断加载的类是否是AbstractPartitionAlgorithm的子类或实现,最后返回类实例。通过上面的知识介绍,现在好理解了,再看下一步:
public static void mapping(Object object, Map parameter) throws IllegalAccessException,
InvocationTargetException {
//获取用于导出clazz这个JavaBean的所有属性的PropertyDescriptor
PropertyDescriptor[] pds = getDescriptors(object.getClass());
for (int i = 0; i < pds.length; i++) {
PropertyDescriptor pd = pds[i];
Object obj = parameter.get(pd.getName());
Object value = obj;
Class> cls = pd.getPropertyType();
//类型转换
if (obj instanceof String) {
String string = (String) obj;
if (!StringUtil.isEmpty(string)) {
string = ConfigUtil.filter(string);
}
if (isPrimitiveType(cls)) {
value = convert(cls, string);
}
} else if (obj instanceof BeanConfig) {
value = createBean((BeanConfig) obj);
} else if (obj instanceof BeanConfig[]) {
List list = new ArrayList();
for (BeanConfig beanconfig : (BeanConfig[]) obj) {
list.add(createBean(beanconfig));
}
value = list.toArray();
}
//赋值
if (cls != null
&& value != null) {
Method method = pd.getWriteMethod();
if (method != null) {
method.invoke(object, new Object[] { value });
}
}
}
}
private static PropertyDescriptor[] getDescriptors(Class> clazz) {
// PropertyDescriptor类表示JavaBean类通过存储器导出一个属性
PropertyDescriptor[] pds;
List list;
PropertyDescriptor[] pds2 = descriptors.get(clazz);
// 该clazz是否第一次加载
if (null == pds2) {
try {
BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
pds = beanInfo.getPropertyDescriptors();
list = new ArrayList();
// 加载每一个类型不为空的property
for (int i = 0; i < pds.length; i++) {
if (null != pds[i].getPropertyType()) {
list.add(pds[i]);
}
}
pds2 = new PropertyDescriptor[list.size()];
list.toArray(pds2);
} catch (IntrospectionException ie) {
LOGGER.error("ParameterMappingError", ie);
pds2 = new PropertyDescriptor[0];
}
}
descriptors.put(clazz, pds2);
return (pds2);
}
package io.mycat.route.function;
import io.mycat.config.model.rule.RuleAlgorithm;
import io.mycat.route.parser.util.Pair;
import io.mycat.route.util.PartitionUtil;
import io.mycat.util.StringUtil;
public final class PartitionByString extends AbstractPartitionAlgorithm implements RuleAlgorithm {
private int hashSliceStart = 0;
private int hashSliceEnd = 8;
protected int[] count;
protected int[] length;
protected PartitionUtil partitionUtil;
public void setPartitionCount(String partitionCount) {
this.count = toIntArray(partitionCount);
}
public void setPartitionLength(String partitionLength) {
this.length = toIntArray(partitionLength);
}
public void setHashLength(int hashLength) {
setHashSlice(String.valueOf(hashLength));
}
public void setHashSlice(String hashSlice) {
Pair p = sequenceSlicing(hashSlice);
hashSliceStart = p.getKey();
hashSliceEnd = p.getValue();
}
public static Pair sequenceSlicing(String slice) {
...
return new Pair(start, end);
}
@Override
public void init() {
partitionUtil = new PartitionUtil(count, length);
}
private static int[] toIntArray(String string) {
...
return ints;
}
@Override
public Integer calculate(String key) {
...
return partitionUtil.partition(hash);
}
@Override
public int getPartitionNum() {
...
return nPartition;
}
}
这个类里有setPartitionCount(String partitionCount),setPartitionLength(String partitionLength)这样的set方法,但却没有定义这样的属性,所以这个类不是标准的Bean类,之所以设置这两个方法,是因为这个分表函数对应的配置文件里有这样的字段:
512
2
0:2
配置文件里有partitionCount、partitionLength这样字段。MyCat首先通过内省获知此分表类有这两个属性(实际上是没有,由于内省是通过方法来确定属性的,而类中又有这两个字段的set方法,所以就认为类有这两个属性),然后通过属性名从配置文件里取相应的值,再调用属性的set方法为属性设置值,由于类里实际上没有这两个属性,所以set方法里实际上是为类真正存在的属性赋值,虽然这样也能执行,也能达到相同的目的,但本人认为在这里提现“技术的高明”并不合适。MyCat作为一个通用的开源中间件,其代码应该规范,内省就应该只用于解析标准的Bean类,避免未知的异常(虽然现在只有一个异常,就是上面提到的属性类型为null的情况,而且MyCat也处理了这种异常,但本人依然认为MyCat的这种处理方式不可取)。
public static void mapping2(Object object, Map parameter)
throws IllegalAccessException, InvocationTargetException {
// 获取Field的数组,私有字段也能获取
Field[] fields = object.getClass().getDeclaredFields();
for (Field field : fields) {
String fieldName = field.getName();
Object obj = parameter.get(fieldName);
Object value = obj;
Class> cls = field.getType();
if (obj != null) {
// 类型转换
if (obj instanceof String) {
String string = (String) obj;
if (!StringUtil.isEmpty(string)) {
string = ConfigUtil.filter(string);
}
if (isPrimitiveType(cls)) {
value = convert(cls, string);
}
} else if (obj instanceof BeanConfig) {
value = createBean((BeanConfig) obj);
} else if (obj instanceof BeanConfig[]) {
List list = new ArrayList();
for (BeanConfig beanconfig : (BeanConfig[]) obj) {
list.add(createBean(beanconfig));
}
value = list.toArray();
}
// 赋值
field.setAccessible(true);
field.set(object, value);
}
}
}
由内省改成了反射,不再需要getDescriptors(Class> clazz)方法,代码更简洁更合理。唯一的不足是反射没有内省的缓存,对性能稍有影响,但分表函数类的加载和初始化只在MyCat启动时执行一次,所以这点损失可以忽略不计。