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);
                }
            }
        }
    ……
  • 相关阅读:
    [C#][Log4Net] 配置
    C# 浅拷贝与深拷贝(复制)
    AJAX学习
    Web应用程序与Web网站在IIS中部署
    hexo配置主题发表文章
    【JQuery】学习
    Hexo博客网站再配置
    win7安装和配置IIS7
    常见正则表达式
    【HTML】id与name的用法
  • 原文地址:https://www.cnblogs.com/nobounds/p/5436791.html
Copyright © 2011-2022 走看看