Java反射(扩展)(五)

简介: 前几篇文章讲述Java反射核心功能与用法,基本的常用方法都已经囊括里面了,本篇针是对前几篇文章进行补充。

前言

image (1).png
前几篇文章讲述Java反射核心功能与用法,基本的常用方法都已经囊括里面了,本篇针是对前几篇文章进行补充。

AccessibleObject

AccessibleObject类是前几篇文章所述的Field、Method、Constructor的基类。它提供了将反射的对象标记为在使用时取消默认Java语言访问控制检查的能力,它可以在在反射对象中设置accessible标志而获取特权,大部分时间我们称这种方式为“暴力反射”。

它们之间的关系如下:

image.png
对于公共成员、默认成员、受保护成员、私有成员,在使用Field、Method、Constructor对象来设置或获取字段、调用方法,又或者创建和初始化类的新实例的时候,会执行访问检查。但是AccessibleObject类在反射对象中设置accessible标志允许具有足够权限,以通常被禁止的方式操作对象。

/**
 * The AccessibleObject class is the base class for Field, Method and
 * Constructor objects.  It provides the ability to flag a reflected
 * object as suppressing default Java language access control checks
 * when it is used.  The access checks--for public, default (package)
 * access, protected, and private members--are performed when Fields,
 * Methods or Constructors are used to set or get fields, to invoke
 * methods, or to create and initialize new instances of classes,
 * respectively.
 *
 * <p>Setting the {@code accessible} flag in a reflected object
 * permits sophisticated applications with sufficient privilege, such
 * as Java Object Serialization or other persistence mechanisms, to
 * manipulate objects in a manner that would normally be prohibited.
 * <p>By default, a reflected object is <em>not</em> accessible.
*/
public class AccessibleObject implements AnnotatedElement {

    /**
     * 用于检查客户端是否具有足够的权限来阻止java语言访问控制检查
     * The Permission object that is used to check whether a client
     * has sufficient privilege to defeat Java language access
     * control checks.
    Permission对象,
     */
    static final private java.security.Permission ACCESS_PERMISSION = new ReflectPermission("suppressAccessChecks");

    /**
     * 使用单一安全检查来设置对象数组的可访问标志的一个方便的方法(为了效率)
     * Convenience method to set the {@code accessible} flag for an
     * array of objects with a single security check (for efficiency).
   */
    public static void setAccessible(AccessibleObject[] array, boolean flag) throws SecurityException {
        // //获取系统安全的接口
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
        for (int i = 0; i < array.length; i++) {
            setAccessible0(array[i], flag);
        }
    }

    /**
     设置可访问标志设置
    */
    public void setAccessible(boolean flag) throws SecurityException {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
        setAccessible0(this, flag);
    }

    /* 
     * Check that you aren't exposing java.lang.Class.<init> or sensitive
       fields in java.lang.Class. 
     */
    private static void setAccessible0(AccessibleObject obj, boolean flag) throws SecurityException {
        if (obj instanceof Constructor && flag == true) {
            Constructor<?> c = (Constructor<?>)obj;
            if (c.getDeclaringClass() == Class.class) {
                throw new SecurityException("Cannot make a java.lang.Class" +
                                            " constructor accessible");
            }
        }
        obj.override = flag;
    }

    /**
   * 获取accessible标志
     */
    public boolean isAccessible() {
        return override;
    }

    /**
     * 构造函数,仅JVM使用
     * Constructor: only used by the Java Virtual Machine.
     */
    protected AccessibleObject() {}

    // Indicates whether language-level access checks are overridden
    // by this object. Initializes to "false". This field is used by
    // Field, Method, and Constructor.
    // 通过这个对象,指示是否覆盖语言级别访问检查权限,初始化为false,该字段用于Field, Method和Constructor

    // NOTE: for security purposes, this field must not be visible
    // outside this package. 出于安全考虑,此字段不得显示出包外

    boolean override;

    // Reflection factory used by subclasses for creating field,
    // method, and constructor accessors. Note that this is called
    // very early in the bootstrapping process. 子类用于创建字段,方法和构造函数的反射工厂

    static final ReflectionFactory reflectionFactory = AccessController.doPrivileged(
            new sun.reflect.ReflectionFactory.GetReflectionFactoryAction()
    );

    // .....
}
方法如下:

image.png

