欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

Java反射和动态代理的使用解读,

来源: javaer 分享于  点击 13783 次 点评:196

Java反射和动态代理的使用解读,


目录
  • 1、反射
    • 1.1 反射的概述
    • 1.2 反射作用
    • 1.3 获取字节码文件对象的方式
    • 1.4 字节码文件和字节码文件对象
    • 1.5 获取构造方法
    • 1.6 获取构造方法并创建对象
    • 1.7 获取成员变量并获取值和修改值
    • 1.8 获取成员方法
    • 1.9 获取成员方法并运行
  • 2. 动态代理
    • 2.1 动态代理好处
    • 2.2 动态代理三要素
    • 2.3 动态代理简单实现
    • 2.4 动态代理扩展
  • 总结

    1、反射

    1.1 反射的概述

    是在运行状态中,不用创建对象就能够调用任意一个类的所有属性和方法;

    1.2 反射作用

    反射都是从class字节码文件中获取的内容。

    • 获取class字节码文件的对象
    • 利用反射如何获取构造方法(创建对象)
    • 利用反射如何获取成员变量(赋值,获取值)
    • 利用反射如何获取成员方法(运行)

    1.3 获取字节码文件对象的方式

    • Class这个类里面的静态方法forName(“全类名”)(最常用)
    • 通过class属性获取,一般更多的是当做参数进行传递
    • 通过对象获取字节码文件对象,当我们已经有了这个类的对象时,才可以使用。
    public class ReflectDemo {
        public static void main(String[] args) throws ClassNotFoundException {
            //1. 第一种方式
            //全类名 : 包名 + 类名
            Class clazz1 = Class.forName("com.ya.reflect.demo01.Student");
    
            //2. 第二种方式
            Class clazz2 = Student.class;
    
            //3.第三种方式
            Student s = new Student();
            Class clazz3 = s.getClass();
    
            System.out.println(clazz1 == clazz2);
            System.out.println(clazz2 == clazz3);
        }
    }

    1.4 字节码文件和字节码文件对象

    • java文件(源代码阶段):就是编写的java代码。
    • 字节码文件(加载阶段):就是通过java文件编译之后的class文件(是在硬盘上真实存在的,用眼睛能看到的)
    • 字节码文件对象(运行阶段):当class文件加载到内存之后,虚拟机自动创建出来的对象。这个对象里面至少包含了:构造方法,成员变量,成员方法。

    反射获取的是字节码文件对象,这个对象在内存中是唯一的。

    1.5 获取构造方法

    规则:

    • get表示获取
    • Declared表示私有
    • 最后的s表示所有,复数形式
    • 如果当前获取到的是私有的,必须要临时修改访问权限setAccessible(true),否则无法使用
    public class ReflectDemo {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
    
            //1.获取class字节码文件对象
            Class clazz = Class.forName("com.ya.reflect.demo02.Student");
    
            //2.获取构造方法
            // 2.1 返回所有公共构造方法对象的数组
            System.out.println("-----返回所有公共构造方法对象的数组----");
            Constructor[] con1 = clazz.getConstructors();
            for (Constructor con : con1) {
                System.out.println(con);
            }
    
            // 2.2 返回所有构造方法对象的数组
            System.out.println("-------返回所有构造方法对象的数组------");
            Constructor[] con2 = clazz.getDeclaredConstructors();
            for (Constructor con : con2) {
                System.out.println(con);
    
            }
    
            // 2.3 返回所有构造方法对象的数组
            System.out.println("-------返回单个公共构造方法对象------");
            Constructor con3 = clazz.getConstructor(String.class);
            System.out.println(con3);
            //2.4 获取指定的空参构造
            System.out.println("-------获取指定的空参构造------");
            Constructor con4 = clazz.getConstructor();
            System.out.println(con4);
    
    
            // 2.5 返回所有构造方法对象的数组
            System.out.println("-------返回单个构造方法对象------");
            Constructor con5 = clazz.getDeclaredConstructor(String.class,int.class);
            System.out.println(con5);
    
    
            System.out.println("-----获取构造方法的权限修饰符(返回整数)--------");
            // 返回的是整数,public 1,private 2,protected 4,abstract 1024
            int modifiers = con5.getModifiers();
            System.out.println(modifiers);
    
            System.out.println("-----获取构造方法的参数--------");
            Parameter[] parameters = con5.getParameters();
            for (Parameter parameter : parameters) {
                System.out.println(parameter);
            }
    
            System.out.println("--------创建对象-----------");
            //暴力反射:表示临时取消权限校验
            con5.setAccessible(true);   // 如果这个权限修饰符是私有的,就要取消权限校验
            Student stu = (Student) con5.newInstance("张三", 23);
            System.out.println(stu);
        }
    }

    1.6 获取构造方法并创建对象

    public class ReflectDemo02 {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
            
            //需求1:获取空参,并创建对象(所创建的对象的属性是默认值)
            //1.获取整体的字节码文件对象
            Class clazz = Class.forName("com.ya.reflect.demo02.Student");
            //2.获取空参的构造方法
            Constructor con = clazz.getConstructor();
            //3.利用空参构造方法创建对象
            Student stu = (Student) con.newInstance();
            System.out.println(stu);
    
            System.out.println("----------------------------------");
    
            //需求2:获取带参构造,并创建对象
            //1.获取整体的字节码文件对象
            Class clazz2 = Class.forName("com.ya.reflect.demo02.Student");
            //2.获取有参构造方法
            Constructor con2 = clazz2.getDeclaredConstructor(String.class, int.class);
            //3.临时修改构造方法的访问权限(暴力反射)
            con2.setAccessible(true);
            //4.直接创建对象
            Student stu2 = (Student) con2.newInstance("zhangsan", 23);
            System.out.println(stu2);
        }
    }

    1.7 获取成员变量并获取值和修改值

    public class ReflectDemo {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
            //1.获取class字节码文件的对象
            Class clazz = Class.forName("com.ya.reflect.demo03.Student");
            //2.获取所有的成员变量
            System.out.println("-----获取所有的成员变量----");
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                System.out.println(field);
            }
            //3.获取单个的成员变量
            System.out.println("-----获取单个的成员变量----");
            Field fieldName = clazz.getDeclaredField("name");
            System.out.println(fieldName);
            System.out.println("-----获取成员变量的数据类型----");
            Class<?> type = fieldName.getType();
            System.out.println(type);
            System.out.println("-----获取成员变量记录的值----");
            Student student = new Student("zhangsan", 23, "男");
            fieldName.setAccessible(true);
            String value = (String) fieldName.get(student);
            System.out.println(value);
            System.out.println("-----修改对象里面记录的值---");
            fieldName.set(student,"lisi");
            System.out.println(student);
        }
    }

    1.8 获取成员方法

    public class ReflectDemo {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
           
            //1. 获取class字节码文件对象
            Class clazz = Class.forName("com.ya.reflect.demo04.Student");
    
            //2. 获取里面所有的方法对象(包含父类中所有的公共方法)
            System.out.println("-------获取里面所有的方法对象(包含父类中所有的公共方法)-----");
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                System.out.println(method);
            }
            // 3. 获取里面所有的方法对象(不能获取父类的,但是可以获取本类中私有的方法)
            System.out.println("--获取里面所有的方法对象(不能获取父类的,但是可以获取本类中私有的方法)--");
            Method[] declaredMethods = clazz.getDeclaredMethods();
            for (Method declaredMethod : declaredMethods) {
                System.out.println(declaredMethod);
            }
            // 4.获取指定的单一方法
            System.out.println("-------获取指定的单一方法-----");
            Method eatMethod = clazz.getDeclaredMethod("eat", String.class);
            System.out.println(eatMethod);
    
            // 5.获取方法的修饰符(返回整数)
            System.out.println("-------获取方法的修饰符(返回整数)-----");
            int modifiers = eatMethod.getModifiers();
            System.out.println(modifiers);
    
            // 6.获取方法的名字
            System.out.println("-------获取方法的名字-----");
            String eatMethodName = eatMethod.getName();
            System.out.println(eatMethodName);
    
            // 7.获取方法的形参
            System.out.println("-------获取方法的形参-----");
            Parameter[] parameters = eatMethod.getParameters();
            for (Parameter parameter : parameters) {
                System.out.println(parameter);
            }
    
            //8.获取方法的抛出的异常
            System.out.println("-------获取方法的抛出的异常-----");
            Class[] exceptionTypes = eatMethod.getExceptionTypes();
            for (Class exceptionType : exceptionTypes) {
                System.out.println(exceptionType);
            }
        }
    }

    1.9 获取成员方法并运行

    public class ReflectDemo {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
            //1. 获取class字节码文件对象
            Class clazz = Class.forName("com.ya.reflect.demo04.Student");
            //2. 获取指定的单一方法
            Method eatMethod = clazz.getDeclaredMethod("eat", String.class); 
            // 3. 方法运行
            System.out.println("-------方法运行-----");
            Student student = new Student();
            eatMethod.setAccessible(true);
            String result = (String) eatMethod.invoke(student, "汉堡包");
            System.out.println(result);
        }
    }

    2. 动态代理

    2.1 动态代理好处

    • 无侵入式的给方法增强功能。
    • 调用者(BitStar)–>代理(ProxyUtil)–>对象(Star)

    2.2 动态代理三要素

    • 接口:代理对象,被代理类和代理类都需要实现这个接口(本例是Star)
    • 代理类:通过Proxy类动态生成的代理类(本例是ProxyUtil)
    • 被代理类:代理类实例,它会代替被代理对象处理方法调用(本例是BigStar)

    注:代理可以增强或者拦截的方法都在接口中,接口需要写在newProxyInstance的第二个参数里。

    2.3 动态代理简单实现

    //代理对象
    public interface Star {
    	//可以把所有想要被代理的方法定义在接口当中
        //唱歌
        public abstract String sing(String name);
        //跳舞
        public abstract void dance();
    }
    //被代理类
    public class BigStar implements Star{
        private String name;
    
        public BigStar() {
        }
        public BigStar(String name) {
            this.name = name;
        }
        //唱歌
        @Override
        public String sing(String name){
            System.out.println(this.name + "正在唱" + name);
            return "谢谢";
        }
        //跳舞
        @Override
        public void dance(){
            System.out.println(this.name + "正在跳舞");
        }
        
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String toString() {
            return "BigStar{name = " + name + "}";
        }
    }
    
    
    //代理类
    // 创建代理   java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法
    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
    参数一:用于指定用哪个类加载器,去加载生成的代理类
    参数二:指定接口,这些接口用于指定生成的代理有哪些方法
    参数三:用来指定生成的代理对象要干什么事情
       
    public class ProxyUtil {
    
        /**
         *  形参:被代理的明星对象
         *  返回值:给明星创建的代理
         */
        public static Star createProxy(BigStar bigStar){
            // 创建代理
            // 这个代码没有显示实现Star接口,但通过 JDK动态代理机制 隐式地为生成的代理类添加了Star接口的实现,从而可以看作实现了Star接口
            Star star = (Star) Proxy.newProxyInstance(
                    ProxyUtil.class.getClassLoader(),//参数一:用于指定用哪个类加载器,去加载生成的代理类
                    new Class[]{Star.class},//参数二:指定接口,这些接口用于指定生成的代理有哪些方法
                    new InvocationHandler() {	//参数三:用来指定生成的代理对象要干什么事情
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            /**
                             * 参数一:代理的对象,即star变量引用的对象
                             * 参数二:要运行的方法,sing,dance
                             * 参数三:调用sing方法时,传递的实参
                             */
                            if("sing".equals(method.getName())){
                                System.out.println("准备话筒,收钱");
                            }else if("dance".equals(method.getName())){
                                System.out.println("准备场地,收钱");
                            }
                            //去找大明星开始唱歌或者跳舞
                            //代码的表现形式:调用大明星里面唱歌或者跳舞的方法
                            return method.invoke(bigStar,args);
                        }
                    }
            );
            return star;
        }
    }
    
    //代理测试
    public class Test {
        public static void main(String[] args) {
            //1. 获取代理的对象
            BigStar bigStar = new BigStar("机哥");
            Star proxy = ProxyUtil.createProxy(bigStar);
    
            //2. 调用唱歌的方法
            String result = proxy.sing("只因你太美");
            System.out.println(result);
        }
    }

    2.4 动态代理扩展

    动态代理,还可以拦截方法。比如:在这个故事中,经纪人作为代理,如果别人让邀请大明星去唱歌,打篮球,经纪人就增强功能。但是如果别人让大明星去扫厕所,经纪人就要拦截,不会去调用大明星的方法。

    public class ProxyUtil {
        public static Star createProxy(BigStar bigStar){
            public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
            Star star = (Star) Proxy.newProxyInstance(
                    ProxyUtil.class.getClassLoader(),
                    new Class[]{Star.class},
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            if("cleanWC".equals(method.getName())){
                                System.out.println("拦截,不调用大明星的方法");
                                return null;
                            }
                            //如果是其他方法,正常执行
                            return method.invoke(bigStar,args);
                        }
                    }
            );
            return star;
        }
    }

    总结

    以上为个人经验,希望能给大家一个参考,也希望大家多多支持3672js教程。

    您可能感兴趣的文章:
    • Java的动态代理和静态代理及反射常用API详解
    • Java使用反射和动态代理实现一个View注解绑定库
    • Java反射的应用之动态代理深入理解
    • Java反射(JDK)与动态代理(CGLIB)详解
    • 详解Java中的反射机制和动态代理
    • Java动态代理和反射机制详解
    相关栏目:

    用户点评