zoukankan      html  css  js  c++  java
  • 我对java的理解(二)——反射是小偷的万能钥匙

      在我们生活中,车上或者路上有时候会遇到一种很讨厌的人——“小偷”,趁我们不注意或者疏忽的时候拿走属于我们的东西。更有甚者,趁我们不在家的时候,手持一把万能钥匙,打开我们的房门,悠闲的查看房间的布置,翻找着他们需要的东西,惬意的时候也许会躺在客厅的沙发上,喝着冰箱里的饮料,俨然一副真正主人的样子。在JAVA中就有这样一个存在——“反射”,这是JDK提供的一把万能钥匙,任何人都可以使用它来获取本不应该属于自己的东西。

      在刚学习JAVA的时候,我们就知道,JAVA提供了private,protected,public,default四种修饰符,这四种修饰符象征着我们的使用权限。就好像我们的房子,房子是我们自己的,但是可以被子孙们继承,因此,房子可以被修饰为protected。房子里有的东西是我私人的,只属于我自己,谁都不能用,这可以被修饰为private。当然有的东西,亲戚朋友来了,都可以用,可以吃,那么可以被定义为default。看门外没有垃圾桶,个人本着爱护环境的初衷,放了一个垃圾桶在外面,谁都可以用,垃圾桶自己就可以被修饰为public。但是“小偷”就是没有这种限制,管你东西是不是私人的,他仗着手上的万能钥匙就是可以把这种访问权限糟蹋掉。尤其这把钥匙还是JDK出品的,很无奈!

      那下面就看看JDK提供的这把万能钥匙到底能干些什么吧。

      1、创建House类,代码如下:

    package person.lb.reflect;
    
    /**
     * 房子
     * @author noboudns
     *
     */
    public class House {
    
        //拥有者
        private String owner = "nobounds";
        //房间数
        private int roomNum = 3;
        //垃圾桶
        public String dustbin = "垃圾桶";
        
        public String getDustbin() {
            return dustbin;
        }
        
        private void turnOnTV() {
            System.out.println("打开电视……");
        }
        
    }

      2、创建KeyDemo 类,代码如下:

    package person.lb.reflect;
    
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    
    public class KeyDemo {
    
        public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException, ClassNotFoundException, SecurityException, NoSuchFieldException {
            Class<?> clazz = Class.forName("person.lb.reflect.House");
            String className = clazz.getName();
            System.out.println("户主是:" + className);
            
            Field[] fields = clazz.getDeclaredFields();
            System.out.println("看看家里都有什么东西:");
            for(Field field : fields ) {
                System.out.println("类型:" + field.getType() + ",名称:" + field.getName() + ",使用权限:" + Modifier.toString(field.getModifiers()));
            }
            
            System.out.println("再看看家里有什么好玩的:");
            Method[] methods = clazz.getDeclaredMethods();
            for(Method method : methods) {
                System.out.println("名字:" + method.getName() 
                                 + ",需要的东西:" + method.getParameterTypes().toString()
                                 + ",得到什么:" + method.getReturnType().toString());
                System.out.println("用用看效果怎么样,嘿嘿!");
                //设置为可访问的
                method.setAccessible(true);
                method.invoke(clazz.newInstance());
            }
            System.out.println("心情不错,把房子据为己有吧!");
            House house = (House) clazz.newInstance();
            Field field = clazz.getDeclaredField("ownerName");
            field.setAccessible(true);
            field.set(house, "thief");
            System.out.println("现在房子的主人是:" + house.getOwnerName() );
        }
    }

    结果为:

    户主是:person.lb.reflect.House
    看看家里都有什么东西:
    类型:class java.lang.String,名称:ownerName,使用权限:private
    类型:int,名称:roomNum,使用权限:private
    类型:class java.lang.String,名称:dustbin,使用权限:public
    再看看家里有什么好玩的:
    名字:getOwnerName,需要的东西:[Ljava.lang.Class;@3d4b7453,得到什么:class java.lang.String
    用用看效果怎么样,嘿嘿!
    名字:getDustbin,需要的东西:[Ljava.lang.Class;@1cc2ea3f,得到什么:class java.lang.String
    用用看效果怎么样,嘿嘿!
    名字:turnOnTV,需要的东西:[Ljava.lang.Class;@40a0dcd9,得到什么:void
    用用看效果怎么样,嘿嘿!
    打开电视……
    心情不错,把房子据为己有吧!
    现在房子的主人是:thief

      当然了,JDK中不止提供了这么多功能,如果有兴趣可以自己私下去查找相关资料,本文就不多说什么了。

      之前因为点儿事随笔写到了这里就发出去了,博友对内容有质疑,因此在这里稍微补充一下。

      我把反射冠名为“反射是小偷的万能钥匙”,虽然听着不好听,但是像这种有特殊能力的人或事物必然有它存在的道理以及特殊性,就像盗墓贼为了私利被称为“贼”,如果被国家收编,那么也能成为一个出色的考古学家。如果专门盗取别人私密数据以及搞破坏的出色黑客弃暗投明,那么很大概率上也能成为一个优秀的安全专家。小偷擅长偷盗,但是做起反偷盗的事情来也是一把好手。在JAVA中反射就被用在很多的框架以及产品中,例如Struts1/2,hibernate,Spring,Tomcat等使用反射不仅很大程度上提高了程序的灵活性,并对框架在加载以及管理Class的时候提供了很大的便利性,以及提供在程序运行时动态更改程序的行为的能力,进而提供丰富的功能,这也更方面了我们开发者。Spring的核心功能IOC和AOP的实现也都应用了JAVA提供的反射功能。那么下面就贴上两段Spring依靠注解注入依赖的源码见识一下。

      org.springframework.util.ReflectionUtils类(反射工具类):

    ……
      //如果类中的属性不是public修饰或者是final修饰并且属性是不可访问的,那么设置字段为可访问
      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);
            }
        }
    
        /**
         * Make the given method accessible, explicitly setting it accessible if
         * necessary. The {@code setAccessible(true)} method is only called
         * when actually necessary, to avoid unnecessary conflicts with a JVM
         * SecurityManager (if active).
         * @param method the method to make accessible
         * @see java.lang.reflect.Method#setAccessible
         */
        public static void makeAccessible(Method method) {
            if ((!Modifier.isPublic(method.getModifiers()) ||
                    !Modifier.isPublic(method.getDeclaringClass().getModifiers())) && !method.isAccessible()) {
                method.setAccessible(true);
            }
        }
    
        /**
         * Make the given constructor accessible, explicitly setting it accessible
         * if necessary. The {@code setAccessible(true)} method is only called
         * when actually necessary, to avoid unnecessary conflicts with a JVM
         * SecurityManager (if active).
         * @param ctor the constructor to make accessible
         * @see java.lang.reflect.Constructor#setAccessible
         */
        public static void makeAccessible(Constructor<?> ctor) {
            if ((!Modifier.isPublic(ctor.getModifiers()) ||
                    !Modifier.isPublic(ctor.getDeclaringClass().getModifiers())) && !ctor.isAccessible()) {
                ctor.setAccessible(true);
            }
        }
    ……

      org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor类(@Autowired注解处理类):

    ……
    private class AutowiredFieldElement extends InjectionMetadata.InjectedElement {
    
            private final boolean required;
    
            private volatile boolean cached = false;
    
            private volatile Object cachedFieldValue;
    
            public AutowiredFieldElement(Field field, boolean required) {
                super(field, null);
                this.required = required;
            }
    
            @Override
            protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
                Field field = (Field) this.member;
                try {
                    Object value;
                    if (this.cached) {
                        value = resolvedCachedArgument(beanName, this.cachedFieldValue);
                    }
                    else {
                        DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
                        desc.setContainingClass(bean.getClass());
                        Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
                        TypeConverter typeConverter = beanFactory.getTypeConverter();
                        value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
                        synchronized (this) {
                            if (!this.cached) {
                                if (value != null || this.required) {
                                    this.cachedFieldValue = desc;
                                    registerDependentBeans(beanName, autowiredBeanNames);
                                    if (autowiredBeanNames.size() == 1) {
                                        String autowiredBeanName = autowiredBeanNames.iterator().next();
                                        if (beanFactory.containsBean(autowiredBeanName)) {
                                            if (beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
                                                this.cachedFieldValue = new RuntimeBeanReference(autowiredBeanName);
                                            }
                                        }
                                    }
                                }
                                else {
                                    this.cachedFieldValue = null;
                                }
                                this.cached = true;
                            }
                        }
                    }
                    if (value != null) {
                        //设置字段访问权限为可访问
                        ReflectionUtils.makeAccessible(field);
                        //对字段进行赋值
                        field.set(bean, value);
                    }
                }
                catch (Throwable ex) {
                    throw new BeanCreationException("Could not autowire field: " + field, ex);
                }
            }
        }
    ……
  • 相关阅读:
    Educational Codeforces Round 86 (Rated for Div. 2) D. Multiple Testcases
    Educational Codeforces Round 86 (Rated for Div. 2) C. Yet Another Counting Problem
    HDU
    HDU
    HDU
    HDU
    Good Bye 2019 C. Make Good (异或的使用)
    Educational Codeforces Round 78 (Rated for Div. 2) C. Berry Jam
    codeforces 909C. Python Indentation
    codeforces1054 C. Candies Distribution
  • 原文地址:https://www.cnblogs.com/nobounds/p/5436791.html
Copyright © 2011-2022 走看看