zoukankan      html  css  js  c++  java
  • java反射与动态代理

    Java反射与动态代理
      Java反射机制可以动态地获取类的结构,动态地调用对象的方法,是java语言一个动态化的机制。java动态代理可以在不改变被调用对象源码的前提下,在被调用方法前后增加自己的操作,极大地降低了模块之间的耦合性。这些都是java的基础知识,要想成为一名合格的程序猿,必须掌握!

    Java反射机制
      JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

      Java反射机制允许程序在运行时判断分析任意一个类的结构,包括成员变量和方法,并调用任意一个对象的方法。Eclipse可以自动弹出对象的方法及属性,就是利用了反射的原理。Java的动态代理就是利用了反射的特性来实现的。

    1、获取类Class对象

      1) A.class :不会加载类,也不会执行静态代码段;

      2) Class.forName("cn.test.reflect.A") :要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段;

      3) new A().getClass() :通过对象获取class

    2、反射创建对象

      1) 通过默认构造函数创建对象:Class<?> t = Class.forName("cn.test.reflect.A"); t.newInstance();

      2) 通过指定构造函数创建对象:Class<?> t = Class.forName("cn.test.reflect.A"); Constructor<?> cons[] = t.getConstructors(); A a = (A) cons[2].newInstance("aa","bb");

      注:

      ① Class<?>表示任何类型的类;

      ② newInstance()方法只能调用public的无参构造函数,它和new关键字创建实例的区别:创建对象的方式不一样,前者是使用类加载机制,后者是创建一个新类;

    3、Class类常用方法

      ▶ getName() :获得类的完整名字;

      ▶ getSuperclass() :获得类的父类;

      ▶ newInstance() :通过类的不带参数的构造方法创建这个类的一个对象;

      ▶ getFields() :获得当前类和父类中的public类型的所有属性;

      ▶ getDeclaredFields() :获得当前类(不包含父类)声明的所有属性,包括private和public;

       注:对于某个属性field,设置field.setAccessible(true),即可访问private的属性值,如field.get(obj)

      ▶ getMethods() :获得前类和父类中public类型的所有方法;

      ▶ getDeclaredMethods() :获得当前类(不包含父类)声明的所有方法,包括private和public;

      ▶ getMethod(String name, Class[] parameterTypes) :获得类的指定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型;

      ▶ getConstructors() :获得当前类的public类型的构造方法;

      ▶ getDeclaredConstructors() :获得当前类的public和private类型的构造方法;

      ▶ getConstructor(Class[] parameterTypes) :获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型;

      ▶ getInterfaces() :获得实现的接口;

      ▶ getSuperclass() :获得继承的父类;

    4、Java反射使用实例

    package com.hicoor.test.reflect;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    import java.lang.reflect.Parameter;
    
    class B{
    public int b;
    public B(){}
    }
    
    interface IA{}
    
    class A extends B implements IA{
    public A() { }
    
    public A(String str) { }
    
    public A(String str1, String str2) { }
    
    private String str;
    public int age;
    
    public int func1(String name) {
    System.out.println("hello " + name);
    return 8;
    }
    
    public void func1(String name1, String name2) {
    System.out.println("hello "+name1+","+name2);
    }
    }
    
    public class ReflectDemo {
    
    public static void main(String[] args) throws Exception {
    // 根据字符串获得类
    Class<?> demoClass = Class.forName("com.hicoor.test.reflect.A");
    
    // 获得类的完整名字
    System.out.println("类名"+demoClass.getName()); // com.hicoor.test.reflect.A
    // 获得类加载器,默认sun.misc.Launcher$AppClassLoader
    System.out.println("类加载器:"+demoClass.getClassLoader().getClass().getName());
    
    //根据Class的共有无参构造方法创建一个实例
    A newAObj = (A)demoClass.newInstance();
    
    // 获得类中声明的属性
    Field[] publicFields = demoClass.getFields(); // 获得当前类和父类中的public类型的所有属性,返回:age
    Field[] declareFields = demoClass.getDeclaredFields(); // 获得当前类(不包含父类)声明的所有属性,包括private和public,返回:str age
    Field specifyField = demoClass.getField("age"); // 根据名称获取指定属性
    specifyField.setAccessible(true);
    //修改属性
    specifyField.set(newAObj, 88);
    
    // 获得类的方法
    Method[] publicMethods = demoClass.getMethods(); // 获得前类和父类中public类型的所有方法
    Method[] declareMethods = demoClass.getDeclaredMethods(); // 获得当前类(不包含父类)声明的所有方法,包括private和public
    Method specifyMethod = demoClass.getDeclaredMethod("func1",new Class<?>[]{java.lang.String.class}); //根据方法名和方法参数类型指定获取一个方法
    //反射调用对象的方法
    specifyMethod.invoke(newAObj, "hans");
    
    //获得构造函数
    Constructor<?>[] publicConstructors = demoClass.getConstructors();
    Constructor<?>[] declareConstructors = demoClass.getDeclaredConstructors(); //获得当前类声明的所有private和public构造方法
    Constructor<?> constructor = demoClass.getConstructor(new Class<?>[]{java.lang.String.class}); //根据指定类型获得构造方法
    A newAobj2 = (A)constructor.newInstance("hello"); //根据指定构造函数创建实例
    
    //获得实现的接口
    Class<?>[] interfaces = demoClass.getInterfaces();
    
    //获得继承的父类
    Class<?> superclass = demoClass.getSuperclass();
    }
    
    //反射获得一个方法的明细定义
    private static void getMethodDetail(Method method) {
    String methodModifier = Modifier.toString(method.getModifiers()); //方法修饰符
    String returnType = method.getReturnType().getName(); //方法返回值
    Class<?>[] parameterTypes = method.getParameterTypes(); //方法参数类型
    System.out.print(methodModifier+" "+returnType+" "+ method.getName()+"(");
    int i=1;
    for (Class<?> parameterType : parameterTypes) {
    System.out.print(parameterType.getName() + " arg"+(i++));
    if(i<=parameterTypes.length){
    System.out.print(",");
    }
    }
    System.out.println(") {}");
    }
    
    }


    JAVA动态代理
      假如有这样的需求,要在某些模块方法调用前后加上一些统一的前后处理操作,比如在添加购物车、修改订单等操作前后统一加上登陆验证与日志记录处理,该怎样实现?首先想到最简单的就是直接修改源码,在对应模块的对应方法前后添加操作。如果模块很多,你会发现,修改源码不仅非常麻烦、难以维护,而且会使代码显得十分臃肿。

      这时候就轮到动态代理上场了,它可以通过反射在被调用方法前后加上自己的操作,而不需要更改被调用类的源码,大大地降低了模块之间的耦合性,体现了极大的优势。

    1、JDK动态代理

      JDK动态代理中包含一个类和一个接口,即Proxy类和InvocationHandler接口。

      1) InvocationHandler接口:

    public interface InvocationHandler {
      public Object invoke(Object proxy,Method method,Object[] args) throws Throwable;
    }
      参数说明:① Object proxy:指被代理的对象;② Method method:要调用的方法;③ Object[] args:方法调用时所需要的参数;

      2) Proxy类: Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法。

    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
      参数说明:① ClassLoader loader:类加载器;② Class<?>[] interfaces:得到全部的接口;③ InvocationHandler h:得到InvocationHandler接口的子类实例 ;

      3) 关于类加载器

      在Proxy类中的newProxyInstance()方法中需要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器,在Java中主要有以下三种类加载器:

      ① Booststrap ClassLoader:此加载器采用C++编写,通常加载jre/lib/rt.jar,一般开发中是看不到的;

      ② Extendsion ClassLoader:用来进行扩展类的加载,通常加载jre/lib/ext/*.jar;

      ③ AppClassLoader:(默认)加载classpath指定的类,是最常使用的是一种加载器;

      4) JDK动态代理实例:

      1 package com.hicoor.test.dynamicProxy;
      2 
      3 import java.lang.reflect.InvocationHandler;
      4 import java.lang.reflect.Method;
      5 import java.lang.reflect.Proxy;
      6 
      7 interface Animal {
      8 public void makeSound(String name);
      9 }
     10 
     11 class Dog implements Animal {
     12 @Override
     13 public void makeSound(String name) {
     14 System.out.println("Hi," + name + ",wang,wang~~");
     15 }
     16 }
     17 
     18 class Cat implements Animal {
     19 @Override
     20 public void makeSound(String name) {
     21 System.out.println("Hi," + name + ",miao,miao~~");
     22 }
     23 }
     24 
     25 /**
     26 * @author Hans 通用动态代理类,被调用对象方法前后增加特殊操作一样的类可都用此类代理
     27 */
     28 class AnimalProxy implements InvocationHandler {
     29 // 要代理的对象
     30 private Object target;
     31 
     32 /**
     33 * 绑定委托对象并返回一个代理类
     34 * 
     35 * @param target
     36 * @return
     37 */
     38 public Object getInstance(Object target) {
     39 this.target = target;
     40 // 取得代理对象
     41 return Proxy.newProxyInstance(target.getClass().getClassLoader(),
     42 target.getClass().getInterfaces(), this);
     43 }
     44 
     45 @Override
     46 public Object invoke(Object proxy, Method method, Object[] args)
     47 throws Throwable {
     48 Object result = null;
     49 System.out.println("方法调用前操作..");
     50 // 执行被调用方法主体
     51 result = method.invoke(target, args);
     52 System.out.println("方法调用后操作..");
     53 return result;
     54 }
     55 
     56 }
     57 
     58 public class DynamicProxyJDKDemo {
     59 public static void main(String[] args) {
     60 AnimalProxy proxy = new AnimalProxy();
     61 Animal dogProxy = (Animal) proxy.getInstance(new Dog());
     62 dogProxy.makeSound("Tom");
     63 }
     64 }
     65 2、Cglib动态代理
     66 
     67   JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
     68 
     69   使用cglib代理需下载cglib.jar文件,下载地址:http://www.java2s.com/Code/Jar/c/Downloadcloudcglibjar.htm
     70 
     71   Cglib动态代理实例:
     72 
     73 
     74 package com.hicoor.test.dynamicProxy;
     75 
     76 import java.lang.reflect.Method;
     77 
     78 import net.sf.cglib.proxy.Enhancer;
     79 import net.sf.cglib.proxy.MethodInterceptor;
     80 import net.sf.cglib.proxy.MethodProxy;
     81 
     82 
     83 class Snake{
     84 public void makeSound(String name) {
     85 System.out.println("Hi," + name + ",si,si~~");
     86 }
     87 }
     88 
     89 class AnimalProxyCglib implements MethodInterceptor {
     90 // 要代理的对象
     91 private Object target;
     92 
     93 /**
     94 * 创建代理对象
     95 * 
     96 * @param target
     97 * @return
     98 */
     99 public Object getInstance(Object target) {
    100 this.target = target;
    101 Enhancer enhancer = new Enhancer();
    102 enhancer.setSuperclass(this.target.getClass());
    103 // 回调方法
    104 enhancer.setCallback(this);
    105 // 创建代理对象
    106 return enhancer.create();
    107 }
    108 
    109 @Override
    110 public Object intercept(Object obj, Method method, Object[] args,
    111 MethodProxy proxy) throws Throwable {
    112 Object result = null;
    113 System.out.println("方法调用前操作..");
    114 // 执行被调用方法主体
    115 result = proxy.invokeSuper(obj, args);
    116 System.out.println("方法调用后操作..");
    117 return result;
    118 }
    119 
    120 }
    121 
    122 public class DynamicProxyCglibDemo {
    123 
    124 public static void main(String[] args) {
    125 AnimalProxyCglib proxy = new AnimalProxyCglib();
    126 Snake dogProxy = (Snake) proxy.getInstance(new Snake());
    127 dogProxy.makeSound("Tom");
    128 }
    129 
    130 }
    复制代码
    package com.hicoor.test.dynamicProxy;
    
    import java.lang.reflect.Method;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    
    class Snake{
        public void makeSound(String name) {
            System.out.println("Hi," + name + ",si,si~~");
        }
    }
    
    class AnimalProxyCglib implements MethodInterceptor {
        // 要代理的对象
        private Object target;
    
        /**
         * 创建代理对象
         * 
         * @param target
         * @return
         */
        public Object getInstance(Object target) {
            this.target = target;
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(this.target.getClass());
            // 回调方法
            enhancer.setCallback(this);
            // 创建代理对象
            return enhancer.create();
        }
    
        @Override
        public Object intercept(Object obj, Method method, Object[] args,
                MethodProxy proxy) throws Throwable {
            Object result = null;
            System.out.println("方法调用前操作..");
            // 执行被调用方法主体
            result = proxy.invokeSuper(obj, args);
            System.out.println("方法调用后操作..");
            return result;
        }
    
    }
    
    public class DynamicProxyCglibDemo {
    
        public static void main(String[] args) {
            AnimalProxyCglib proxy = new AnimalProxyCglib();
            Snake dogProxy = (Snake) proxy.getInstance(new Snake());
            dogProxy.makeSound("Tom");
        }
        
    }
    复制代码
  • 相关阅读:
    Flex 布局教程:语法篇
    ajax总结
    响应式技能储备
    响应式选项卡
    搜索框代码
    如何区分内存类型及查看内存的兼容性
    动手动脑4(03继承与多态)
    Log Explorer恢复sql误删数据
    判断YYYYMMDD这种格式的AJAX,基本上把闰年和2月等的情况都考虑进去了
    关于(object sender, System.EventArgs e)中参数sender和e的问题。
  • 原文地址:https://www.cnblogs.com/ilinuxer/p/4872814.html
Copyright © 2011-2022 走看看