设计模式——代理模式

代理模式是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
  • 只能基于接口进行动态代理
代码实现例子:

Subject接口:
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方法进行代理。


例子代码路径:设计模式



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值