java中的反射和代理

Wesley13
• 阅读 735

  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反射使用实例

java中的反射和代理 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动态代理实例:

java中的反射和代理 java中的反射和代理

package com.hicoor.test.dynamicProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface Animal {
    public void makeSound(String name);
}

class Dog implements Animal {
    @Override
    public void makeSound(String name) {
        System.out.println("Hi," + name + ",wang,wang~~");
    }
}

class Cat implements Animal {
    @Override
    public void makeSound(String name) {
        System.out.println("Hi," + name + ",miao,miao~~");
    }
}

/**
 * @author Hans 通用动态代理类,被调用对象方法前后增加特殊操作一样的类可都用此类代理
 */
class AnimalProxy implements InvocationHandler {
    // 要代理的对象
    private Object target;

    /**
     * 绑定委托对象并返回一个代理类
     * 
     * @param target
     * @return
     */
    public Object getInstance(Object target) {
        this.target = target;
        // 取得代理对象
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        Object result = null;
        System.out.println("方法调用前操作..");
        // 执行被调用方法主体
        result = method.invoke(target, args);
        System.out.println("方法调用后操作..");
        return result;
    }

}

public class DynamicProxyJDKDemo {
    public static void main(String[] args) {
        AnimalProxy proxy = new AnimalProxy();
        Animal dogProxy = (Animal) proxy.getInstance(new Dog());
        dogProxy.makeSound("Tom");
    }
}

2、Cglib动态代理

  JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。

  使用cglib代理需下载cglib.jar文件,下载地址:[http://www.java2s.com/Code/Jar/c/Downloadcloudcglibjar.htm][http_www.java2s.com\_Code\_Jar\_c\_Downloadcloudcglibjar.htm\]

  Cglib动态代理实例:

java中的反射和代理 java中的反射和代理

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");
    }
    
}

关注公众号:java宝典 java中的反射和代理

点赞
收藏
评论区
推荐文章
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
3年前
java中 什么是反射?
JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。JAVA反射(放射)机制:“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言(https://www.oschina.net/act
浪人 浪人
3年前
Java基础与提高干货系列——Java反射机制
前言今天介绍下Java的反射机制,以前我们获取一个类的实例都是使用new一个实例出来。那样太low了,今天跟我一起来学习学习一种更加高大上的方式来实现。正文Java反射机制定义Java反射机制是指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以
Wesley13 Wesley13
3年前
java面试(反射)05
1.什么是反射JAVA反射机制是在运行状态中,对于任意一个类,都能够获取这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取类信息以及动态调用对象内容就称为java语言的反射机制。2.反射的作用在运行时判断任意一个对象所属的
lzy lzy
3年前
RPC框架手撕之路---java反射以及动态代理机制
在上一篇文章中,我们提到了,RPC框架所需要的java基础,第一点就是java的动态代理机制,动态代理机制的基础是反射,无论是在实际编程或者是面试时,都是java知识的重中之重。java反射:定义:在运行状态中,对于任意一个类,都能够知道这一个类的所有属性和方法,对于任意一个对象都能够通过反射机制调用一个类的任意方法,这种动态获取类信息以及动态调用类方法
御弟哥哥 御弟哥哥
3年前
Java基础与提高干货系列 -- Java反射机制
前言今天介绍下Java的反射机制,以前我们获取一个类的实例都是使用new一个实例出来。那样太low了,今天跟我一起来学习学习一种更加高大上的方式来实现。正文Java反射机制定义Java反射机制是指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性
桃浪十七丶 桃浪十七丶
3年前
工厂模式实例(顺便回忆反射机制的应用)
一、原理反射机制的原理JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。工厂模式自述所谓工厂模式,是说由某个产品类接口、产品实现类、工厂类、客户端(单元测试主类)构成的一个模式,大程度的降低了代码的
Wesley13 Wesley13
3年前
Java基础之反射(非常重要)
反射是框架设计的灵魂(使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码))一、反射的概述JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法
Wesley13 Wesley13
3年前
Java重点基础:反射机制
一、什么是反射?Java反射说的是在运行状态中,对于任何一个类,我们都能够知道这个类有哪些方法和属性。对于任何一个对象,我们都能够对它的方法和属性进行调用。我们把这种动态获取对象信息和调用对象方法的功能称之为反射机制。二、反射的三种方式
Wesley13 Wesley13
3年前
Java反射机制及适用场景
什么是Java反射机制?JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的以及动态调用对象的方法的功能称为Java的反射机制。反射的适用场景是什么?1.当你做一个软件可以安装插件的功能,你连插件的类型名称都不知道,你怎么实例化这个对象呢