前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java反射与动态代理

Java反射与动态代理

作者头像
Java栈
修改2019-07-29 11:22:09
5760
修改2019-07-29 11:22:09
举报
文章被收录于专栏:Java栈

<反射机制>

    为了更好的理解java的反射机制,最好先对java的泛型有所了解。java泛型就是参数化类型,即为所操作的数据类型指定一个参数。如果只指定了<?>,而没有extends,则默认是允许Object及其下的任何Java类。也就是任意类

1. Java运行时,对任意一个类,想知道它有哪些属性和方法,对于任意一个对象,想调用它的任意一个方法,都是可以实现的,这来自JAVA的反射机制

2. JAVA的反射机制主要功能:

    (1)在运行时判断任意一个对象所属的类。

    (2)在运行时构造任意一个类的对象。

    (3)在运行时判断任意一个类所具有的成员变量和方法。

    (4)在运行时调用任意一个对象的方法

    前提是在运行时,不是编译时,也就是在运行前并不知道调用哪一个类,通过反射就可以做到这些

3.在JDK中,主要由以下类来实现JAVA反射机制,这些类位于java.lang.reflect包中:

    Class类:代表一个类

    Field 类:代表类的成员变量(成员变量也称为类的属性)。

    Method类:代表类的方法。

Constructor 类:代表类的构造方法。

    Array类:提供了动态创建数组,以及访问数组的元素的静态方法

4. Class类是Reflection API 中的核心类,它有以下方法

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

    getFields():获得类的public类型的属性

    getDeclaredFields():获得类的所有属性

    getMethods():获得类的public类型的方法

    getDeclaredMethods():获得类的所有方法

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

    getConstructors():获得类的public类型的构造方法

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

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

【实例1】

读取命令行参数指定的类名,然后打印这个类所具有的方法信息。即JAVA的反射机制功能中的“在运行时判断任意一个类所具有的方法”

代码语言:javascript
复制
/**
 * @FileName    DumpMethods.java
 * @Description 
 * @author      looton@163.com
 * @Time        
 */

import java.lang.reflect.Method;

public class DumpMethods {
	public static void main(String args[]) throws Exception {
		// 加载并初始化命令行参数指定的类
		Class<?> classType = Class.forName(args[0]);
		// 获得类的所有方法
		Method methods[] = classType.getDeclaredMethods();
		for (int i = 0; i < methods.length; i++) {
			System.out.println(methods[i].toString());
		}
	}
}

在命令行里输入参数,此处参数必须为类的全称,包含包名

运行输出该类所具有的方法,包括private的

【重点】

(1). Class.forName(args[0])  传入的是类的全称,返回的是与这个类所对应的一个Class类的实例,对于JAVA每一个类在实例化的时候都会有一个与它对应的Class类的一个实例,这个实例就是用来获得这个类的对象本身所具有的一些属性和方法

(2). Method methods[] = classType.getDeclaredMethods()  获得该类所有的方法,包括private的

【实例2】

ReflectTester类有一个copy(Object object)方法,这个方法能够创建一个和参数object 同样类型的对象,然后把object对象中的所有属性拷贝到新建的对象中,并将它返回。这个例子只能复制简单的JavaBean,假定JavaBean 的每个属性都有public 类型的getXXX()和setXXX()方法。体现了JAVA的反射机制中的“在运行时判断任意一个类所具有的属性”、“在运行时调用任意一个对象的方法”和“在运行时构造任意一个类的对象”

