Java代理模式:静态、动态、CGLIB,三连暴击!💥

417 阅读3分钟

一、静态代理

这种代理需要代理类和目标类实现同一接口,让我们一起来写一个 demo 吧

先创建一个接口IUser:

public interface IUser {
	public void sayName();
}

实现类User:

public class User implements IUser{
	
	@Override
		public void sayName() {
		System.out.println("tntaxin");
		}
}
创建代理类,同样实现IUser接口,我们扩展下sayName()方法
public class UserProxy implements IUser{
	private IUser target;
		public UserProxy(IUser obj){
		this.target = obj;
		}

	@Override
		public void sayName() {
		System.out.println("我是它的代理");
			this.target.sayName();
		}
}

最后创建一个测试代理效果的类:

public class App
{
	public static void main( String[] args )
	{
		IUser user = new User();
			// 创建静态代理
			IUser userProxy = new UserProxy(user);
			userProxy.sayName();
	}
}

运行效果如下 (贴张图

动态代理

上面提到的静态代理很好理解,就是在目标对象外又包装了一层,那什么又是动态代理呢?它俩的区别如下:

静态代理在编译时就已经实现,编译完成后代理类是一个实际的class文件 动态代理是在运行时动态生成的,即编译完成后没有实际的class文件,而是在运行时动态生成类字节码,并加载到JVM中 动态代理不需要事先目标对象的接口,但是要求目标对象必须是要事先接口的,没有接口的对象是不能使用动态代理的

接下来我们还是通过编写demo的方式来学习吧

接口以及实现类还是上面用到的IUser以及User

除了上面两个类之外,我们再创建一个动态代理类:

public class ProxyFactory {  
    private Object target;  
 		public ProxyFactory(Object target){  
        this.target = target;  
 }  
  
    public Object getProxyInstance(){  
        return Proxy.newProxyInstance(this.target.getClass().getClassLoader(), 			this.target.getClass().getInterfaces(),  
 				new InvocationHandler() {  
                    @Override  
 					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
                        System.out.println("我是他的代理");  
 						method.invoke(target, args);  
 					return null; }  
                });  
	 }  
}

可以看到,这个类实际上是一个工厂类,用于生产代理对象的

让我们把目光聚集到getProxyInstance方法,它使用到了Proxy.newProxyInstance方法

static Object    newProxyInstance(ClassLoader loader,  //指定当前目标对象使用类加载器
 Class<?>[] interfaces,    //目标对象实现的接口的类型
 InvocationHandler h      //事件处理器
) 
//返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。

(引用)现在知道为啥使用动态代理的类必须实现接口了吧,因为这个方法中的第二个参数需要

从名字可以看出来,这个方法才是真正生成代理的方法,其中第三个参数是事件处理器InvocationHandler,实现该类必须要实现invoke方法

 Object invoke(Object proxy, Method method, Object[] args) 
// 在代理实例上处理方法调用并返回结果。

在这个方法中我们一般会通过反射的方式调用目标对象的对应方法,我想,大家也能够从这个方法的参数中看出一点端倪 (链接)java反射机制(同时引用) 同样的,我们最后创建一个测试程序来测试下动态代理的效果:

public class App   
{  
    public static void main( String[] args )  
    {  
        IUser user = new User();  
  
//         创建动态代理  
 		ProxyFactory proxyFactory = new ProxyFactory(user);  
 		IUser userProxy2 = (IUser)proxyFactory.getProxyInstance();  
		// 打印生成的代理对象的类名  
		System.out.println(userProxy2.getClass());
 		userProxy2.sayName();  
	}  
}

运行上面上面的类后,会得出: class com.sun.proxy.$Proxy0 我是它的代理 tntaxin

这里,我们只是打印了一下动态生成的代理类的类名,其实我们还可以进一步用一些字节码操作工具把这个代理类的源码给输出到文件中,当然我刚刚还搜到更方便的一种方法,在代码中将sun.misc.ProxyGenerator.saveGeneratedFiles属性值设为true,就会在项目根目录下生成代理类的文件,改动后的代码如下:

public class App   
{  
    public static void main( String[] args )  
    {  
        IUser user = new User();  
  
 		//生成$Proxy0的class文件 
		System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");  
 		// 创建动态代理  
 		ProxyFactory proxyFactory = new ProxyFactory(user);  
		IUser userProxy2 = (IUser)proxyFactory.getProxyInstance();  
 		// 打印生成的代理对象的类名  
 		System.out.println(userProxy2.getClass());  
 		userProxy2.sayName();  
 	}  
}

运行上述代码,生成的$Proxy0.class文件内容如下:

其中super.h其实就是我们在newProxyInstance方法中传入的invocationhandler了,看了这个代码,是不是把动态代理掌握的透透儿的了?

cglib代理

cglib (Code Generation Library )是一个第三方代码生成类库,运行时在内存中动态生成一个子类对象从而实现对目标对象功能的扩展。

cglib与动态代理最大的区别就是

使用动态代理的对象必须实现一个或多个接口
使用cglib代理的对象则无需实现接口,达到代理类无侵入。

同样的,我们来创建demo,先引入cglib包

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.5</version>
</dependency>

然后我们使用cglib来创建一个代理工厂类:

public class ProxyFactory implements MethodInterceptor {  
  
     private Object target;  
  
	 public ProxyFactory(Object target){  
			this.target = target;  
	 }  
  
		//为目标对象生成代理对象  
	 public Object getProxyInstance() {  
			//工具类  
			Enhancer en = new Enhancer();  
			//设置父类  
			 en.setSuperclass(target.getClass());  
			 //设置回调函数  
			en.setCallback(this);  
			 //创建子类对象代理  
			return en.create();  
	 }  

  
		@Override  
	 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {  
			System.out.println("我是cglib生成的代理");  
			method.invoke(target, objects);  
			return null; }  
	}

还是很简单的吧,然后创建一个测试类:

public class App   
{  
    public static void main( String[] args )  
    {  
        IUser user = new User();  
  		// cglib创建代理类  
		IUser userProxy3 = (IUser)new ProxyFactory2(user).getProxyInstance();  
		System.out.println(userProxy3.getClass());  
		userProxy3.sayName();
 		
 	}  
}

运行后会得到如下输出:

class org.example.UserEnhancerByCGLIBEnhancerByCGLIB8458a7f7 我是cglib生成的代理 tntaxin

ref: segmentfault.com/a/119000001…