动态代理双剑客--JDK Proxy与CGLIB

背景:


研究过设计模式的同胞们都知道代理模式可以有两种实现方案:


1.接口实现(或继承抽象类)

核心代码片段

ProxySubject-->>doOperation()

//dosomething before
realSubject.doOperation()
//dosomething after

2.继承父类



核心代码片段

ProxySubject-->>doOperation()

//dosomething before
super.doOperation()
//dosomething after

总结:

相同点

都可以通过Proxy控制对Target的访问


不同点


可行性

如果Target实现了接口,那么这两种方式都可以;

如果没有实现任何接口,那只能采取“继承父类”的方式了




正文


Java中动态代理对应着也有两种实现方式


1.“接口实现"---JDK Proxy

用到JDK提供的InvocationHandler接口和Proxy


类之间的关系如下


InvocationHandler接口

用于处理方法请求

Proxy

用于生成代理对象


代码演示

ISubject接口

public interface ISubject {
	
	public void showName(String name);
}

RealSubject

public class RealSubject implements ISubject {

	@Override
	public void showName(String name) {
		System.out.println(name+"闪亮登场");
	}

}

LogHandler

为了更明确的说明动态代理的工作原理,将代理的创建过程放到了LogHandler的外部,即main方法中

public class LogHandler implements InvocationHandler {

	Object target=null;
	
	public Object getTarget() {
		return target;
	}

	public void setTarget(Object target) {
		this.target = target;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		Object result=null;
		//调用目标对象方法前的逻辑
		System.out.println("下面有一个大人物要出现");
		//调用目标对象的方法,这句代码将代理与目标类联系了起来
		method.invoke(target, args);
		//调用目标对象方法后的逻辑
		System.out.println("大家鼓掌欢迎");
		return result;
				
	}

}



客户端 类Client

public class Client {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		LogHandler logHandler=new LogHandler();
		logHandler.setTarget(new RealSubject());
		//创建代理对象
		ISubject proxySubject=(ISubject)Proxy.newProxyInstance(RealSubject.class.getClassLoader(), RealSubject.class.getInterfaces(), logHandler);
		System.out.println("-------JDK Proxy-------------");
		proxySubject.showName("委座");

	}

}

执行结果



调用过程时序图






2.“继承父类”---CGLIB

用到了CBLIB提供的Enhancer类和MethodInterceptor接口 


类之间的关系如下


需要引入第三方jar包

  • cglib-2.2.jar
  • asm-3.1.jar

Enhancer

用于创建代理对象

MethodInterceptor接口

用于处理方法请求

 

代码演示

RealSubject类同上,但是个pojo类

public class RealSubject {

	public void showName(String name) {
		System.out.println(name+"闪亮登场");
	}

}


 

LogIntercept类

public class LogIntercept implements MethodInterceptor {
	Object target=null;
	
	public Object getTarget() {
		return target;
	}

	public void setTarget(Object target) {
		this.target = target;
	}
	
	@Override
	public Object intercept(Object arg0, Method arg1, Object[] arg2,
			MethodProxy arg3) throws Throwable {
		
		Object result=null;
		//调用目标对象方法前的逻辑
		System.out.println("下面有一个大人物要出现");
		//调用目标对象的方法,这句代码将代理与目标类联系了起来
		arg3.invoke(target, arg2);
		//调用目标对象方法后的逻辑
		System.out.println("大家鼓掌欢迎");
		return result;
	}

}



客户端类Client

public class Client {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		LogIntercept logIntercept=new LogIntercept();
		logIntercept.setTarget(new RealSubject());
		Enhancer enhancer=new Enhancer();
		enhancer.setSuperclass(RealSubject.class);
		enhancer.setCallback(logIntercept);
		
		<pre name="code" class="java">                RealSubject proxySubject=(RealSubject )enhancer.create();
		System.out.println("-------CBLIB-------------");
		proxySubject.showName("委座");

	}

}
 
 


调用过程时序图



总结

大家可以看到JDK ProxyCGLIB这两种动态代理的实现过程是非常相似的,但也有区别

相同点:

  • 都用到了一个接口一个类;
  • 接口用于处理方法调用,类用于创建代理对象

JDK Proxy

InvocationHandler接口

Proxy

CGLIB

MethodIntercept接口

Enhancer

不同点:

JDK Proxy

使用目标类的接口创建动态代理

CBLIB

使用目标类的子类创建动态代理

 

最后

          JDK ProxyCGLIB两种动态代理各有千秋,具体用哪个方案要看具体情况。如果目标类实现了对应接口,两种方案都可以;如果没有实现任何接口则要使用CBLIB比如Hibernate中的实体类是POJO类,没有实现任何接口,那么要通过代理实现延迟加载就只能采用CGLIB方案了。





猜你喜欢

转载自blog.csdn.net/wwwwenhuan/article/details/11266659