Java反射与动态代理

        在学习反射之前必须要了解的一个概念就是:Class

Class对象基本概念

         Java中的类用来表示具有相同属性的方法的对象的集合,是抽象的概念。对象是类创建的,同一个类的不同对象具有不同的属性值。Java中定义的所有类都属于同一类事物,可以用Class来表示。Class类的对象就是不同的类对应的字节码。

获取Clas对象由三种方法:

1、通过类名直接访问Class:

Class stringCls = String.class;

2、通过实例访问getClass:

String s = "";
Class stringCls2 = s.getClass();

3、通过Class类的静态方法forName(类名)

Class stringCls3 = Class.forName("java.lang.String");

        class (包括 interface )的本质是数据类型( Type )。 class 是由 JVM 在执行过 程中动态加载的。 JVM 在第一次读取到一种 class 类型时,将其加载进内存。每加载一种 c lass , JVM 就为其创建一个 Class 类型的实例,并关联起来。

反射

        反射就是把Java类中的各个组成部分映射成相应的Java类;

        一个类的组成部分包括:属性,方法,构造方法,包等。这些组成部分都会被映射成相应的类;Class类定义了一系列方法来获取java类的属性、方法、构造方法,包等信息,这些信息都有来自相应的类来表示,分别是Field、Method、Constructor、Package等。

注意:要进行反射操作必须要拿到这个类对应的Class对象

反射的意义:java运行环境的缓存中保存了类的无参构造方法所对应的Construtor对象。

反射的特征:

(1)反射机制极大的提高了程序的灵活性和扩展性,降低模块的耦合性,提高自身的适应能力。

(2)通过反射机制可以让程序创建和控制任何类的对象,无需提前硬编码目标类。

(3)使用反射机制能够在运行时构造一个类的对象、判断一个类所具有的成员变量和方法、调用一个对象的方法。

(4)反射机制是构建框架技术的基础所在,使用反射可以避免将代码写死在框架中。正是反射有以上的特征,所以它能动态编译和创建对象,极大的激发了编程语言的灵活性,强化了多态的特性,进一步提升了面向对象编程的抽象能力。

通过调用构造方法

可以调用Class提供的newInstance方法

Object obj1 = clazz.newInstance();

        但是,调用 Class.newInstance() 的局限是,它只能调用该类的 public 无参数构造方法。如果构造方法带有参数,或者不是 public ,就无法直接通过 Class.newInstance() 来调用。所以为了调用任意的构造方法, Java 的反射 API 提供了 Constructor 对象,它包含一个构造方法的所有信息,可以创建一个实例。 Constructor 对象是一个构造方法,调用结果总是返回实例。

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Test04 {
	public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException,
			IllegalAccessException, IllegalArgumentException, InvocationTargetException {

		Class clazz = Example.class;

		// 获取该class对象的所有构造方法
		Constructor[] cons = clazz.getConstructors();
		for (Constructor c : cons) {
			System.out.println(c);
		}
		System.out.println("------------------------");

		// 调用有参的构造方法 传入参数的class对象
		Constructor con = clazz.getConstructor(int.class, double.class);

		Example e = (Example) con.newInstance(120, 12.23);

		System.out.println("---------------------------");

		Constructor[] conss = clazz.getDeclaredConstructors();
		for (Constructor c : conss) {
			System.out.println(c);
		}
		Constructor conn = clazz.getDeclaredConstructor(String.class);
		conn.setAccessible(true);
		Example ee = (Example) conn.newInstance("ddc");

	}
}

class Example {

	public Example() {

	}

	private Example(String s) {
		System.out.println(s);
	}

	protected Example(boolean is) {
		System.out.println(is);
	}

	public Example(int a) {
		System.out.println("a=" + a);
	}

	public Example(int a, double b) {
		System.out.println("a = " + a + " b = " + b);
	}

}

(1)getConstructors():获取该class对象的所有public的构造方法;

(2)getDeclaredConstructors():获取该class对象定义的所有构造方法;

(3)getConstructor(Class class...):获取某个public的构造方法;

(4)getDeclaredConstructor(Class class):获取某个定义的构造方法。

查看class对象实现的接口以及父类

// 实现的接口
Class[] clss = MyCompareable.class.getInterfaces();
for (Class c : clss) {
	System.out.println(c.getName());
}

// 父类
System.out.println(MyCompareable.class.getSuperclass().getName());

(1)getInterfaces():获取该class对象是实现的所有接口;

(2)getSuperclass():获取该class对象的父类;

注意:Object类是所有类的父类,所以如果对Object调用getSuperclass()会返回null

补充:instanceof 与 isAssignableFrom()的区别

import java.io.Serializable;

