代理模式是Java常见的设计模式之一。所谓代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象。
代理模式的UML图:
静态代理:
静态代理:定义一个接口,目标对象委托代理类与客户端进行交互,代理类中调用目前对象的方法,在客户端中定义一个接口对象,对象用代理类进行实例化,在new代理类的时候传入需要目前对象的类。
Subject接口:
package staticProxy;
/**
* 真实对象和代理对象的共同接口:定义一个接口或者抽象类
*
*/
public abstract class Subject
{
public abstract void request();
}
代理类ProxySubject:
package staticProxy;
/**
* 代理角色
*
*/
public class ProxySubject extends Subject{
//代理角色对象内部含有对真实对象的引用
private RealSubject realSubject;
@Override
public void request()
{
//在真实角色操作之前所附加的操作
preRequest();
if(null == realSubject)
{
realSubject = new RealSubject();
}
//真实角色所完成的事情
realSubject.request();
//在真实角色操作之后所附加的操作
postRequest();
}
private void preRequest(){
System.out.println("Pre Request.");
}
private void postRequest(){
System.out.println("Post Request");
}
}
真实角色RealSubject:
package staticProxy;
/**
* 真实角色
*
*/
public class RealSubject extends Subject
{
@Override
public void request(){
System.out.println("From Real Subject!");
}
}
client调用:
package staticProxy;
public class Client {
public static void main(String[] args) {
Subject subject = new ProxySubject();
subject.request();
}
}
结果:
Pre Request.
From Real Subject!
Post Request
动态代理:
实现基于接口代理与基于继承代理(两类实现的代表:JDK代理和Cglib代理),jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。
JDK动态代理:
实现要点:
- 类java.lang.reflect.Proxy
- 接口InvocationHandler
- 只能基于接口进行动态代理
package common;
public interface Subject {
void request();
}
JDK动态代理实现类:
package DynamicProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 动态代理类
* @author yyy
*
*/
public class JdkDynamicProxy implements InvocationHandler{
//被代理的目标对象
private Object target;
public JdkDynamicProxy(Object target) {
super();
this.target = target;
}
@SuppressWarnings("unchecked")
public <T> T getProxy() {
return (T)Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
before();
Object result = method.invoke(target, args);
after();
return result;
}
private void before() {
System.out.println("before.....");
}
private void after() {
System.out.println("after.....");
}
}
真实实现类RealSubject:
package common;
public class RealSubject implements Subject {
@Override
public void request() {
// TODO Auto-generated method stub
System.out.println("From Real Subject!");
}
}
client调用:
package DynamicProxy;
import common.RealSubject;
import common.Subject;
public class Client {
public static void main(String[] args) {
JdkDynamicProxy dynamicProxy = new JdkDynamicProxy(new RealSubject());
Subject subject = (Subject)dynamicProxy.getProxy();
//当接口改变的时候,只需修改接口和实现类,不需要修改代理类,但是要代理一个没有接口的类就歇菜了
subject.request();
}
}
结果:
before.....
From Real Subject!
after.....
cglib动态代理实现:
Cglib是一个优秀的动态代理框架,它的底层使用ASM在内存中动态的生成被代理类的子类,使用CGLIB即使代理类没有实现任何接口也可以实现动态代理功能。CGLIB具有简单易用,它的运行速度要远远快于JDK的Proxy动态代理;
CGLIB的核心类:
net.sf.cglib.proxy.Enhancer – 主要的增强类
net.sf.cglib.proxy.MethodInterceptor – 主要的方法拦截类,它是Callback接口的子接口,需要用户实现
net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用,如使用:
Object o = methodProxy.invokeSuper(proxy, args);//虽然第一个参数是被代理对象,也不会出现死循环的问题。
net.sf.cglib.proxy.MethodInterceptor接口是最通用的回调(callback)类型,它经常被基于代理的AOP用来实现拦截(intercept)方法的调用。这个接口只定义了一个方法
public Object intercept(Object object, java.lang.reflect.Method method,
Object[] args, MethodProxy proxy) throws Throwable;
第一个参数是代理对像,第二和第三个参数分别是拦截的方法和方法的参数。原来的方法可能通过使用java.lang.reflect.Method对象的一般反射调用,或者使用 net.sf.cglib.proxy.MethodProxy对象调用。net.sf.cglib.proxy.MethodProxy通常被首选使用,因为它更快。
代码实现例子:
在开始之前需要导入包asm-x.x.x.jar 和 cglib-nodep.x.x.x.jar
Subject接口和真实实现类RealSubject同上。
Cglib代理实现类:
package DynamicProxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import common.RealSubject;
public class CglibDynamicProxy implements MethodInterceptor{
public static Object getProxy(Class clz, MethodInterceptor interceptor) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clz);
enhancer.setCallback(interceptor);
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
// TODO Auto-generated method stub
before();
Object result = methodProxy.invokeSuper(obj, args);
after();
return result;
}
private void before() {
System.out.println("before.....");
}
private void after() {
System.out.println("after.....");
}
}
client接口调用:
package DynamicProxy;
import common.RealSubject;
import common.Subject;
import net.sf.cglib.proxy.Enhancer;
public class CglibClient {
public static void main(String[] args) {
CglibDynamicProxy cglibProxy = new CglibDynamicProxy();
// Enhancer enhancer = new Enhancer();
// enhancer.setSuperclass(RealSubject.class);
// enhancer.setCallback(cglibProxy);
// Subject subject = (Subject)enhancer.create();
Subject subject = (Subject) CglibDynamicProxy.getProxy(RealSubject.class, cglibProxy);
subject.request();
}
}
结果:
package DynamicProxy;
import common.RealSubject;
import common.Subject;
public class Client {
public static void main(String[] args) {
JdkDynamicProxy dynamicProxy = new JdkDynamicProxy(new RealSubject());
Subject subject = (Subject)dynamicProxy.getProxy();
//当接口改变的时候,只需修改接口和实现类,不需要修改代理类,但是要代理一个没有接口的类就歇菜了
subject.request();
}
}
JDK动态代理和Cglib动态代理的对比:
JDK只能针对有接口的类的接口方法进行动态代理。
Cglib基于继承来实现代理,无法对static、final类进行代理。
Cglib基于继承来实现代理,无法对private、static方法进行代理。
例子代码路径:设计模式