静态代理与动态代理(含主要源码分析)

为什么要用代理?
假设让你在项目的所有类的方法中加入打印日志这段代码,如何在不修改已有代码的前提下,完成这个需求呢?这就需要用到代理了。

静态代理:

例如,如何在不修改代码前提下,在类hk中的print()方法中加入打印日志

public class hk {
	public void print(){
		 System.out.println("打印!");
	}
}

首先应实现一个接口

public interface abc {	
	void print();
}
public class hk implements abc{
	public void print(){
		 System.out.println("打印!");
	}
}

然后需要一个代理对象

public class ProxyHandler implements abc{

	abc ABC;
	public ProxyHandler() {
		ABC=new hk();
	}
	
	 public void print(){
	       System.out.println("日志");
	       this.ABC.print();
	    }
}

执行下列代码后就能完成需求了

 ProxyHandler proxy = new ProxyHandler();
 proxy.print();

静态代理缺点:需要我们去写代理对象的代码,当类的方法很多,或者有许多的类的时候,工作量非常大。

---------------------------------------------------------------

动态代理:动态代理不需要我们去写代理对象的代码,那如何生成代理对象呢,这就得了解类的生命周期。

在这里插入图片描述
之前我们是如何创建一个对象的呢?
例如Person person= new Person()
实际上创建对象person经历了编译,类加载,实例化的过程。

现在我们没有Java源文件了,如何创建对象呢?带着这个问题我们可以先看看动态代理的实现,再深入源码,查找答案。

public class hk implements abc{
	public void print(){
		 System.out.println("打印!");
	}
}
public interface abc {
	void print();
}
public class ProxyHandler implements InvocationHandler {

	private Object targetObject;//被代理的对象
	 
	    public  Object newProxyInstance(Object targetObject){
	        this.targetObject = targetObject;
	        
	        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this); //获得代理对象
	    }
	    //该方法在代理对象调用方法时调用
	    @Override
	    public Object invoke(Object proxy,Method method,Object[] args) throws Throwable {
	        System.out.println("记录日志");
	        return method.invoke(targetObject,args);
	    }
}

执行以下代码就完成了需求

public class Text {

	public static void main(String[] args){
		   ProxyHandler proxyHandler=new ProxyHandler();
		 abc ABC =  (abc) proxyHandler.newProxyInstance(new hk());
		  ABC.print();
		 }	
}

---------------------------------------------------------------
接下来深入源码看看Proxy.newProxyInstance是如何获取代理对象的(以下的是主要的代码,并不是完整的源码)。

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException
{
    Class<?> cl = getProxyClass0(loader, interfaces);    //获得Class对象,这行代码做了两件事,一:生成字节码文件。二:将字节码文件类加载,生成Class对象
     final Constructor<?> cons = cl.getConstructor(constructorParams);  //从Class对象中拿到构造函数
       return cons.newInstance(new Object[]{h});  //通过反射拿到实例对象
}
private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces)
 {
    return proxyClassCache.get(loader, interfaces);
}
public V get(K key, P parameter) 
{

  Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));  
}

public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

// 所有代理类对象的前缀
private static final String proxyClassNamePrefix = "$Proxy";

 // 代理类的包名
  String proxyPkg = null;    

//生成代理类的类名
     long num = nextUniqueNumber.getAndIncrement();  //计数器
 String proxyName = proxyPkg + proxyClassNamePrefix + num;  //所以生成的代理类的名称应该是$Proxy0,$Proxy1,$Proxy2......这种规则

 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);  //生成字节码数组,相当于字节码文件        
            
                 return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);  //返回Class对象
 }
private Static native Class defineClass0(ClassLoader loader,String name,byte[] b,int off,int len); //本地方法,调用操作系统的方法,生成Class对象

---------------------------------------------------------------
通过分析可以知道下列三段代码实现了 生成字节码文件,类加载生成Class文件,实例化代理对象。

这就是如何在不写代码也就是没有Java源文件如何创建对象的答案。

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);  //生成字节码数组,相当于字节码文件        
 return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);  //返回Class对象
cons.newInstance(new Object[]{h});  //通过反射拿到实例对象

创建出来的字节码反编译后主要代码如下:

public final class $Proxy0 extends Proxy implements abc{

private static Method m1;
private static Method m2;
private static Method m3;
private static Method m4;

public final void print(String paramString)
{
this.h.invoke(this, m4, new Object[] { paramString });
}

这就是代理对象的代码了,它是继承Proxy并且实现abc接口的一个类。

---------------------------------------------------------------
动态代理大概过程:
所以无论执行什么方法,都会执行invoke方法,从而达到增强代码的目的。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值