public class Test01 {
	public static void main(String[] args) {

		// instanceof 运算符 : 判断“引用”和 “类型”之间的关系
		// 可以指向该类实现的接口以及父类
		Object obj = Integer.valueOf(123);

		System.out.println((obj instanceof String));
		System.out.println((obj instanceof Integer));
		System.out.println((obj instanceof Double));
		System.out.println((obj instanceof Number));
		System.out.println((obj instanceof Comparable));
		System.out.println((obj instanceof Serializable));
		System.out.println("--------------------------------");

		// isAssignableFrom() 方法 : 判断类型与类型之间的关系

		System.out.println("Integer <= Integer:" + Integer.class.isAssignableFrom(Integer.class));
		System.out.println("Integer <= Number:" + Integer.class.isAssignableFrom(Number.class));
		System.out.println("Number <= Integer:" + Number.class.isAssignableFrom(Integer.class));
		System.out.println("Comparable <= Integer:" + Comparable.class.isAssignableFrom(Integer.class));
	}
}

        通过上述代码可以发现:instanceof 运算符是判断“引用”和 “类型”之间的关系,并且可以指向该类实现的接口以及父类,而isAssignableFrom() 方法是用来判断类型与类型之间的关系。

访问字段Field

Field类用来表示类中的属性(字段)。

常用方法:

(1)getFields():包含父类的public字段;

(2)getDeclaredFields():得到该对象的定义的所有字段名称(只包含自己的);

(3)getDeclaredField("bookName"):通过指定字段名称获取字段。

(4)getField("bookName"):包含父类的public字段某个字段;

设置字段值

import java.lang.reflect.Field;

public class Test03 {
	public static void main(String[] args)
			throws InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException {
		// 运行期
		// 通过反射的方式 完成成员变量保存值
		Class clazz = Book.class;
		Object obj = clazz.newInstance();
		// 通过指定字段名称获取字段
		Field field1 = clazz.getDeclaredField("bookName");
		// 参数1:目标对象
		// 参数2:存入成员变量的值
		field1.set(obj, "ddc");
		System.out.println(obj);
	}
}

获取字段值


public class Test04 {
	public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
		Book book = new Book();
		book.setBookName("ddc");
		book.setNumber(123);
		book.setAge("12345");
		book.setPrice(123.456);
		printInfo(book);
	}

	public static void printInfo(Object obj) throws IllegalArgumentException, IllegalAccessException {
		Class clazz = obj.getClass();
		Field[] fs = clazz.getDeclaredFields();
		for (Field f : fs) {
			System.out.println("字段名称:" + f.getName());
			// 判断该字段是否可以访问
			if (!f.isAccessible()) {
				f.setAccessible(true); // 设置私有成员变量允许访问
			}
			// 获取字段值
			System.out.println("字段值:" + f.get(obj));
			System.out.println();
		}
	}
}

调用方法

Method用来表示类中的方法。通过Class对象的如下方法得到Method对象:

(1)Method getMethod(String name,Class<?>…parameterTypes)按名称得到某个特定的public方法(包括从父类或者接口继承的方法);

(2)Method[] getMethods():得到public方法(包括从父类或者接口继承的方法);

(3)Method[] getDeclaredMethods():得到所有的方法(不包括继承的方法);

(4)Method getDeclaredMethod(String name,Class<?>…parame-terTypes):按名称得到某个特定的方法(不包括继承)。

得到某个方法对应的Method对象后,需要调用如下方法来在某个对象上执行该方法:

(1)invoke(Object obj,Object…obj)方法用来调用Method所表示的方法。其中,第一个参数表示此方法作用于哪一个对象。

(2)如果调用的时静态方法,那么invoke()方法中的第一个参数用null表示。

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;

public class Test05 {
	public static void main(String[] args) {

		Class clazz = Book.class;
		// 包含父类的public方法
//		Method[] methods = clazz.getMethods();
		// 不包含父类的所有方法
		Method[] methods = clazz.getDeclaredMethods();
		for (Method m : methods) {
			System.out.println("访问修饰符:" + Modifier.toString(m.getModifiers()));
			System.out.println("方法名称:" + m.getName());
			System.out.println("方法返回值类型:" + m.getReturnType());
			// 获得所有的参数对象
			Parameter[] ps = m.getParameters();
			for (Parameter p : ps) {
				System.out.println("参数名称:" + p.getName());
				System.out.println("参数类型:" + p.getType());
				System.out.println();
			}
			System.out.println();
		}
	}
}

