代理模式(Proxy)
定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
优点:(1)代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
(2)代理对象可以扩展目标对象的功能;
(3)代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;
缺点:(1)在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
(2)增加了系统的复杂度;
按照代理创建的时期来进行分类的话,可以分为两种:静态代理、动态代理。
静态代理是由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.class文件就已经被创建了。动态代理是在程序运行时通过反射机制动态创建的。
1 静态代理
(1)主要角色
抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
package proxy;
public class ProxyTest
{
public static void main(String[] args)
{
Proxy proxy=new Proxy();
proxy.Request();
}
}
//抽象主题
interface Subject
{
void Request();
}
//真实主题
class RealSubject implements Subject
{
public void Request()
{
System.out.println("访问真实主题方法...");
}
}
//代理
class Proxy implements Subject
{
private RealSubject realSubject;
public void Request()
{
if (realSubject==null)
{
realSubject=new RealSubject();
}
beforeRequest();
realSubject.Request();
afterRequest();
}
public void beforeRequest()
{
System.out.println("访问真实主题之前的预处理。");
}
public void afterRequest()
{
System.out.println("访问真实主题之后的后续处理。");
}
}
2 动态代理
动态代理技术目前实现方式主要分为Java自己提供的JDK动态代理技术和CGLIB技术。Java自带的JDK动态代理技术是需要接口的,而CGLIB则是直接修改字节码。
(1)JDK实现动态代理
使用JDK代理的基本前提是目标对象必须要实现接口。在动态代理中我们不再需要再手动的创建代理类,只需要编写一个动态处理器就可以了。真正的代理对象由JDK再运行时为我们动态的来创建。需要java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口的支持;
实现代码:
package proxy;
public class ProxyTest
{
public static void main(String[] args)
{
// 动态代理类和被代理类必须继承同一个接口。
// 动态代理只能对接口中声明的方法进行代理。
// 每一个动态代理实例都有一个关联的InvocationHandler。
// 通过代理实例调用方法,方法调用请求会被转发给InvocationHandler的invoke方法。
// 只能代理声明在接口中的方法。
Subject obj=(Subject)new JDKProxy().getIntence(new RealSubject());
obj.Request();
}
}
//抽象主题
interface Subject
{
void Request();
}
//真实主题
class RealSubject implements Subject
{
public void Request()
{
System.out.println("访问真实主题方法...");
}
}
/**
* jdk动态代理
**/
public class JDKProxy implements InvocationHandler {
private Object target;
public Object getIntence(Object object){
this.target=object;
Class<?>clazz=target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforeRequest();
Object obj=method.invoke(this.target,args);
afterRequest();
return obj;
}
public void beforeRequest()
{
System.out.println("访问真实主题之前的预处理。");
}
public void afterRequest()
{
System.out.println("访问真实主题之后的后续处理。");
}
}
优点:相对于静态代理,动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度。
缺点:目标类必须实现接口,还增加了代码的理解难度。
(2)CGLIB实现动态代理
(1)使用Cglib进行动态代理的目标类不需要强制要求实现接口。
(2)Cglib代理也叫做子类代理,它是在内存中动态的创建一个目标类的子类对象从而实现对目标类的代理。所以使用Cglib代理的类不能是final修饰的,并且被代理类中final和static修饰的方法不会被代理。
(3)Cglib的底层是通过字节码处理框架ASM来转换字节码并生成新的子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。
(4)Cglib是一个强大的高性能代码生成工具,它可以在程序运行时期扩展类与实现接口。许多AOP框架都有使用Cglib。
实现代码:
package proxy;
public class ProxyTest
{
public static void main(String[] args)
{
RealSubject realSubject=(RealSubject)new CGLIBProxy ().getInstence(RealSubject.class);
realSubject.Request();
}
}
//抽象主题
interface Subject
{
void Request();
}
//真实主题
class RealSubject implements Subject
{
public void Request()
{
System.out.println("访问真实主题方法...");
}
}
/**
* CGLIB代理
**/
public class CGLIBProxy implements MethodInterceptor {
public Object getInstence(Class<?> clazz){
Enhancer enhancer =new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
beforeRequest();
Object obj=methodProxy.invokeSuper(o,objects);
afterRequest();
return obj;
}
public void beforeRequest()
{
System.out.println("访问真实主题之前的预处理。");
}
public void afterRequest()
{
System.out.println("访问真实主题之后的后续处理。");
}
}
优点:不需要额外维护代理类,并且也不需要强制要求被代理的目标对象实现接口,使得代码更加的灵活。同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。
缺点:和JDK的动态代理一样,增加了代码的理解难度。
总结
一般当我们需要代理的类少的时候,可以考虑使用静态代理,但是静态代理的选择一般都比较少,因为其维护成本要高,并且局限也大。当被代理的目标类实现了接口时可以考虑使用JDK代理,反之当目标类没有实现接口时就只能选择使用Cglib代理了。