其他的方法,本文不做展开,具体的请参阅文档,我们重点来看setAccessible方法。

setAccessible

image.png

  • 值为true时代表反射的对象在使用时应该取消Java语言的访问检查。
  • 值为false时代表反射的对象应该实施Java语言访问检查。

由于JDK的安全检查耗时较多,所以通过 setAccessible(true) 的方式关闭安全检查来提升反射速度。

Example

如何通过反射在访问类的private属性/方法/构造方法?
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    static class User {

        private String name;

        private Integer age;

        private void print(String msg) {
            System.out.println(msg);
            System.out.println("hi,我是:" + name + ",年龄:" + age);
        }
    }
// 访问私有的成员变量
public static void main(String[] args) throws Exception {
        User user = new User("Duansg", 18);

        // 访问私有的成员变量
        Field field = User.class.getDeclaredField("name");
        field.setAccessible(true);
        // Duansg
        System.out.println(field.get(user));
}
// 改变私有的成员变量值
public static void main(String[] args) throws Exception {
        User user = new User("Duansg", 18);
        // 访问私有的成员变量
        Field field = User.class.getDeclaredField("name");
        field.setAccessible(true);
        field.set(user, "马云");
        // 马云
        System.out.println(field.get(user));
}
// 访问private方法
public static void main(String[] args) throws Exception {
        User user = new User("Duansg", 18);
        Method method = User.class.getDeclaredMethod("print", String.class);
        method.setAccessible(true);
        method.invoke(user, "hi,我是晓断,今年22");
//        hi,我是晓断,今年22
//        hi,我是:Duansg,年龄:18
}

PropertyDescriptor

PropertyDescriptor类是一个属性的描述器,它表示JavaBean类通过存储器导出一个属性。详细的可了解Javad的内省机制,PropertyDescriptor主要方法:

image.png

从上图不难看出,它就是用于处理属性相关的信息,比如读写方法的设置和读取,获取属性的类型等等操作。

FeatureDescriptor的顶层类,它的子类有PropertyDescriptor、MethodDescriptor、BeanDescriptor等等。它们的关系如下:

image.png

package java.beans;

/**
* A PropertyDescriptor describes one property that a Java Bean
* exports via a pair of accessor methods.
*/
public class PropertyDescriptor extends FeatureDescriptor {

    private Reference<? extends Class<?>> propertyTypeRef;
    private final MethodRef readMethodRef = new MethodRef();
    private final MethodRef writeMethodRef = new MethodRef();
    private Reference<? extends Class<?>> propertyEditorClassRef;

    private boolean bound;
    private boolean constrained;

    // The base name of the method name which will be prefixed with the
    // read and write method. If name == "foo" then the baseName is "Foo"
    private String baseName;

    private String writeMethodName;
    private String readMethodName;
    // ......
}

Constructs

/**
     * Constructs a PropertyDescriptor for a property that follows
     * the standard Java convention by having getFoo and setFoo
     * accessor methods.  Thus if the argument name is "fred", it will
     * assume that the writer method is "setFred" and the reader method
     * is "getFred" (or "isFred" for a boolean property).  Note that the
     * property name should start with a lower case character, which will
     * be capitalized in the method names.
     * 通过调用getFoo和setFoo存取方法,为符合标准Java约定的属性构造一个PropertyDescriptor。
     * 如果参数名为"fred",则假定writer方法为"setFred",reader方法为"getFred"
     *(对于boolean属性则为"isFred"),注意:属性名应该以小写字母开头,而方法名称中的首写字母将是大写的。
     * @param propertyName The programmatic name of the property.
     * @param beanClass The Class object for the target bean.  For
     *          example sun.beans.OurButton.class.
     * @exception IntrospectionException if an exception occurs during
     *              introspection.
     */
    public PropertyDescriptor(String propertyName, Class<?> beanClass)
                throws IntrospectionException {
        this(propertyName, beanClass,
                Introspector.IS_PREFIX + NameGenerator.capitalize(propertyName),
                Introspector.SET_PREFIX + NameGenerator.capitalize(propertyName));
    }
示例
@Data
@NoArgsConstructor
@AllArgsConstructor
class User {

    private String name;

    private Integer age;

}

