代理模式
对于client -> server 访问模式,如果我想在client 访问 server时候多做一些工作,那么可以是用代码,交互模式就变为client-> serverProxy -> server.
其具体的java 伪代码如下:
public interface iA
{
public void do();
}
public class RealA implement iA
{
public void do()
{
//dosomething....
}
}
public ProxyA implement iA //运用代理后客户端访问接口不变所以需要继承相同的接口。
{
RealA a;
public void do()
{
//create A if a is not create
//dosomething before .....
a.do();
//dosomething after .....
}
}
main()
{
iA a = new ProxyA();
}
动态代理模式
现在我们知道了简单的代理模式,如果A有多个实现类,比如RealA,RealA1,RealA2,难道我要对每个RealA? 类写一个代理类嘛?且就算你写了 ProxyRealA,ProxyRealB,我们也不太好统一调用。
代理有一个目的就是client -> proxy -> server这种模式中的server 封装成一种统一调用,不用管其内部实现的复杂性。
那么我们有没有办法在程序运行时候自动生成ProxyReal呢?
其大概伪代码如下:
InvocationHanderAimpl implements InvacationHandler
{
/**
* 这个方法实现就是类似ProxyA.do()方法类似的功能,在调用具体方法前后做一些处理。<br>
*/
public Object invoke(Object proxy, Method method, Object[] args)
{
... do something before invoke.
method.invoke(proxy,args);
.. do something after invoke.
}
}
main()
{
// getProxyInstance() 为iA a = new ProxyA();动态代理版本。
// InvocationHandler 代表了该代理类具体行为,即执行方法前后如何处理。
iA dynamicProxyObj = getProxyInstance(A.class,Aimpl.class,InvocationHandler);
dynamicProxyObj .do();
}
动态代理的两种具体实现
- jdk 的版本
public class ProxyHandler implements InvocationHandler
{
private Object proxied;
public ProxyHandler( Object proxied )
{
this.proxied = proxied;
}
public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable
{
//在转调具体目标对象之前,可以执行一些功能处理
//转调具体目标对象的方法
return method.invoke( proxied, args);
//在转调具体目标对象之后,可以执行一些功能处理
}
}
main()
{
RealA realA= new RealA ();
iA proxiedA= (A)Proxy.newProxyInstance(A.class.getClassLoader(),
new Class[]{iA.class},
new ProxyHandler(realA));
iA.do();
}
这里重点注意:
1.invocationHandler的实现。
2.newProxyInstance 的方法参数,interfaces只接收接口类型,即iA.class ,不接收RealA.class
//proxy.java
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h){
}
- cglib版实现
关于cglib,从网上找到英文版的介绍:
Cglib is an open source library that capable creating and loading class files in memory during Java run time. To do that it uses Java byte-code generation library ‘asm’, which is a very low level byte code creation tool,其开源类库为:net.sf.cglib.proxy.*。
我们先看看源码如何创建Cglib代理类的
package com.thoughtworks.proxy.factory;
.......
import net.sf.cglib.core.CodeGenerationException;
import net.sf.cglib.core.DefaultNamingPolicy;
import net.sf.cglib.core.Predicate;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.InvocationHandler;
import net.sf.cglib.proxy.Proxy;
.......
public class CglibProxyFactory extends AbstractProxyFactory {
{
public <T> T createProxy(Invoker invoker, Class... types) {
Class type = this.getSingleClass(types); //type =null ,说明types对象全部是接口,没有一个类
if(type == null) {
//全部是接口使用标准jdk的代理方式创建代理类,即proxy.newProxyInstance();
return standardProxyFactory.createProxy(invoker, types);
} else if(type.isPrimitive()) {
throw new IllegalArgumentException("Cannot subclass primitive type");
} else {
Class[] interfaces = this.getInterfaces(types);
Enhancer enhancer = new Enhancer();
while(true) {
enhancer.setSuperclass(type);
enhancer.setInterfaces(interfaces);
enhancer.setCallback(new CglibProxyFactory.CGLIBInvocationHandlerAdapter(invoker));
Object proxy;
try {
proxy = enhancer.create();
return proxy;
} catch (CodeGenerationException var8) {
Throwable wrapper = var8.getCause();
if(wrapper != null && wrapper.getCause() instanceof SecurityException && enhancer.getNamingPolicy() != this.namingPolicy) {
enhancer.setNamingPolicy(this.namingPolicy);
continue;
}
} catch (IllegalArgumentException var9) {
;
} catch (NoSuchMethodError var10) {
;
}
proxy = this.createWithConstructor(type, enhancer);
return proxy;
}
}
}
}
其具体如何创建和使用CglibProxy可以参考博客:http://blog.csdn.net/ljphhj/article/details/20483171
两种方式的优缺点:
CGLib创建的动态代理对象性能比JDK创建的动态代理对象的性能高不少,但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。同时,由于CGLib由于是采用动态创建子类的方法,对于final方法,无法进行代理!
说在后面的话
这篇文章只是简单的介绍proxy干了一件什么事情。
从具体的技术层面还有以下疑问:
1.ClassLoader 装载的是哪个类?如果是一个接口的话类是如何加载进来的。
2.Cglib与jdk Proxy类的区别。
这两个问题以后待分析。
参考
关于cglib:
https://www.programcreek.com/java-api-examples/index.php?api=net.sf.cglib.proxy.MethodInterceptor
https://github.com/cglib/cglib/wiki/Tutorial