代理模式(Proxy Pattern) - JDK动态代理 以及其原理

动态代理

JDK动态代理

JDK动态代理是使用Java提供的一个动态代理类Proxy,根据其提供的静态方法newProxyInstance,来动态的在内存中构建代理对象。

注意目标类需实现接口

应用实例:

  • 定义一个老师的接口:ITeacher
  • 目标对象(被代理对象) LiTeacher 实现接口 ITeacher
  • 代理类ProxyFactory,聚合一个Object类型的属性(通过构造器注入目标对象),使用Proxy.newInstance()生成代理对象
  • 通过返回的代理对象来调用目标对象的方法

代码示例:

// 目标类所实现的接口
public interface ITeacher {
	void teach();
}

// 目标类
// 目标类
public class LiTeacher implements ITeacher{

	@Override
	public void teach() {
		System.out.println("正在上课中...");
	}
}

// 生成代理对象的工厂
public class ProxyFactory {

	private Object target;
	
	public ProxyFactory(Object target) {
		this.target = target;
	}
	
    // 获取代理对象的方法
	public Object getProxyInstance() {
		/*
		 	该方法三个参数:
		 		ClassLoader loader:类加载器,用于加载代理类。可以通过目标对象获取
		 		Class<?>[] interfaces:目标对象所属类实现的接口的字节码对象[]
		 		InvocationHandler h:代理对象的调用处理程序
		 */
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
				target.getClass().getInterfaces(), 
				new InvocationHandler() {

					/*
					  	Object invoke: 代理对象
					  	Method method: 对接口方法进行封装的method对象
					  	Object[] args: 调用方法的实际参数
					 */
					@Override
					public Object invoke(Object proxy, Method method,
							Object[] args) throws Throwable {
						System.out.println("增强操作1...");
						// 反射机制调用目标方法
						Object returnVal = method.invoke(target, args);
						System.out.println("增强操作2...");
						return returnVal;
					}
			});
	}
}

// 测试类
public class Main {
	public static void main(String[] args) {
		
		// 创建目标对象
		LiTeacher target = new LiTeacher();
		
		// 创建代理工厂对象
		ProxyFactory factory = new ProxyFactory(target);
		// 获取代理对象
		ITeacher proxyObject = (ITeacher)factory.getProxyInstance();
		
		proxyObject.teach();//com.sun.proxy.$Proxy0
	}
}

// 打印结果:
    增强操作1...
    正在上课中...
    增强操作2...
    

JDK动态代理原理:

调用Proxy.newProxyInstance()方法后,会在内存中生成代理类com.sun.proxy.$Proxy0,该代理类会持有一个InvocationHandler接口的实例类的引用。通过构造器传递给父类Proxy

从下方对$Proxy0反编译的代码中可以看出,在我们使用代理类调用方法proxyObject.teach()时,会自动调用当前代理对象父类Proxy的引用属性protected InvocationHandler h; .invoke()方法。

并将当前代理对象this,当前 被调用方法的** 方法对象**m3,当前方法参数封装成Object数组(Object[])null,一并传递。
该引用对象属性protected InvocationHandler h;即为我们自己所编写的new InvocationHandler(){...};匿名内部类对象。

进入匿名内部类后,通过Object returnVal = method.invoke(target, args);反射原理,调用目标对象的方法。也会先后执行我们的增强操作

简单来说: 调用代理对象方法proxyObject.teach() -> 会自动调用匿名内部类InvocationHandlerinvoke方法并将当前方法信息传递 -> 执行目标对象的方法 -> 前后也可执行我们的增强方法

进行反编译后,代理类$Proxy0结构:

public final class class extends Proxy
    implements Iteacer {
    private static Method m1;
    private static Method m8;
    private static Method m2;
    private static Method m3;
    private static Method m6;
    private static Method m5;
    private static Method m7;
    private static Method m9;
    private static Method m0;
    private static Method m4;
    
     public class(InvocationHandler invocationhandler) //通过构造将handler传入
    {
        super(invocationhandler);
    }
    ...
    public final void teach() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    ...
}

使用IO的方式将内存中代理类写入到文件中:

/**
	 * 使用IO的方式将内存中代理类写入到文件中,然后反编译出来进行观察
	 */
	private static void createProxyClassFile() {
		byte[] data = ProxyGenerator.generateProxyClass("$Proxy0.class", new Class[] {LiTeacher.class});

		try {
			FileOutputStream out = new FileOutputStream("$Proxy0.class");
			out.write(data);
			out.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

Cglib动态代理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值