基础 | Java的反射与动态代理

基础 | Java的反射与动态代理

关于「反射」请参看Class类详解(反射)部分。

动态代理作为Java反射的主要应用之一,其在多种JavaEE框架中均有使用,如Spring框架中AOP的实现原理就是动态代理,面试中提到AOP也必定会问 「谈谈对动态代理的理解?」 相关问题,在此做一个梳理与总结。


谈谈对动态代理的理解?

动态代理作为设计模式中动态代理模式的一部分,其和静态代理构成鲜明对比。下面分别对代理模式、静态代理和动态代理技术点进行详细介绍与分析。


代理模式

代理模式的主要思想是创建一个代理对象来包装原始对象,任何对原始对象的调用必须通过代理对象,并由代理对象来决定是否以及何时将方法调用转到原始对象上。

说明:关于「设计模式」的详细内容,将放在后续文章进行学习和分享。


静态代理

静态代理的主要特征是代理类和被代理类都是在编译期间就确定下来的,一个代理类仅能为一个接口服务,不利于程序的扩展。

实现步骤:

  • 创建被代理类所实现的接口(包括抽象方法);
  • 创建被代理类,实现相应的接口并重写其抽象方法;
  • 创建代理类,实现被代理类所实现的接口,创建构造器并传入被代理类的对象,在重写接口的抽象方法中发起对被代理类的调用。

示例代码:

//1.创建代理类所实现的接口 
interface ClothFactory {
	void productCloth();
}

// 2.创建被代理类,并实现相应的接口
class NikeClothFactory implements ClothFactory {
    // 重写抽象方法
	public void productCloth() {
		System.out.println("Nike-Nike!");
	}
}

// 3.创建代理类,实现被代理类所实现的接口
class ProxyFactory implements ClothFactory {
	ClothFactory cf;
	// 创建构造器,传入的是被代理类的对象!
	public ProxyFactory(ClothFactory cf) {
		this.cf = cf;
	}
	// 重写抽象方法,发起对被代理对象方法的调用
	public void productCloth() {
		cf.productCloth();
	}
}

public class TestStaticProxy {
	public static void main(String[] args) {
		//1.创建被代理类对象
		NikeClothFactory ncf = new NikeClothFactory();
		//2.创建代理类对象
		ProxyFactory pf = new ProxyFactory(ncf);
		//3.执行代理类的方法,但实际发起的是对被代理类中指定方法的调用
		pf.productCloth();
	}
}

在代码上看,执行的是代理类的方法,但实际是通过代理类方法发起对被代理类方法的调用。


动态代理

动态代理主要是为了解决静态代理中一个代理类仅能服务一个接口的问题。

动态代理的主要特征是通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建被代理类的代理对象。

实现步骤:

  • 创建被代理类所实现的接口(包括抽象方法);
  • 创建被代理类,实现相应的接口并重写其抽象方法;
  • 创建一个实现InvocationHandler接口的代理类,重写其invoke()方法以完成代理的具体操作,同时创建blind()方法一方面完成对被代理类对象的实例化,另一方面通过Proxy的静态newProxyInstance()方法返回一个动态代理类的对象

示例代码:

// 1.被代理类所实现的接口
interface Subject {
	void action();
}

// 2.创建被代理类,实现接口并重写接口的抽象方法
class RealSubject implements Subject {
	@Override
	public void action() {
		System.out.println("被代理类-被代理");
	}
}

// 3.创建代理类,实现InvocationHandler接口
class MyInvocationHandler implements InvocationHandler {
	Object obj;
	// 3.1 重写invoke方法,完成代理的具体操作
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		//method为被代理类的目标方法
		Object returnVal = method.invoke(obj, args);//调用被代理类的action方法
		return returnVal;
	}
	// 3.2 实例化被代理对象,返回一个代理类对象
	public Object blind(Object obj) {
		this.obj = obj;
		// 返回一个代理类对象
		return
		Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
	}

}

public class TestProxy {
	public static void main(String[] args) {
		//1.创建被代理类的对象
		RealSubject real = new RealSubject();
		//2.创建代理类的对象
		MyInvocationHandler handler = new MyInvocationHandler();
		//3.调用blind()方法,动态返回一个同样实现被代理类所实现接口的代理类的对象
		Object obj = handler.blind(real);
		Subject sub = (Subject)obj;//此时的sub即为代理类的对象
		sub.action();
		
		NikeClothFactory ncf = new NikeClothFactory();
		obj = handler.blind(ncf);
		ClothFactory cf = (ClothFactory)obj; //此时的cf即为代理类的对象
		cf.productCloth();
	}
}


扩展面试题

问:你在项目中的哪些场景下使用过动态代理?

答:动态代理最主要的应用场景就是面向切面编程,主要在调用目标方法的前后插入一些通用处理,比如日志操作和事务处理等,示意图如下:

基础 | Java的反射与动态代理

扩展:动态代理之Proxy类

Proxy类是专门完成代理的操作类,其是所有动态代理类的父类,通过此类为一个或多个接口动态地生成实现类。其提供的主要静态方法如下:

// 获取动态代理类所对应的Class对象
static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)  

// 创建动态代理对象
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

推荐阅读


欢迎关注

Java名企面试吧,每天10点24分,我们不见不散!

丙子先生的宗旨是,每天以短篇幅讲高频面试题,不增加太多负担,但需要持之以恒。

能力有限,欢迎指教!

基础 | Java的反射与动态代理