了解反射
4种获取Class对象的方法
//4种获取Class对象的方法,clazz1==clazz2==clazz3==clazz4
Boy boy = new Boy();
Class clazz1 = boy.getClass();
Class clazz2 = Boy.class;
Class clazz3 = ReflectTest.class.getClassLoader().loadClass("com.sky.reflectdetail.Boy");
Class clazz4 = Class.forName("com.sky.reflectdetail.Boy");
Class对象的方法
//获取修饰符 , 特征符
int modifier = clazz.getModifiers();
//获取包对象
Package personPackage = clazz.getPackage();
//类名
String fullClassName = clazz.getName();
String simpleClassname = clazz.getSimpleName();
//获取类加载器
ClassLoader classLoader = clazz.getClassLoader();
//获取当前类实现的接口列表
Class[] cs = clazz.getInterfaces();
//父类
Class fc = clazz.getSuperclass();
//获取所有公有的属性,包含继承
Field[] fields = clazz.getFields();
//获取指定名字的属性对象
Field nameField = clazz.getField("name");
Field heightField = clazz.getField("height");
//操作属性
nameField.set(boy, "小皮皮");
heightField.set(boy, 180);
//类中属性获取及操作 (本类中定义,不管什么修饰都能获取)
Field[] declaredFields = clazz.getDeclaredFields();
//获取指定的属性..
Field likeDescField = clazz.getDeclaredField("likeDesc");
int modifersFiled = likeDescField.getModifiers();
//likeDesc 设置属性强制访问(强吻)
likeDescField.setAccessible(true);
likeDescField.set(boy, "Beautiful girls");
//获取所有公有的方法,包含继承
Method[] methods = clazz.getMethods();
//获取类中定义的指定名称的公有方法
Method talkMethod = clazz.getMethod("talk", String.class);
Method playBallMethod = clazz.getMethod("playBall");
//Method对象方法详解
int modifers = playBallMethod.getModifiers(); //获取方法定义的修饰
Class returnClazz = talkMethod.getReturnType(); //方法的返回类型
Class[] parameterClazzs = talkMethod.getParameterTypes(); //方法的参数列表
Class[] exceptionClazzs = talkMethod.getExceptionTypes(); //方法的抛出的异常列表
//方法调用
talkMethod.invoke(boy, "I LOVE Qiuping");
//静态方法调用
playBallMethod.invoke(null);
//获取本类中定义的方式(不管修饰符统统获取)
Method[] declaredMethods = clazz.getDeclaredMethods();
//获取指定名称的本类定义的方法
Method likeGirlsMethod = clazz.getDeclaredMethod("likeGirls");
//pickUpGirlsMethod 私有方法 强吻
likeGirlsMethod.setAccessible(true);
likeGirlsMethod.invoke(boy);
//获取类中定义的所有的构造方法(包含公有,私有)
Constructor[] constructors = clazz.getDeclaredConstructors();
//获取无参构造函数
Constructor constructorWithOutParam = clazz.getDeclaredConstructor();
//constructor操作
//获取构造函数的修饰符
int modifiers = constructorWithOutParam.getModifiers();
//获取参数列表类型
Class[] paramClazzs = constructorWithOutParam.getParameterTypes();
//获取异常列表类型
Class[] exceptionClazzs = constructorWithOutParam.getExceptionTypes();
//构造器调用
Boy boyReflected = (Boy) constructorWithOutParam.newInstance();
//带参私有构造使用案例
Constructor constructorWithParamAndPrivate = clazz.getDeclaredConstructor(String.class, String.class);
//强吻
constructorWithParamAndPrivate.setAccessible(true);
boyReflected = (Boy) constructorWithParamAndPrivate.newInstance("亭亭玉立", "小铁头");
加载顺序
Class.forName("..") 只会加载出Class字节码对象,不会创建实例对象,只会初始化static的属性和块
手写一个简易的加密类加载器
public class ClassLoaderTest {
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
ClassLoaderFrame frame = new ClassLoaderFrame();
frame.runClass("F:\\test\\ClassLoaderMain", "1");
});
}
}
/**
* This frame contains two text fields for the name of the class to load and the decryption
* key.
*/
class ClassLoaderFrame {
public ClassLoaderFrame() {
}
/**
* Runs the main method of a given class.
*
* @param name the class name
* @param key the decryption key for the class files
*/
public void runClass(String name, String key) {
try {
name = name + ".class";
CryptoClassLoader loader = new CryptoClassLoader(Integer.parseInt(key));
Class<?> c = loader.loadClass(name);
//执行静态方法
Method m = c.getMethod("main", String[].class);
m.invoke(null, (Object) new String[]{});
//执行非静态方法
Object obj = c.newInstance();
m = c.getDeclaredMethod("hello", String.class);
m.invoke(obj, "sky");
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* This class loader loads encrypted class files.
*/
class CryptoClassLoader extends ClassLoader {
private int key;
/**
* Constructs a crypto class loader.
*
* @param k the decryption key
*/
public CryptoClassLoader(int k) {
key = k;
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] classBytes = loadClassBytes(name);
Class<?> cl = defineClass("com.sky.test.ClassLoaderMain", classBytes, 0, classBytes.length);
if (cl == null) throw new ClassNotFoundException(name);
return cl;
} catch (IOException e) {
throw new ClassNotFoundException(name);
}
}
/**
* Loads and decrypt the class file bytes.
*
* @param name the class name
* @return an array with the class file bytes
*/
private byte[] loadClassBytes(String name) throws IOException {
return Files.readAllBytes(Paths.get(name));
}
}
什么是反射
JVM 通过 Class 的类名找到 Class 的字节码文件,然后再通过
ClassLoader
的类加载机制在堆内存 中分配对象(暂不考虑对象在栈,TLAB 上分配的情况) 。
反射的优缺点
- 优点
- 增加程序的灵活性,避免将固有逻辑写死 代码简洁,可读性强,可提高代码的复用率
- 缺点
- 相比较于直接调用,在访问量较大的情况下,反射会导致系统性能明显下降
- 打破了类的封装性,存在一定的安全隐患
反射机制性能问题分析
- 反射调用会进行一系列的安全性校验
- 反射需要调用一系列的native方法来实现
- 寻找Class字节码的过程,比如通过ClassName找到对应的字 节码Class,然后进行加载、解析,也会比较慢,而new的方式则无需寻找,因为在Linking的解析阶段已经将符号引用 转为了直接引用
- 入参校验
@sun.reflect.CallerSensitive
public static native java.lang.Class<?> getCallerClass();
@CallerSensitive
public static Class<?> forName(String className)
throws ClassNotFoundException {
//调用 native 方法获取调用者的 Class
Class<?> caller = Reflection.getCallerClass();
//调用 native 方法根据 className 查找字节码对象,并进行加载、解析
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
/**
* Called after security check for system loader access checks have been made.
*/
private static native Class<?> forName0(String name, boolean initialize,
ClassLoader loader,
Class<?> caller)
throws ClassNotFoundException;
@CallerSensitive
public T newInstance()
throws InstantiationException, IllegalAccessException {
//通过反射进行实例化的时候,如果有配置 SecurityManager 则要先进行安全校验,普通方式实例化则不需要
if (System.getSecurityManager() != null) {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);
}
if (cachedConstructor == null) {
if (this == Class.class) {
throw new IllegalAccessException(
"Can not call newInstance() on the Class for java.lang.Class"
);
}
try {
Class<?>[] empty = {};
final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
//安全校验第一次
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
c.setAccessible(true);
return null;
}
});
//将构造器进行缓存,提高下次通过反射创建对象时的性能
cachedConstructor = c;
} catch (NoSuchMethodException e) {
throw (InstantiationException)
new InstantiationException(getName()).initCause(e);
}
}
Constructor<T> tmpConstructor = cachedConstructor;
//安全校验第二次,这个 modifier 在 JDK 里是以一个 int 值来表示,
int modifiers = tmpConstructor.getModifiers();
if (!Reflection.quickCheckMemberAccess(this, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
if (newInstanceCallerCache != caller) {
Reflection.ensureMemberAccess(caller, this, null, modifiers);
newInstanceCallerCache = caller;
}
}
// Run constructor
try {
//通过构造器来实例化对象
return tmpConstructor.newInstance((Object[]) null);
} catch (InvocationTargetException e) {
Unsafe.getUnsafe().throwException(e.getTargetException());
// Not reached
return null;
}
}
反射在SpringIOC和DI中的应用
IOC : (Inversion of Control)
DI : (Dependency Injection)
Spring的应用
#A.java
public class A {
public A() {
System.out.println("A whitout parameters Constructor invoke!");
}
public static B createBObj() { //factory-method
System.out.println("A static function createBObj() invoke!");
return new B();
}
public C createCObj() { //instance-method
System.out.println("a's createCObj() invoke!");
return new C();
}
}
# spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 创建方式1:空参构造创建 -->
<bean id="a" class="com.nx.qiuping.vip.iocdi.A"/>
<!--
创建方式2:静态工厂创建
调用A的createBObj方法来创建名为b的对象放入容器
-->
<bean id="b" class="com.nx.qiuping.vip.iocdi.A" factory-method="createBObj"/>
<!--
创建方式3:实例工厂创建
调用实例a的createCObj方法来创建名为c的对象放入容器
-->
<bean id="c" factory-bean="a" factory-method="createCObj"/>
<!--DI 方式1 通过有参构造器的方式 -->
<bean id="d" class="com.nx.qiuping.vip.iocdi.D">
<constructor-arg ref="a"/>
<constructor-arg ref="b"/>
</bean>
<!--DI 方式2 通过setter函数的方式 -->
<bean id="e" class="com.nx.qiuping.vip.iocdi.E">
<property name="a" ref="a"/>
<property name="b" ref="b"/>
</bean>
</beans>
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
//IOC 测试
A a = context.getBean("a", A.class);
B b = context.getBean("b", B.class);
C c = context.getBean("c", C.class);
//DI 测试
D d = context.getBean("d", D.class);
E e = context.getBean("e", E.class);
}
手写简易SpringIOC和DI
# BeanConfig.java 配置类
public class BeanConfig { //spring-ioc.xml spring-di.xml
private String id;
private String clazz;
private String factoryMethod;
private String factoryBean;
private HashMap<String,List<String>> dependencyHashMap = new HashMap<>();
//..get set...
}
# IOCContainer 一个hash表容器
public class IOCContainer {
private static HashMap<String,Object> IoContainer = new HashMap<>();
public static void putBean(String id, Object object) {
IoContainer.put(id, object);
}
public static Object getBean(String id) {
return IoContainer.get(id);
}
public static <T> T getBean(String id, Class<T> clazz) {
return (T) IoContainer.get(id);
}
}
# BootStrap.java
public class BootStrap {
public static void main(String[] args) throws Exception {
// 模拟解析xml拿到 beanConfigs
List<BeanConfig> beanConfigs = parseXmltoBeanConfig();
//循环
for (BeanConfig tmpconfig : beanConfigs) {
Object ans = null;
if (null != tmpconfig.getClazz()) {
//拿到clazz
Class clazz = Class.forName(tmpconfig.getClazz());
if (null != tmpconfig.getFactoryMethod()) {
//【静态工厂创建】
//基于Class对象获取method对象
Method method = clazz.getDeclaredMethod(tmpconfig.getFactoryMethod());
ans = method.invoke(null);
} else {
//【空参构造创建】
ans = clazz.newInstance();
}
} else if (null != tmpconfig.getFactoryBean()) {
//【实例工厂创建】
//从容器中拿到实体bean
Object obj = IOCContainer.getBean(tmpconfig.getFactoryBean());
Method method = obj.getClass().getDeclaredMethod(tmpconfig.getFactoryMethod());
ans = method.invoke(obj);
} else {
System.out.println("Configuration is required!");
}
//-----DI-----
// 解析依赖注入的关系 DependencyHashMap
// 反射调用 Object Constructor, setter 方法设置对象之间依赖关系
if (tmpconfig.getDependencyHashMap() != null && tmpconfig.getClazz() != null) {
Class clazz = Class.forName(tmpconfig.getClazz());
HashMap<String, List<String>> dependencyMap = tmpconfig.getDependencyHashMap();
if (dependencyMap.containsKey("constructor-arg")) {
//【DI 通过有参构造器的方式】
List<String> list = dependencyMap.get("constructor-arg");
Class[] clazzList = new Class[list.size()];
List<Object> diObjs = new ArrayList<>();
for (int i = 0;i<list.size();i++) {
clazzList[i] = IOCContainer.getBean(list.get(i)).getClass();
diObjs.add(IOCContainer.getBean(list.get(i)));
}
Constructor constructor = clazz.getDeclaredConstructor(clazzList);
ans = constructor.newInstance(diObjs.toArray());
} else if (dependencyMap.containsKey("property")) {
//【DI 通过setter函数的方式】
List<String> list = dependencyMap.get("property");
for (String diBean : list) {
String methodName = "set" + (char) (diBean.charAt(0) - 32) + diBean.substring(1);
Method method = clazz.getMethod(methodName, IOCContainer.getBean(diBean).getClass());
method.invoke(ans, IOCContainer.getBean(diBean));
}
}
}
IOCContainer.putBean(tmpconfig.getId(), ans);
}
//IOC 测试
A a = IOCContainer.getBean("a", A.class);
B b = IOCContainer.getBean("b", B.class);
C c = IOCContainer.getBean("c", C.class);
D d = IOCContainer.getBean("d", D.class);
E e = IOCContainer.getBean("e", E.class);
}
/**
* 模拟一个解析XML过程
*
* @return
*/
private static List<BeanConfig> parseXmltoBeanConfig() {
//TODO
List<BeanConfig> beanConfigs = new ArrayList<BeanConfig>();
BeanConfig beanConfig1 = new BeanConfig();
beanConfig1.setClazz("com.nx.qiuping.vip.iocdi.A");
beanConfig1.setId("a");
beanConfigs.add(beanConfig1);
BeanConfig beanConfig2 = new BeanConfig();
beanConfig2.setClazz("com.nx.qiuping.vip.iocdi.A");
beanConfig2.setId("b");
beanConfig2.setFactoryMethod("createBObj");
beanConfigs.add(beanConfig2);
BeanConfig beanConfig3 = new BeanConfig();
beanConfig3.setId("c");
beanConfig3.setFactoryBean("a");
beanConfig3.setFactoryMethod("createCObj");
beanConfigs.add(beanConfig3);
BeanConfig beanConfig4 = new BeanConfig();
beanConfig4.setId("d");
beanConfig4.setClazz("com.nx.qiuping.vip.iocdi.D");
HashMap<String, List<String>> map = new HashMap<>();
List<String> list = Arrays.asList("a", "b");
map.put("constructor-arg", list);
beanConfig4.setDependencyHashMap(map);
beanConfigs.add(beanConfig4);
BeanConfig beanConfig5 = new BeanConfig();
beanConfig5.setId("e");
beanConfig5.setClazz("com.nx.qiuping.vip.iocdi.E");
map = new HashMap<>();
list = Arrays.asList("a", "b");
map.put("property", list);
beanConfig5.setDependencyHashMap(map);
beanConfigs.add(beanConfig5);
return beanConfigs;
}
}
@Test注解
public static void main(String[] args) {
int passed = 0, failed = 0;
try {
// hard code to get TestCase Classes
//TODO scan package to get all of TestCase Classes
Method[] methods = Class.forName("com.sky.test.OrderTestCase").getMethods();
for (Method m : methods) {
if (m.isAnnotationPresent(Test.class)) {
try {
m.invoke(null);
passed++;
} catch (Throwable ex) {
System.out.printf("Test %s failed: %s %n", m, ex.getCause());
failed++;
}
}
}
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
System.out.printf("Passed: %d, Failed %d%n", passed, failed);
}
Comments | 0 条评论