2005年2月21日

リフレクションの学習


[okita@tamkatsu-rhl9 okita]$ cat RefrectionTest.java
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

public class RefrectionTest {
        public static void main(String [] args) {
                try {
                        /*
                         * loading class by reflection mechanizm
                         */
                        Class [] types = new Class [] { String.class, String.class };
                        Constructor cons = TwoString.class.getConstructor(types);
                        Object [] objs = new Object [] { "a", "b" };

                        TwoString ts = (TwoString) cons.newInstance(objs);

                        /*
                         * access field
                         */
                        Field field = ts.getClass().getDeclaredField("message1");
                        System.out.println(field.get("message"));


                        ts.print();
                } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                } catch (InstantiationException e) {
                        e.printStackTrace();
                } catch (IllegalAccessException e ) {
                        e.printStackTrace();
                } catch (InvocationTargetException e) {
                        e.printStackTrace();
                } catch (NoSuchFieldException e) {
                        e.printStackTrace();
                }

        }
}

class TwoString {
        private String message1;
        private String message2;

        public TwoString(String s1, String s2) {
                message1 = s1;
                message2 = s2;
        }

        public void print() {
                System.out.println("message1:" + message1);
                System.out.println("message2:" + message2);
        }
}
[okita@tamkatsu-rhl9 okita]$ javac RefrectionTest.java
[okita@tamkatsu-rhl9 okita]$ java -verbose:class -cp . RefrectionTest
[Opened /home/okita/j2sdk1.4.2_01/jre/lib/rt.jar]
[Opened /home/okita/j2sdk1.4.2_01/jre/lib/sunrsasign.jar]
[Opened /home/okita/j2sdk1.4.2_01/jre/lib/jsse.jar]
[Opened /home/okita/j2sdk1.4.2_01/jre/lib/jce.jar]
...
...
[Loaded java.security.cert.Certificate from /home/okita/j2sdk1.4.2_01/jre/lib/rt.jar]
[Loaded RefrectionTest]
[Loaded java.lang.NoSuchFieldException from /home/okita/j2sdk1.4.2_01/jre/lib/rt.jar]
[Loaded java.lang.reflect.InvocationTargetException from /home/okita/j2sdk1.4.2_01/jre/lib/rt.jar]
[Loaded java.lang.IllegalAccessException from /home/okita/j2sdk1.4.2_01/jre/lib/rt.jar]
[Loaded java.lang.InstantiationException from /home/okita/j2sdk1.4.2_01/jre/lib/rt.jar]
[Loaded java.lang.NoSuchMethodException from /home/okita/j2sdk1.4.2_01/jre/lib/rt.jar]
[Loaded TwoString]
java.lang.IllegalAccessException: Class RefrectionTest can not access a member of class TwoString with modifiers "private"
[Loaded java.lang.StackTraceElement from /home/okita/j2sdk1.4.2_01/jre/lib/rt.jar]
        at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:57)
        at java.lang.reflect.Field.doSecurityCheck(Field.java:811)
        at java.lang.reflect.Field.getFieldAccessor(Field.java:758)
        at java.lang.reflect.Field.get(Field.java:228)
        at RefrectionTest.main(RefrectionTest.java:21)
[Loaded java.lang.Shutdown from /home/okita/j2sdk1.4.2_01/jre/lib/rt.jar]
[Loaded java.lang.Shutdown$Lock from /home/okita/j2sdk1.4.2_01/jre/lib/rt.jar]

message1がprivateフィールドのためアクセス時にIllegalAccessExceptionが発生している。

しかし、通常のフィールドやメソッドアクセスとは異なり、リフレクションをつかうとprivateなものに関してもアクセスできる。
そのためには、java.lang.reflect.AccessibleObject#setAccessibleメソッドをつかう。java.lang.reflect.Fieldクラスなどは
このAccessibleObjectクラスを継承しているため利用できる。




[okita@tamkatsu-rhl9 okita]$ cat RefrectionTest.java
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

import java.lang.reflect.InvocationTargetException;

public class RefrectionTest {
        public static void main(String [] args) {
                try {
                        /*
                         * loading class by reflection mechanizm
                         */
                        Class [] types = new Class [] { String.class, String.class };
                        Constructor cons = TwoString.class.getConstructor(types);
                        Object [] objs = new Object [] { "a", "b" };

                        TwoString ts = (TwoString) cons.newInstance(objs);

                        /*
                         * access field
                         */
                        Field field = ts.getClass().getDeclaredField("message1");
                        /*
                         * set accessible private field.
                         */
                        field.setAccessible(true);

                        // argment is instance of TwoString class.
                        System.out.println(field.get(ts));


                        ts.print();
                } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                } catch (InstantiationException e) {
                        e.printStackTrace();
                } catch (IllegalAccessException e ) {
                        e.printStackTrace();
                } catch (InvocationTargetException e) {
                        e.printStackTrace();
                } catch (NoSuchFieldException e) {
                        e.printStackTrace();
                }

        }
}

class TwoString {
        public String message1;
        private String message2;

        public TwoString(String s1, String s2) {
                message1 = s1;
                message2 = s2;
        }

        public void print() {
                System.out.println("message1:" + message1);
                System.out.println("message2:" + message2);
        }
}
[okita@tamkatsu-rhl9 okita]$ java -cp . RefrectionTest
a
message1:a
message2:b
[okita@tamkatsu-rhl9 okita]$ java -Djava.security.manager -cp . RefrectionTest
Exception in thread "main" java.security.AccessControlException: access denied (java.lang.reflect.ReflectPermission suppressAccessChecks)
        at java.security.AccessControlContext.checkPermission(AccessControlContext.java:269)
        at java.security.AccessController.checkPermission(AccessController.java:401)
        at java.lang.SecurityManager.checkPermission(SecurityManager.java:524)
        at java.lang.reflect.AccessibleObject.setAccessible(AccessibleObject.java:107)
        at RefrectionTest.main(RefrectionTest.java:21)
[okita@tamkatsu-rhl9 okita]$

-Djava.security.managerを負荷するとsetAccessibleメソッドを実装してもアクセスできなくなる。

また、リフレクション機能は、実行時に動的に解析する。つまりインタプリタのため非常に実行速度が遅くなる。
そして、ソースコードの解析が非常に困難になる。



http://www-6.ibm.com/jp/developerworks/java/040709/j_j-shared.html
Dynamic Proxy
Ruby#eval

http://www-6.ibm.com/jp/developerworks/java/030808/j_j-dyn0603.html