当前位置 博文首页 > FMC_WBL的博客:Java核心技术之什么是反射

    FMC_WBL的博客:Java核心技术之什么是反射

    作者:[db:作者] 时间:2021-08-28 18:49

    什么是反射

    官方的解释 https://docs.oracle.com/javase/8/docs/api/
    ? ??Package java.lang.reflect Description Provides classes and interfaces for obtaining reflective information about classes and objects. Reflection allows programmatic access to information about the fields, methods and constructors of loaded classes, and the use of reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions.
    ? ? AccessibleObject allows suppression of access checks if the necessary ReflectPermission is available.
    ? ? Array provides static methods to dynamically create and access arrays.
    ? ? Classes in this package, along with java.lang.Class accommodate applications such as debuggers,interpreters, object inspectors, class browsers, and services such as Object Serialization and JavaBeans that need access to either the public members of a target object (based on its runtime class) or the members declared by a given class.

    这段话的大概意思就是:软件包java.lang.reflect提供用于获取有关类和对象的反射信息的类和接口。?通过反射,可以在安全性限制内以编程方式访问有关已加载类的字段,方法和构造函数的信息,并可以使用反射的字段,方法和构造函数对其基础副本进行操作。
    如果必要的ReflectPermission可用,AccessibleObject允许禁止访问检查。
    数组提供静态方法来动态创建和访问数组
    此包中的类以及java.lang.Class需要访问目标对象的公共成员(基于其对象)的应用程序,例如调试器,解释器,对象检查器,类浏览器以及服务,例如对象序列化和JavaBeans。 运行时类)或给定类声明的成员。

    通过官方给出的定义,我们可以看到他的介绍,那么我们思考既然已经有new来创建对象了,反射的意义是什么呢?对什么反射?

    在Java中创建对象,获取属性,调用对象的方法通常都是通过 Object o = new Object(), 然后通过 o.getXXX(), o.setXXX() o.doSomething()来实现。这个可以满足大多数的业务场景需求,那么在下面的几个场景下是否也同样满足呢?
    一、公司要你实现一个底层组件,此组件要根据上层的业务能在运行时动态的实例化对象,从而实现一些功能,如何实现?
    二、在这个组件中,要动态的获取它的属性,并更新属性的值和状态又该如何实现?
    三、同样在这个组件中,某个业务要根据不同业务参数动态的调用实例化对象的不同方法,又该如何实现?

    要解决以上的问题,普通的对象实例化方式是无法实现的,于是JVM提供了一种动态实例化对象,并且可以获取到实例对象的属性,方法,并对其进行一些操作。

    通过以上示例的对比,可以发现反射的方式创建实例的方式比普通方式更加灵活,也就是扩展性好。

    从 JVM 层面理解反射原理

    JVM 通过 Class 的类名找到 Class 的字节码文件,然后再通过 ClassLoader 的类加载机制在堆内存中分配对象(暂不考虑对象在栈,TLAB 上分配的情况) 。

    反射的例子

        @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);
            }
            // NOTE: the following code may not be strictly correct under
            // the current Java memory model.
            // Constructor lookup
            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);
                    // Disable accessibility checks on the constructor
                    // since we have to do the security check here anyway
                    // (the stack depth is wrong for the Constructor's
                    // security check to work)
                    // 安全校验第一次
                    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;
            // Security check (same as in java.lang.reflect.Constructor)
            // 安全校验第二次,这个 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;
            }
        }

    反射的优缺点

    优点:
    1、增加程序的灵活性,避免将固有逻辑写死
    2、代码简洁,可读性强,可提高代码的复用率
    缺点:
    1、相比较于直接调用,在访问量较大的情况下,反射会导致系统性能明显下降
    2、打破了类的封装性,存在一定的安全隐患
    3、反射性能慢。原因:同构 (1)反射调用会进行一系列的安全性校验(2)反射需要调用一系列的 native 方法来实现(3)寻找 Class 字节码的过程,比如通过 ClassName 找到对应的字节码 Class,然后进行加载、解析,这也会比较慢,而 new 的方式则无需寻找,因为在连接的解析阶段已经将符号引用转为了直接引用(4)入参校验

    反射在类加载中的应用

    类加载过程

    类装载的三部曲,加载、连接、初始化(简要介绍,详情请看博主另一篇类加载机制的博文)
    加载
    加载.class文件,主要有以下几个途径:
    从本地的文件系统中加载
    通过网络下载
    从 zip.jar 包加载.class 文件
    从存储中间件中加载(数据库、缓存…)
    在 JVM 运行期间通过动态字节码重组的方式(ASM)

    连接
    连接就是将已经读入到内存的类的二进制数据合并到 JVM 的运行时环境中,连接过程主要有三个阶段
    类的验证,对类的文件进行检查(版本号、模数。。)以保证.class 符合 JVM 规范
    类的准备,分配常量池空间,解析常量池,静态变量的分配,但不会初始化,只会给默认值
    类的解析,解析父类,解析接口,解析 Filed,解析方法列表(包含栈、字节码表、异常表、局部变量表、运行指针),把类二进制的符号引用转变为直接引用

    初始化
    JVM 执行类的初始话语句,为类的静态变量赋值
    如果这个类还没有被加载和连接,那就先进行加载和连接
    如果这个类存在父类,并且父类还没有初始话,那就先初始化父类
    如果类中存在初始化语句(Static 块),依次执行初始化语句

    类完成初始化的时机

    1、创建类的实例 new xxxClass() || Class.newInstance() || constructor.newInstance()访问某种的某个静态变量,或者对静态变量进行赋值
    2、调用类的静态方法
    3、Class.forName("包类名"),因为 forName 里有一个 init 参数是 true
    4、完成子类的初始化,也会完成对本类的初始化(接口除外)
    5、该类是程序引导入口(main 入口 或者 test 入口)

    Class 对象的获取方式

    Class clazz = xxxClass.class;?? ? // 没有完成初始化过程
    Class class = instanceObj.getClass;? ? //?对象已生成,完成初始化过程
    Class clazz = xxxClassLoader.load("包类名");? ? // 未完成初始化过程
    Class clazz = Class.forName("包类名");? ? // 完成初始化过程

    为什么 JDBC 用 Class.forName 的方式加载驱动而不是 ClassLoader.loadClass()?
    因为 Driver 的注册是通过 static 的静态块注册,所以得通过 Class.forName 的方式完成类的初始化,才能完成驱动的注册

    反射在Spring设计中的应用

    在 spring 中的 IOC 实例创建?

        protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd,
                                                 @Nullable Object[] args) {
            // Make sure bean class is actually resolved at this point.
            Class<?> beanClass = resolveBeanClass(mbd, beanName);
            if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers())
                    && !mbd.isNonPublicAccessAllowed()) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "Bean class isn't public, and non-public access not allowed: " +
                                beanClass.getName());
            }
            Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
            if (instanceSupplier != null) {
                return obtainFromSupplier(instanceSupplier, beanName);
            }
            if (mbd.getFactoryMethodName() != null) {
                return instantiateUsingFactoryMethod(beanName, mbd, args);
            }
            // Shortcut when re-creating the same bean...
            boolean resolved = false;
            boolean autowireNecessary = false;
            if (args == null) {
                synchronized (mbd.constructorArgumentLock) {
                    if (mbd.resolvedConstructorOrFactoryMethod != null) {
                        resolved = true;
                        autowireNecessary = mbd.constructorArgumentsResolved;
                    }
                }
            }
            if (resolved) {
                if (autowireNecessary) {
                    return autowireConstructor(beanName, mbd, null, null);
                } else {
                    return instantiateBean(beanName, mbd);
                }
            }
            // Candidate constructors for autowiring?
            Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass,
                    beanName);
            if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
                    mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
                return autowireConstructor(beanName, mbd, ctors, args);
            }
            // Preferred constructors for default construction?
            ctors = mbd.getPreferredConstructors();
            if (ctors != null) {
                return autowireConstructor(beanName, mbd, ctors, null);
            }
            // No special handling: simply use no-arg constructor.
            // 实例化 Bean 的方法
            return instantiateBean(beanName, mbd);
        }
    
        /**
         * Instantiate the given bean using its default constructor.
         *
         * @param beanName the name of the bean
         * @param mbd      the bean definition for the bean
         * @return a BeanWrapper for the new instance
         */
        protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition
                mbd) {
            try {
                Object beanInstance;
                final BeanFactory parent = this;
                if (System.getSecurityManager() != null) {
                    beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) ()
                                    ->
                                    getInstantiationStrategy().instantiate(mbd, beanName, parent),
                            getAccessControlContext());
                } else {
                    beanInstance = getInstantiationStrategy().instantiate(mbd, beanName,
                            parent);
                }
                BeanWrapper bw = new BeanWrapperImpl(beanInstance);
                initBeanWrapper(bw);
                return bw;
            } catch (Throwable ex) {
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Instantiation of bean failed",
                        ex);
            }
        }
    
        public T newInstance(Object... initargs)
                throws InstantiationException, IllegalAccessException,
                IllegalArgumentException, InvocationTargetException {
            if (!override) {
                if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                    Class<?> caller = Reflection.getCallerClass();
                    checkAccess(caller, clazz, null, modifiers);
                }
            }
            if ((clazz.getModifiers() & Modifier.ENUM) != 0)
                throw new IllegalArgumentException("Cannot reflectively create enum objects");
            ConstructorAccessor ca = constructorAccessor; // read volatile
            if (ca == null) {
                ca = acquireConstructorAccessor();
            }
            @SuppressWarnings("unchecked")
            T inst = (T) ca.newInstance(initargs);
            return inst;
        }
    
        public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws
                BeanInstantiationException {
            Assert.notNull(ctor, "Constructor must not be null");
            try {
                ReflectionUtils.makeAccessible(ctor);
                return (KotlinDetector.isKotlinReflectPresent() &&
                        KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
                        KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
            } catch (InstantiationException ex) {
                throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
            } catch (IllegalAccessException ex) {
                throw new BeanInstantiationException(ctor, "Is the constructor accessible?",
                        ex);
            } catch (IllegalArgumentException ex) {
                throw new BeanInstantiationException(ctor, "Illegal arguments for constructor",
                        ex);
            } catch (InvocationTargetException ex) {
                throw new BeanInstantiationException(ctor, "Constructor threw exception",
                        ex.getTargetException());
            }
        }

    解析 Bean 的 class,spring 里使用的是通过 classLoader 的方式

        @Nullable
        private Class<?> doResolveBeanClass(RootBeanDefinition mbd, Class<?>... typesToMatch)
                throws ClassNotFoundException {
            ClassLoader beanClassLoader = getBeanClassLoader();
            ClassLoader dynamicLoader = beanClassLoader;
            boolean freshResolve = false;
            if (!ObjectUtils.isEmpty(typesToMatch)) {
                // When just doing type checks (i.e. not creating an actual instance yet),
                // use the specified temporary class loader (e.g. in a weaving scenario).
                ClassLoader tempClassLoader = getTempClassLoader();
                if (tempClassLoader != null) {
                    dynamicLoader = tempClassLoader;
                    freshResolve = true;
                    if (tempClassLoader instanceof DecoratingClassLoader) {
                        DecoratingClassLoader dcl = (DecoratingClassLoader) tempClassLoader;
                        for (Class<?> typeToMatch : typesToMatch) {
                            dcl.excludeClass(typeToMatch.getName());
                        }
                    }
                }
            }
            String className = mbd.getBeanClassName();
            if (className != null) {
                Object evaluated = evaluateBeanDefinitionString(className, mbd);
                if (!className.equals(evaluated)) {
                    // A dynamically resolved expression, supported as of 4.2...
                    if (evaluated instanceof Class) {
                        return (Class<?>) evaluated;
                    } else if (evaluated instanceof String) {
                        className = (String) evaluated;
                        freshResolve = true;
                    } else {
                        throw new IllegalStateException("Invalid class name expression result:
                                " + evaluated);
                    }
                }
                if (freshResolve) {
                    // When resolving against a temporary class loader, exit early in order
                    // to avoid storing the resolved Class in the bean definition.
                    if (dynamicLoader != null) {
                        try {
                            return dynamicLoader.loadClass(className);
                        } catch (ClassNotFoundException ex) {
                            if (logger.isTraceEnabled()) {
                                logger.trace("Could not load class [" + className + "] from " +
                                        dynamicLoader + ": " + ex);
                            }
                        }
                    }
                    return ClassUtils.forName(className, dynamicLoader);
                }
            }
            // Resolve regularly, caching the result in the BeanDefinition...
            return mbd.resolveBeanClass(beanClassLoader);
        }

    设置实例属性

        protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper
                bw) {
            if (bw == null) {
                if (mbd.hasPropertyValues()) {
                    throw new BeanCreationException(
                            mbd.getResourceDescription(), beanName, "Cannot apply property values to
                    null instance ");
                } else {
                    // Skip property population phase for null instance.
                    return;
                }
            }
            // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
            // state of the bean before properties are set. This can be used, for example,
            // to support styles of field injection.
            boolean continueWithPropertyPopulation = true;
            if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
                for (BeanPostProcessor bp : getBeanPostProcessors()) {
                    if (bp instanceof InstantiationAwareBeanPostProcessor) {
                        InstantiationAwareBeanPostProcessor ibp =
                                (InstantiationAwareBeanPostProcessor) bp;
                        if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(),
                                beanName)) {
                            continueWithPropertyPopulation = false;
                            break;
                        }
                    }
                }
            }
            if (!continueWithPropertyPopulation) {
                return;
            }
            PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
            if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode()
                    == AUTOWIRE_BY_TYPE) {
                MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
                // Add property values based on autowire by name if applicable.
                if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) {
                    // 根据名称注入
                    autowireByName(beanName, mbd, bw, newPvs);
                }
                // Add property values based on autowire by type if applicable.
                if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
                    // 根据类型注入
                    autowireByType(beanName, mbd, bw, newPvs);
                }
                pvs = newPvs;
            }
            boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
            boolean needsDepCheck = (mbd.getDependencyCheck() !=
                    AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
            PropertyDescriptor[] filteredPds = null;
            if (hasInstAwareBpps) {
                if (pvs == null) {
                    pvs = mbd.getPropertyValues();
                }
                for (BeanPostProcessor bp : getBeanPostProcessors()) {
                    if (bp instanceof InstantiationAwareBeanPostProcessor) {
                        InstantiationAwareBeanPostProcessor ibp =
                                (InstantiationAwareBeanPostProcessor) bp;
                        // 通过@Resource @Autowre 的 BeanPostProcessor
                        PropertyValues pvsToUse = ibp.postProcessProperties(pvs,
                                bw.getWrappedInstance(), beanName);
                        if (pvsToUse == null) {
                            if (filteredPds == null) {
                                filteredPds = filterPropertyDescriptorsForDependencyCheck(bw,
                                        mbd.allowCaching);
                            }
                            pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds,
                                    bw.getWrappedInstance(), beanName);
                            if (pvsToUse == null) {
                                return;
                            }
                        }
                        pvs = pvsToUse;
                    }
                }
            }
            if (needsDepCheck) {
                if (filteredPds == null) {
                    filteredPds = filterPropertyDescriptorsForDependencyCheck(bw,
                            mbd.allowCaching);
                }
                checkDependencies(beanName, mbd, filteredPds, pvs);
            }
            if (pvs != null) {
                applyPropertyValues(beanName, mbd, bw, pvs);
            }
        }
    
        /**
         * Either this or {@link #getResourceToInject} needs to be overridden.
         */
        // 通过反射注入属性
        protected void inject(Object target, @Nullable String requestingBeanName, @Nullable
                PropertyValues pvs)
                throws Throwable {
            if (this.isField) {
                Field field = (Field) this.member;
                ReflectionUtils.makeAccessible(field);
                field.set(target, getResourceToInject(target, requestingBeanName));
            } else {
                if (checkPropertySkipping(pvs)) {
                    return;
                }
                try {
                    Method method = (Method) this.member;
                    ReflectionUtils.makeAccessible(method);
                    method.invoke(target, getResourceToInject(target, requestingBeanName));
                } catch (InvocationTargetException ex) {
                    throw ex.getTargetException();
                }
            }
        }

    反射Spring场景总结

    动态创建对象实例
    动态操作(设置)实例对象的方法、属性
    Spring IOC
    Spring DI
    Testing Framework

    那么什么是反射呢,怎么回答呢?

    官网回答 + 反射的属性(优缺点、性能分析) + JVM中反射的应用 + Spring中反射的应用

    cs