了解反射

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);
}

hhhhh