代码语言:javascript
复制
/**
 * @FileName    ReflectTester.java
 * @Description 
 * @author      looton@163.com
 * @Time        
 */

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectTester {
	public Object copy(Object object) throws Exception {
		// 获得对象的类的类型
		Class<?> classType = object.getClass();
		System.out.println("Class:" + classType.getName());
		// 先调用Class类的getConstructor()方法获得一个Constructor
		// 对象,它代表默认的构造方法,然后调用Constructor对象的newInstance()方法构造一个实例。
		Object objectCopy = classType.getConstructor(new Class[] {}).newInstance(new Object[] {});
		// 获得对象的所有属性
		Field fields[] = classType.getDeclaredFields();
		for (int i = 0; i < fields.length; i++) {
			Field field = fields[i];
			String fieldName = field.getName();
			String firstLetter = fieldName.substring(0, 1).toUpperCase();
			// 获得和属性对应的getXXX()方法的名字
			String getMethodName = "get" + firstLetter + fieldName.substring(1);
			// 获得和属性对应的setXXX()方法的名字
			String setMethodName = "set" + firstLetter + fieldName.substring(1);
			// 获得和属性对应的getXXX()方法
			Method getMethod = classType.getMethod(getMethodName, new Class[] {});
			// 获得和属性对应的setXXX()方法
			Method setMethod = classType.getMethod(setMethodName, new Class[] { field.getType() });
			// 调用原对象的getXXX()方法
			Object value = getMethod.invoke(object, new Object[] {});
			System.out.println(fieldName + ":" + value);
			// 调用拷贝对象的setXXX()方法
			setMethod.invoke(objectCopy, new Object[] { value });
		}
		return objectCopy;
	}

	public static void main(String[] args) throws Exception {
		Customer customer = new Customer("Tom", 21);
		customer.setId(new Long(1));
		Customer customerCopy = (Customer) new ReflectTester().copy(customer);
		System.out.println("Copy information:" + customerCopy.getId() + " " + customerCopy.getName() + " "
				+ customerCopy.getAge());
	}
}

class Customer {
	private Long id;
	private String name;
	private int age;

	public Customer() {
	}

	public Customer(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
}

打印该对象所属的类的名字 后打印出原对象中所有属性及对应值  最后打印出新建对象复制原对象后的属性及对应值

【重点】

(1). 获得对象的类的类型 Class<?> classType = object.getClass();与Class.forName()是一样的结果,返回一个对象运行时的Class,这个Class描述了当前这个对象所具有的一些属性和方法,也就是内部的构造。getClass方法定义在Object类里面,也就是JAVA中任何一个类都有这个方法 (2). Object objectCopy = classType.getConstructor(new Class[] {}).newInstance(new Object[] {});这句话主要的目的是通过默认的构造方法创建一个该Class类的实例。通过Class实例调用getConstructor方法,可以获得当前对象的构造方法。参数是用来辨别返回哪个构造方法的,所以是Class类型数组,无参数表示返回默认构造方法。 newInstance方法,通过当前构造方法生成当前类的一个实例。

【实例3】

该类的main()方法中,运用反射机制调用一个InvokeTester对象的add()和echo()方法。add()方法的两个参数为int 类型,获得表示add()方法的Method对象的代码如下:

Method addMethod=classType.getMethod("add",new Class[]{int.class,int.class});

Method类的invoke(Object obj,Object args[])方法接收的参数必须为对象,如果参数为基本类型数据,必须转换为相应的包装类型的对象。invoke()方法的返回值总是对象,如果实际被调用的方法的返回类型是基本类型数据,那么invoke()方法会把它转换为相应的包装类型的对象,再将其返回。体现了JAVA的反射机制功能中的“在运行时获得任意一个类的方法”、“在运行时调用任意一个对象的方法”

代码语言:javascript
复制
/**
 * @FileName    InvokeTester.java
 * @Description 
 * @author      looton@163.com
 * @Time        
 */

import java.lang.reflect.Method;

public class InvokeTester {
	public int add(int param1, int param2) {
		return param1 + param2;
	}

	public String echo(String msg) {
		return "echo: " + msg;
	}