public static void main(String[] args) throws Exception {
        PropertyDescriptor descriptor = new PropertyDescriptor("name", User.class);
        // class java.lang.String
        System.out.println(descriptor.getPropertyType());
        // null
        System.out.println(descriptor.getPropertyEditorClass());
        // public java.lang.String cn.gov.zcy.item.User.getName()
        System.out.println(descriptor.getReadMethod());
        // public void cn.gov.zcy.item.User.setName(java.lang.String)
        System.out.println(descriptor.getWriteMethod());
}

getReadMethod

  • 获取读取属性值的方法。
@Data
    @NoArgsConstructor
    @AllArgsConstructor
    class User {

        private String name;

        private Integer age;

    }

public static void main(String[] args) throws Exception {
    PropertyDescriptor descriptor = new PropertyDescriptor("name", User.class);
    // public java.lang.String cn.gov.zcy.item.User.getName()
        System.out.println(descriptor.getReadMethod());

}

getWriteMethod

  • 获取写入属性值的方法。
@Data
@NoArgsConstructor
@AllArgsConstructor
class User {

    private String name;

    private Integer age;

}

public static void main(String[] args) throws Exception {

            Object catObj = User.class.newInstance();
            //获取Name属性
            PropertyDescriptor descriptor = new PropertyDescriptor("name", User.class);
            //得到对应的写方法
            Method method = descriptor.getWriteMethod();
            //将值赋进这个类中
            method.invoke(catObj,"Duansg");
            User cat = (User)catObj;
             // User(name=Duansg, age=null)
            System.out.println(cat);
}
目录
相关文章
|
4月前
|
Java API Spring
打造未来电商新引擎:揭秘Java可扩展API设计,让支付与物流灵活如丝,引领电商时代潮流!
【8月更文挑战第30天】本文通过电商平台案例,探讨了如何设计可扩展的Java API。首先定义支付和物流服务的接口与抽象类,然后实现具体服务,接着引入工厂模式或依赖注入管理服务实例,最后通过配置实现灵活扩展。这种设计确保了应用架构的灵活性和长期稳定性。
66 3
|
28天前
|
监控 Java
Java基础——反射
本文介绍了Java反射机制的基本概念和使用方法,包括`Class`类的使用、动态加载类、获取方法和成员变量信息、方法反射操作、以及通过反射了解集合泛型的本质。同时,文章还探讨了动态代理的概念及其应用,通过实例展示了如何利用动态代理实现面向切面编程(AOP),例如为方法执行添加性能监控。
|
1月前
|
Java
Java的反射
Java的反射。
29 2
|
2月前
|
存储 Java
[Java]反射
本文详细介绍了Java反射机制的基本概念、使用方法及其注意事项。首先解释了反射的定义和类加载过程,接着通过具体示例展示了如何使用反射获取和操作类的构造方法、方法和变量。文章还讨论了反射在类加载、内部类、父类成员访问等方面的特殊行为,并提供了通过反射跳过泛型检查的示例。最后,简要介绍了字面量和符号引用的概念。全文旨在帮助读者深入理解反射机制及其应用场景。
37 0
[Java]反射
|
3月前
|
安全 Java 索引
Java——反射&枚举
本文介绍了Java反射机制及其应用,包括获取Class对象、构造方法、成员变量和成员方法。反射允许在运行时动态操作类和对象,例如创建对象、调用方法和访问字段。文章详细解释了不同方法的使用方式及其注意事项,并展示了如何通过反射获取类的各种信息。此外,还介绍了枚举类型的特点和使用方法,包括枚举的构造方法及其在反射中的特殊处理。
75 9
Java——反射&枚举
|
2月前
|
安全 Java 测试技术
🌟Java零基础-反射:从入门到精通
【10月更文挑战第4天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
30 2
|
2月前
|
缓存 Java 数据库连接
使用 NCache 将 Java 微服务扩展到极致性能
使用 NCache 将 Java 微服务扩展到极致性能
36 8
|
3月前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
|
2月前
|
IDE Java 编译器
java的反射与注解
java的反射与注解
21 0
|
3月前
|
Java 程序员 编译器
Java的反射技术reflect
Java的反射技术允许程序在运行时动态加载和操作类,基于字节码文件构建中间语言代码,进而生成机器码在JVM上执行,实现了“一次编译,到处运行”。此技术虽需更多运行时间,但广泛应用于Spring框架的持续集成、动态配置及三大特性(IOC、DI、AOP)中,支持企业级应用的迭代升级和灵活配置管理,适用于集群部署与数据同步场景。