由于最近在看spring源码,从Ioc入手,其实现用到了java的反射,所以打算仔细学习一下。
什么是java反射?
动态获取运行时的类信息。什么意思呢?对于任意一个类,能获得它的相关属性(包括字段,注解)和方法;对于任意一个对象,能调用它的属性和方法;
一般来说,我们开发时,比如实例化一个对象
Car car = new Car();
或者设置一个属性
car.setBrand("XXX");
调用一个方法
car.introduce();
然后代码编译,运行...
但是反射提供了另外一种方式,让我们可以在运行时获取car的属性和方法,并调用,设置相关方法和属性
第一个例子
有一个Car类
public class Car {
private Video video;
/**
* 车牌
*/
private String brand;
/**
* 颜色
*/
private Integer color;
/**
* 最大速度
*/
private int maxSpeed;
/**
* 无参方法
*/
public void introduce() {
System.out.println("brand:" + brand + ";color:" + color + ";maxSpeed:" + maxSpeed);
}
public void introduceWithParameters(String brand, int maxSpeed) {
System.out.println("brand:" + brand + ";maxSpeed:" + maxSpeed);
}
// 省略get,set方法
}
@Test
public void demoTest() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 获取Class对象
Class clazz = Class.forName("com.example.learnjdk.reflation.Car");
// 获取类的默认构造器对象并通过它实例化Car
Constructor<Car> constructor = clazz.getConstructor(null);
Car car = constructor.newInstance();
// 通过反射设置属性
Method setBrand = clazz.getDeclaredMethod("setBrand", String.class);
setBrand.invoke(car, "大众");
// 通过反射调用方法
Method introduce = clazz.getMethod("introduce", null);
introduce.invoke(car, null);
System.out.println(car);
}
Class对象
Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象。
Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。
获取Class对象的方式
- 类字面值来获取指定类型(或 void)的 Class 对象
System.out.println("类字面值方式:" + Car.class.getName());
- 对象方式
-
Class.forName(String className)System.out.println("对象方式:" + car.getClass().getName());
-
ClassLoader加载Class clazz = Class.forName("com.example.learnjdk.reflation.Car");
ClassLoader loader = Thread.currentThread().getContextClassLoader(); Class clazz = loader.loadClass("com.example.learnjdk.reflation.Car");
Class是反射的基础类,还有几个比较重要的反射类
- Constructor:构造函数反射类
- Method:类方法的反射类
- Field:类的成员变量反射类
Constructor
可以通过这个反射类,实例化对象。newInstance是主要方法
T newInstance(Object... initargs)
看下面的例子
// 通过构造函数,实例化对象
//获取所有构造函数
Constructor[] constructors = clazz.getConstructors();
for (int i = 0; i < constructors.length; i++) {
System.out.println(constructors[i]);
}
// 根据指定类型获取无参构造函数
Constructor<Car> defaultConstructor = clazz.getConstructor(null);
System.out.println("默认构造函数:" + defaultConstructor);
Car defaultCar = defaultConstructor.newInstance(null);
// 相当于new Car()
System.out.println("默认构造函数创建的对象:" + defaultCar);
// 根据指定类型获取有参构造函数
Constructor<Car> constructorHasParameters = clazz.getConstructor(String.class, Integer.class, int.class);
System.out.println("有参构造:" + constructorHasParameters);
Car carHasParameters = constructorHasParameters.newInstance("宝马", 2, 5000);
// 相当于new Car("宝马", 2, 5000)
System.out.println("有参构造的函数:" + carHasParameters);
Method
提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)
Method[] getDeclaredMethods()
这个方法获取所有返回 Method
对象的一个数组,这些对象反映此 Class
对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法
// 获取反射的所有的方法,并调用
Method[] methods = clazz.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
System.out.println("反射获取的方法:" + methods[i]);
// 以 String 形式返回此 Method 对象表示的方法名称。
System.out.println("方法名称:" + methods[i].getName());
Class[] parameterTypes = methods[i].getParameterTypes();
// 按照声明顺序返回 Class 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型。
// 如果底层方法不带参数,则返回长度为 0 的数组。
for (int j = 0; j < parameterTypes.length; j++) {
System.out.println("方法参数类型:" + parameterTypes[j]);
}
// 返回一个 Class 对象,该对象描述了此 Method 对象所表示的方法的正式返回类型。
Class<?> returnType = methods[i].getReturnType();
System.out.println("方法返回类型:" + returnType);
}
调用一下对应的方法,
public Object invoke(Object obj, Object... args)
// 获取类的默认构造器对象并通过它实例化Car
Constructor<Car> constructor = clazz.getConstructor(null);
Car car = constructor.newInstance();
// 通过反射设置属性
Method setBrand = clazz.getDeclaredMethod("setBrand", String.class);
setBrand.invoke(car, "大众");
// 通过反射调用方法
Method introduce = clazz.getMethod("introduce", null);
introduce.invoke(car, null);
System.out.println(car);
这里要注意一点,对私有方法的访问,在Car类里面再加一个私有方法
private void innerMethod() {
System.out.println("This is a private method");
}
// 私有方法
Method innerMethod = clazz.getDeclaredMethod("innerMethod", null);
// innerMethod.setAccessible(true);
innerMethod.invoke(car, null);
调用试试,会抛出异常,这时需要设置innerMethod.setAccessible(true);才可以访问,下面的Field也是类似
Field
Field
提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。
Field类最主要的方法是set(Object obj, Object value)
public void set(Object obj, Object value)
// 设置属性值brand
Field brand = clazz.getDeclaredField("brand");
// 设置访问private
brand.setAccessible(true);
brand.set(car, "奔驰");
// 设置maxSpeed
Field maxSpeed = clazz.getDeclaredField("maxSpeed");
maxSpeed.setAccessible(true);
maxSpeed.set(car, 500);
// 对于基本类型,有对应的setType方法;比如int,setInt;boolean,setBoolean
maxSpeed.setInt(car, 600);
System.out.println(car);
输出
Java 语言访问控制检查
在上面例子中,是不能直接访问私有方法,属性的,必须要设置setAccessible(true).
那具体是什么意思呢?
将对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。
并且,提供了相应的方法,返回对象的修饰符,来确定到底是private,protect,public...
public int getModifiers()
以整数形式返回由此 Field
对象表示的字段的 Java 语言修饰符。应该使用 Modifier
类对这些修饰符进行解码。
// 获取语言修饰符
int modifier = brand.getModifiers();
System.out.println(brand.getName() + "的修饰符是" + modifier);
// 获取方法修饰符
int methodModifier = setBrand.getModifiers();
System.out.println(setBrand.getName() + "的修饰符是" + methodModifier);
根据对照可以发现是private属性,public方法
最后提供一个反射的方法类,有需要的朋友可以看看
package com.example.learnjdk.reflation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.*;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 反射工具类.
* 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.
*/
@Slf4j
public class Reflections {
/**
* set方法前缀
*/
private static final String SETTER_PREFIX = "set";
/**
* get方法前缀
*/
private static final String GETTER_PREFIX = "get";
/**
* 内部类路径
*/
private static final String CGLIB_CLASS_SEPARATOR = "$$";
/**
* 字段属性分隔符
*/
private static final String PROPERTY_SAPERATOR = ".";
/**
* 默认参数
*/
private static final Class<?>[] PARAMETER_TYPES = new Class[]{};
/**
* 默认参数
*/
private static final Object[] PARAMETER_ARGS = new Object[]{};
private static final Pattern LIST_PARAM_PAT = Pattern.compile("\\[[0-9]+\\]");
/**
* 调用Getter方法.
* 支持多级,如:对象名.对象名.方法
* 支持list元素,如: addressInfo.selfAddress[1].name
*/
public static Object invokeGetter(Object obj, String propertyName) throws InvocationTargetException, IllegalAccessException {
if (null == obj || StringUtils.isBlank(propertyName)) {
return null;
}
Object object = obj;
// 多级属性
for (String name : StringUtils.split(propertyName, PROPERTY_SAPERATOR)) {
// 兼容List格式:name[1]
Matcher matcher = LIST_PARAM_PAT.matcher(name);
if (matcher.find()) {
name = StringUtils.replace(name, matcher.group(), "");
}
object = invokeMethod(object, GETTER_PREFIX + StringUtils.capitalize(name), PARAMETER_TYPES, PARAMETER_ARGS);
// 如果上级对象为空则返回null
if (null == object) {
return null;
}
if (object instanceof List) {
String matchStr = matcher.group();
Integer ind = Integer.valueOf(matchStr.substring(1, matchStr.length() - 1));
List list = (List) object;
if (list.size() <= ind) {
return null;
}
object = list.get(ind);
}
}
return object;
}
/**
* 调用Setter方法, 仅匹配方法名。
* 支持多级,如:对象名.对象名.方法
*/
public static void invokeSetter(Object obj, String propertyName, Object value) throws InvocationTargetException, IllegalAccessException {
if (null == obj || StringUtils.isBlank(propertyName)) {
return;
}
Object object = obj;
String[] names = StringUtils.split(propertyName, PROPERTY_SAPERATOR);
for (int i = 0; i < names.length; i++) {
// 获取上级对象
if (i < names.length - 1) {
object = invokeMethod(object, GETTER_PREFIX + StringUtils.capitalize(names[i]), PARAMETER_TYPES, PARAMETER_ARGS);
// 如果上级对象不存在则不设置
if (null == object) {
return;
}
continue;
}
invokeMethodByName(object, SETTER_PREFIX + StringUtils.capitalize(names[i]), new Object[]{value});
}
}
/**
* 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
*/
public static Object getFieldValue(final Object obj, final String fieldName) throws IllegalAccessException {
// 参数为空
if (null == obj || StringUtils.isBlank(fieldName)) {
return null;
}
Field field = getAccessibleField(obj, fieldName);
// 字段为空
if (field == null) {
return null;
}
try {
return field.get(obj);
} catch (IllegalAccessException e) {
throw e;
}
}
/**
* 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
*/
public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws IllegalAccessException {
if (null == obj || StringUtils.isBlank(fieldName) || null == value) {
return;
}
Field field = getAccessibleField(obj, fieldName);
if (field == null) {
return;
}
try {
field.set(obj, value);
} catch (IllegalAccessException e) {
throw e;
}
}
/**
* 直接调用对象方法, 无视private/protected修饰符.
* 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用.
* 同时匹配方法名+参数类型,
*/
public static Object invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes,
final Object[] args) throws InvocationTargetException, IllegalAccessException {
if (null == obj || StringUtils.isBlank(methodName)) {
return null;
}
Method method = getAccessibleMethod(obj, methodName, parameterTypes);
if (method == null) {
return null;
}
try {
return method.invoke(obj, args);
} catch (Exception e) {
throw e;
}
}
/**
* 直接调用对象方法, 无视private/protected修饰符,
* 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用.
* 只匹配函数名,如果有多个同名函数调用第一个。
*/
public static Object invokeMethodByName(final Object obj, final String methodName, final Object[] args) throws InvocationTargetException, IllegalAccessException {
if (null == obj || StringUtils.isBlank(methodName)) {
return null;
}
Method method = getAccessibleMethodByName(obj, methodName);
if (method == null) {
return null;
}
try {
return method.invoke(obj, args);
} catch (Exception e) {
throw e;
}
}
/**
* 根据调用静态方法
*/
public static Object invokeStaticMethodByName(final String classPath, final String methodName, final Object[] args) throws Exception {
if (StringUtils.isBlank(classPath) || StringUtils.isBlank(methodName)) {
return null;
}
Class<?> clzz = getClass(classPath);
if (null == clzz) {
return null;
}
try {
Method method = clzz.getMethod(methodName, String.class);
if (method == null) {
return null;
}
return method.invoke(null, args);
} catch (Exception e) {
throw e;
}
}
/**
* 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.
* <p>
* 如向上转型到Object仍无法找到, 返回null.
*/
public static Field getAccessibleField(final Object obj, final String fieldName) {
if (null == obj || StringUtils.isBlank(fieldName)) {
return null;
}
for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
try {
Field field = superClass.getDeclaredField(fieldName);
makeAccessible(field);
return field;
} catch (NoSuchFieldException e) {//NOSONAR
// Field不在当前类定义,继续向上转型
continue;// new add
}
}
return null;
}
/**
* 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
* 如向上转型到Object仍无法找到, 返回null.
* 匹配函数名+参数类型。
* <p>
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
*/
public static Method getAccessibleMethod(final Object obj, final String methodName,
final Class<?>... parameterTypes) {
if (null == obj || StringUtils.isBlank(methodName)) {
return null;
}
for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
try {
Method method = searchType.getDeclaredMethod(methodName, parameterTypes);
makeAccessible(method);
return method;
} catch (NoSuchMethodException e) {
// Method不在当前类定义,继续向上转型
continue;// new add
}
}
return null;
}
/**
* 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
* 如向上转型到Object仍无法找到, 返回null.
* 只匹配函数名。
* <p>
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
*/
public static Method getAccessibleMethodByName(final Object obj, final String methodName) {
if (null == obj || StringUtils.isBlank(methodName)) {
return null;
}
for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
Method[] methods = searchType.getDeclaredMethods();
for (Method method : methods) {
if (method.getName().equals(methodName)) {
makeAccessible(method);
return method;
}
}
}
return null;
}
/**
* 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
*/
public static void makeAccessible(Method method) {
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
&& !method.isAccessible()) {
method.setAccessible(true);
}
}
/**
* 通过发射查询对象的方法
*
* @param clazz 对象的class
* @param methodName 方法名称
* @param parameterTypes 方法参数
* @return 方法对象
*/
public static Method getMethod(final Class clazz, final String methodName,
final Class<?>... parameterTypes) {
try {
return clazz.getMethod(methodName, parameterTypes);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
/**
* 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
*/
public static void makeAccessible(Field field) {
if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier
.isFinal(field.getModifiers())) && !field.isAccessible()) {
field.setAccessible(true);
}
}
/**
* 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处
* 如无法找到, 返回Object.class.
* eg.
* public UserDao extends HibernateDao<User>
*
* @param clazz The class to introspect
* @return the first generic declaration, or Object.class if cannot be determined
*/
@SuppressWarnings("unchecked")
public static <T> Class<T> getClassGenricType(final Class clazz) {
return getClassGenricType(clazz, 0);
}
/**
* 通过反射, 获得Class定义中声明的父类的泛型参数的类型.
* 如无法找到, 返回Object.class.
* <p>
* 如public UserDao extends HibernateDao<User,Long>
*
* @param clazz clazz The class to introspect
* @param index the Index of the generic ddeclaration,start from 0.
* @return the index generic declaration, or Object.class if cannot be determined
*/
public static Class getClassGenricType(final Class clazz, final int index) {
Type genType = clazz.getGenericSuperclass();
if (!(genType instanceof ParameterizedType)) {
log.warn(clazz.getSimpleName() + "'s superclass not ParameterizedType");
return Object.class;
}
Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
if (index >= params.length || index < 0) {
log.warn("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
+ params.length);
return Object.class;
}
if (!(params[index] instanceof Class)) {
log.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
return Object.class;
}
return (Class) params[index];
}
public static Class<?> getUserClass(Object instance) {
if (null == instance) {
return null;
}
Class clazz = instance.getClass();
if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
Class<?> superClass = clazz.getSuperclass();
if (superClass != null && !Object.class.equals(superClass)) {
return superClass;
}
}
return clazz;
}
/**
* 获取Class对象
*/
public static Class<?> getClass(String calssPath) {
if (StringUtils.isBlank(calssPath)) {
return null;
}
try {
return Class.forName(calssPath);
} catch (ClassNotFoundException e) {
return null;
}
}
/**
* 创建对象
*/
public static Object newInstance(Constructor constructor, Object... initargs) {
if (null == constructor) {
return null;
}
try {
return constructor.newInstance(initargs);
} catch (Exception e) {
return null;
}
}
}
demo代码github:https://github.com/kobet/threa-learn