代理模式:给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。代理类与目标类实现相同的接口,让代理类去持有目标类的引用,进而去代理目标对象的一些行为
用处:1安全考虑,这点很容易理解,比如我只想暴露A类的方法f()给外界,但它还有其他的方法g()、h(),那么用代理模式就能达到目的了
2、性能增强,延迟重量级对象创建
// 数据库查询接口类
public interface IDBQuery {
public void request(String requestParamStr);
}
// 数据库查询实现类
public DBQuery implements IDBQuery {
public DBQuery() {
// 连接数据库,初始化等等耗时操作,略
}
@Override
public void request(String requestParamStr) {
// 略
}
}
// 代理类
public class DBQueryProxy implements IDBQuery {
private DBQuery real= null; // 被代理对象
@Override
public void request(String requestParamStr) {
if(real == null)
real = new DBQuery(); // 延迟到这里创建DBQuery对象
real.request();
}
}
// 使用
IDBQuery query = new DBQueryProxy();
query.requst(); // 真正使用时才会创建真实的DBQuery对象
JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了。CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。
简单的实现举例:
这是一个需要被代理的类,也就是父类,通过字节码技术创建这个类的子类,实现动态代理。
该类实现了创建子类的方法与代理的方法。getProxy(SuperClass.class)方法通过入参即父类的字节码,通过扩展父类的class来创建代理对象。intercept()方法拦截所有目标类方法的调用,obj表示目标类的实例,method为目标类方法的反射对象,args为方法的动态入参,proxy为代理类实例。proxy.invokeSuper(obj, args)通过代理类调用父类中的方法。
具体实现类:
输出结果:
CGLib创建的动态代理对象性能比JDK创建的动态代理对象的性能高不少,但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。同时,由于CGLib由于是采用动态创建子类的方法,对于final方法,无法进行代理。
JDK 动态代理的简单理解
动态代理
代理模式是 Java 中的常用设计模式,代理类通过调用被代理类的相关方法,提供预处理、过滤、事后处理等服务,动态代理及通过反射机制动态实现代理机制。JDK 中的 java.lang.reflect.Proxy 类可以用来实现动态代理。
首先,准备一个简单的接口和实现类
/**
* 接口 IHello.java
*/
public interface IHello {
void hello();
}
/**
* 实现类 Hello.java
*/
public class Hello implements IHello {
@Override
public void hello() {
System.out.println("我是Hello.");
}
}
通过 Proxy 实现动态代理
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.junit.Test;
public class HelloTest {
@Test
public void testProxy() throws Exception {
final IHello hello = new Hello();
/*
* proxyHello : 代理主题角色,代理类的实例
* IHello : 抽象主题角色,代理类和被代理类都需要实现的接口,JDK中的动态代理必须针对接口
* hello : 真实主题角色,被代理类的实例
*/
IHello proxyHello = (IHello) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class[] { IHello.class }, new InvocationHandler() {
/*
* @param proxy : 当前代理类的一个实例; 若在invoke()方法中调用proxy的非final方法,将造成无限循环调用.
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置的业务逻辑操作
System.out.println("---开始");
// 调用被代理类的方法,传入参数args,得到返回
Object object = method.invoke(hello, args);
// 后置的业务逻辑操作
System.out.println("---结束");
return object;
}
});
proxyHello.hello();
}
}