调用方法使用invoke函数

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test06 {
	public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException,
			IllegalAccessException, IllegalArgumentException, InvocationTargetException {

		Class clazz = Base.class;
		Object obj = clazz.newInstance();

		// 调用无参数的create
//		Method method = clazz.getMethod("create");

		// 调用有参的create
		Method method = clazz.getMethod("create", int.class);

		// 以反射的方式执行方法
		int r = (int) method.invoke(obj, 1000);
		System.out.println(r);

	}
}

class Base {

	public int create() {
		return create(100);
	}

	public int create(int x) {
		return (int) (Math.random() * x);
	}
}

调用静态方法

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test07 {
	public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException,
			IllegalArgumentException, InvocationTargetException {
		// 调用静态方法
		Class clazz = Math.class;
		Method method = clazz.getMethod("log10", double.class);
		int n = Double.valueOf((double) method.invoke(null, 12312312)).intValue() + 1;
		System.out.println(n);
	}
}

调用静态方法的时候需要将invoke函数的第一个参数写为null,因为静态方法属于类

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test08 {
	public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException,
			IllegalArgumentException, InvocationTargetException {
		// 反射时候多态依然存在
		Method method = Person.class.getMethod("hello");
		method.invoke(new Student());
	}
}

class Person {

	public void hello() {
		System.out.println("Person.hello");
	}
}

class Student extends Person {
	public void hello() {
		System.out.println("Student.hello");
	}
}

在使用反射调用方法的时候,我们看可以看到多态依然存在

动态代理

        JDK动态代理主要涉及两个类: java.lang.reflect.Proxy 和 java.lang.reflect.InvocationHandler 。我们通过编写一个调用逻辑处理器 LogHandler 类案例来提供日志增强功能,并实现 InvocationHandler接口;在 LogHandler 中维护一个目标对象,这个对象是被代理的对象 (真实主题角色);在 invoke() 方法中编写方法调用的逻辑处理。

首先定义接口类 和接口类的具体实现类

public interface UserService {
	void select();

	void update();

	int ddc(int a);
}
//真正的实现类
public class UserServiceImpl implements UserService {

	@Override
	public void select() {
		System.out.println("select * ..................");
		System.out.println("数据库中完成用户信息的查询执行!");
	}

	@Override
	public void update() {
		System.out.println("update ...................");
		System.out.println("数据库中用户状态的更新执行!");
	}

	@Override
	public int ddc(int a) {
		int b = a * 10;
		return b;
	}
}

        然后定义LogInvocationHandlerImpl为InvocationHandler接口实现类:代理类中扩展逻辑抽取封装。

实现给接口需要覆写invoke方法 里面有三个参数分别为:

1、proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0;

2、method:我们所要调用某个对象真实的方法的Method对象;

3、args:指代代理对象方法传递的参数。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;

// InvocationHandler接口实现类:代理类中扩展逻辑抽取封装
public class LogInvocationHandlerImpl implements InvocationHandler {

	private Object obj;

	public LogInvocationHandlerImpl(Object obj) {
		this.obj = obj;
	}

//    proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0
//    method:我们所要调用某个对象真实的方法的Method对象
//    args:指代代理对象方法传递的参数

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("方法" + method.getName() + "开始执行了");
		// 执行目标对象的目标方法
		Object returnValue = method.invoke(obj, args);
		System.out.println(Arrays.toString(args));
		System.out.println("方法" + method.getName() + "执行结束");
		return returnValue;
	}
}

其中定义了我们需要扩展的方法,在没有直接改变具体实现类代码的情况下

下面定义客户端类

import java.lang.reflect.Proxy;

// 动态代理
public class Client {
	public static void main(String[] args) {

		// 创建目标对象
		LogInvocationHandlerImpl handler1 = new LogInvocationHandlerImpl(new UserServiceImpl());

		// 创建UserService接口的动态代理对象
		// 一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载
		// 一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,如果我们提供了这样一
		// 个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。
		// 一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用。
		UserService proxy = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(),
				new Class[] { UserService.class }, handler1);

		// 通过代理对象调用方法
//		proxy.select();
//		System.out.println(proxy.getClass());
		System.out.println(proxy.ddc(10));

		OrderServiceImpl order = new OrderServiceImpl();
		LogInvocationHandlerImpl handler2 = new LogInvocationHandlerImpl(order);

		OrderService proxy2 = (OrderService) Proxy.newProxyInstance(order.getClass().getClassLoader(),
				order.getClass().getInterfaces(), handler2);

//		proxy2.createOrder();
//		System.out.println(proxy2.getClass());
	}
}

        主要实现的是Proxy.newProxyInstance方法,其中有三个参数分别为:

1、一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载;

2、一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,如果我们提供了这样一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法;

3、一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用。

由此可见,动态代理相比较于静态代理具有较大的优势

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农叮叮车

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值