	public static void main(String[] args) throws Exception {
		// 获得类的类型
		Class<?> classType = InvokeTester.class;
		// 生成实例
		Object invokeTester = classType.newInstance();

		// 调用InvokeTester对象的add()方法
		Method addMethod = classType.getMethod("add", new Class[] { int.class, int.class });
		// 对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
		Object result = addMethod.invoke(invokeTester, new Object[] { 100, 200 });
		System.out.println(result);

		// 调用InvokeTester对象的echo()方法
		Method echoMethod = classType.getMethod("echo", new Class[] { String.class });
		result = echoMethod.invoke(invokeTester, new Object[] { "Hello" });
		System.out.println(result);
	}
}

<代理模式>

1. 代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用

2. 代理模式一般涉及到的角色

(1)抽象角色:声明真实对象和代理对象的共同接口

(2)代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装

(3)真实角色:代理角色所代表的真实对象,是我们最终要引用的对象

【实例】

Subject  抽象类  抽象角色 定义一个抽象方法request

RealSubject  真实角色  继承了抽象类Subject 实现抽象方法request

ProxySubject  代理角色  同样继承抽象类Subject实现抽象方法request

Client  客户端 

代码语言:javascript
复制
//抽象角色
abstract public class Subject{
    abstract public void request();
}
代码语言:javascript
复制
//真实角色:实现了Subject的request()方法
public class RealSubject extends Subject{
    public RealSubject(){
    }
    public void request(){
        System.out.println("局长办事了!");
    }
}
代码语言:javascript
复制
//代理角色
public class ProxySubject extends Subject{
  private RealSubject realSubject; // 以真实角色作为代理角色的属性
  public ProxySubject(){
  }
 // 该方法封装了真实对象的request方法
  public void request(){
      preRequest();
      if (realSubject == null){
          realSubject = new RealSubject();
      }
      realSubject.request(); // 此处执行真实对象的request方法
      postRequest();
  }
  private void preRequest(){
     	  System.out.println("秘书去找局长");
  }
  private void postRequest(){
	  System.out.println("秘书回来了");
  }
}
代码语言:javascript
复制
//客户端调用
public class Client{
    public static void main(String[] args){
        Subject sub = new ProxySubject();
        sub.request();
    }
}

在客户端里,并没有直接去调用真实对象中的request方法,但是却实现了真实对象中的方法,是通过代理对象间接调用的,这里体现了代理模式的特点

1. 如果要按照上述的方法使用代理模式,那么真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。但是实际使用时,一个真实角色必须对应一个 代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色,该如何使用代理呢?这个问题可以通过Java的动态代理类来解决

2. 动态代理是指客户通过代理类来调用其它对象的方法

3. Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类: 

(1)Interface InvocationHandler:该接口中仅定义了一个方法 public object invoke(Object obj,Method method, Object[] args) 在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。 这个抽象方法在代理类中动态实现。 

(2)Proxy:该类即为动态代理类,作用类似于上例中的ProxySubject

1. 动态代理的步骤

(1).创建一个实现接口InvocationHandler的类,它必须实现invoke方法

(2).创建被代理的类以及接口

(3).通过Proxy的静态方法

newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个代理

(4).通过代理调用方法

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • <反射机制>
    • 【实例2】
      • 【实例3】
        • 该类的main()方法中,运用反射机制调用一个InvokeTester对象的add()和echo()方法。add()方法的两个参数为int 类型,获得表示add()方法的Method对象的代码如下:
          • Method addMethod=classType.getMethod("add",new Class[]{int.class,int.class});
            • Method类的invoke(Object obj,Object args[])方法接收的参数必须为对象,如果参数为基本类型数据,必须转换为相应的包装类型的对象。invoke()方法的返回值总是对象,如果实际被调用的方法的返回类型是基本类型数据,那么invoke()方法会把它转换为相应的包装类型的对象,再将其返回。体现了JAVA的反射机制功能中的“在运行时获得任意一个类的方法”、“在运行时调用任意一个对象的方法”
            • <代理模式>
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档