java代理的深入浅出(一)-Proxy

1.什么是代理

代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。

按照代理的创建时期,代理类可以分为两种。

静态代理:

由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。

实现方式:

1、为每个代理类,写对应的代理类

 优点:简单方便
缺点:当业务系统中需要大量的代理类时, 定义繁多的代理类

2、使用Aspectj

 使用Aspectj工具,将目标类class类,织入横切逻辑
优点:因在JVM加载类前,已经将横切的业务逻辑加载到目标类中, 所以在执行效率上非常高
缺点:不易维护, 修改的话, 还需要重新生成,编译

动态代理:在程序运行时,运用反射机制动态创建而成。

优点:易修改,易维护

缺点:需要动态生成代理类, 在效率上比静态代理相对低

实现的两种方式

1、JDK动态代理

目标对象必须有对应接口定义

2、CGLIB动态代理

目标对象不用有对应接口, 会生成目标类的子类,所以目标类的方法不能是final

Spring AOP实现机制是动态代理, 主要是上面两种方式JDK、CGLIB

2.基本原理

目前Java开发包中包含了对动态代理的支持,但是其实现只支持对接口的的实现。 其实现主要通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。 Proxy类主要用来获取动态代理对象,InvocationHandler接口用来约束调用者实现。

Proxy类:

Porxy类也是在java.lang.reflect,Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。

代理类具用以下属性:

  • 代理类是公共的、最终的,而不是抽象的。

  • 未指定代理类的非限定名称。但是,以字符串 "$Proxy" 开头的类名空间应该为代理类保留。

  • 代理类扩展 java.lang.reflect.Proxy。

  • 代理类会按同一顺序准确地实现其创建时指定的接口。

  • 如果代理类实现了非公共接口,那么它将在与该接口相同的包中定义。否则,代理类的包也是未指定的。注意,包密封将不阻止代理类在运行时在特定包中的成功定义,也不会阻止相同类加载器和带有特定签名的包所定义的类。

  • 由于代理类将实现所有在其创建时指定的接口,所以对其 Class 对象调用 getInterfaces 将返回一个包含相同接口列表的数组(按其创建时指定的顺序),对其 Class 对象调用 getMethods 将返回一个包括这些接口中所有方法的 Method 对象的数组,并且调用 getMethod 将会在代理接口中找到期望的一些方法。

  • 如果 Proxy.isProxyClass 方法传递代理类(由 Proxy.getProxyClass 返回的类,或由 Proxy.newProxyInstance 返回的对象的类),则该方法返回 true,否则返回 false。

  • 代理类的 java.security.ProtectionDomain 与由引导类加载器(如 java.lang.Object)加载的系统类相同,原因是代理类的代码由受信任的系统代码生成。此保护域通常被授予 java.security.AllPermission。

  • 每个代理类都有一个可以带一个参数(接口 InvocationHandler 的实现)的公共构造方法,用于设置代理实例的调用处理程序。并非必须使用反射 API 才能访问公共构造方法,通过调用 Proxy.newInstance 方法(将调用 Proxy.getProxyClass 的操作和调用带有调用处理程序的构造方法结合在一起)也可以创建代理实例。

      protected  Proxy(InvocationHandler h)  //使用其调用处理程序的指定值从子类(通常为动态代理类)构建新的 Proxy 实例。 
    
      static InvocationHandler getInvocationHandler(Object proxy) //返回指定代理实例的调用处理程序。 
    
      static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) //返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。 
    
      static boolean isProxyClass(Class<?> cl) //当且仅当指定的类通过 getProxyClass 方法或 newProxyInstance 方法动态生成为代理类时,返回 true。 
    
      static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) //返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。

InvocationHandler接口:

InvocationHandler接口也是在java.lang.reflect,唯一的一个方法是invoke如下:

Object invoke(Object proxy, Method method, Object[] args)

这个方法有三个参数,其中第二和第三个参数都比较好理解,一个是被拦截的方法,一个是该方法的参数列表。关键是第一个参数。按照doc文档的解析,proxy - the proxy instance that the method was invoked on也就是说,proxy应该是一个代理实例(动态代理类)。

3.示例

接口与实现类:

public interface UserService {

    void addUser(long cardId);
} public class UserServiceImpl implements UserService {
public void addUser(long cardId) {
System.out.println("cardId>>>>>"+cardId);
}
}

代理类的创建和拦截处理类

public class UserInvacationHandler implements InvocationHandler {

    private Object target;
public Object getProxyInstance(Object target){
this.target =target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
} public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
System.out.println("before target method.....");
result = method.invoke(target,args);
System.out.println("after target method.....");
return result;
}
}

代理测试类

public class ProxyTest {
public static void main(String[] args){
UserInvacationHandler userInvacationHandler = new UserInvacationHandler();
UserService userService = (UserService) userInvacationHandler.getProxyInstance(new UserServiceImpl());
userService.addUser(1L);
}
}

代理类反编译

生成代理类的方法Proxy.newProxyInstance(), 其源码核心代码是ProxyGenerator.generateProxyClass(String paramString, Class[] paramArrayOfClass)

public class ProxyClassFile {

    public static void main(String[] args){

        String proxyName = "UserServiceProxy";

        UserService a = new UserServiceImpl();
Class[] interfaces = a.getClass().getInterfaces();
byte[] bytes = ProxyGenerator.generateProxyClass(proxyName, interfaces);
File f = new File("D:/work/code/middleware/study/proxy/UserServiceProxy.class");
try{
FileOutputStream fos = new FileOutputStream(f);
fos.write(bytes);
fos.flush();
fos.close();
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e1){
e1.printStackTrace();
} }
}

反编译

import com.longchao.proxy.UserService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException; public final class UserServiceProxy extends Proxy
implements UserService
{
private static Method m1;
private static Method m3;
private static Method m0;
private static Method m2; public UserServiceProxy(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
} public final boolean equals(Object paramObject)
throws
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
} public final void addUser(long paramLong)
throws
{
try
{
this.h.invoke(this, m3, new Object[] { Long.valueOf(paramLong) });
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
} public final int hashCode()
throws
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
} public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
} static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("com.longchao.proxy.UserService").getMethod("addUser", new Class[] { Long.TYPE });
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
}
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}

从源码发现, addUser方法(或其他方法)都会调用this.h.invoke(this, m3, null);

this.h 是其父类Proxy的protected InvocationHandler h; 即我们自定义的UserInvocationHandler,

m3:

m3 = Class.forName("com.user.UserService").getMethod("addUser", new Class[0]);

4.总结

一个典型的动态代理创建对象过程可分为以下四个步骤:

  • 1、通过实现InvocationHandler接口创建自己的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(...);

  • 2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类

    Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});

  • 3、通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型

    Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});

  • 4、通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入

    Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));

    为了简化对象创建过程,Proxy类中的newInstance方法封装了2~4,只需两步即可完成代理对象的创建。

    生成的ProxySubject继承Proxy类实现Subject接口,实现的Subject的方法实际调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的的方法(Object result=method.invoke(proxied,args))

5美中不足

诚然,Proxy已经设计得非常优美,但是还是有一点点小小的遗憾之处,那就是它始终无法摆脱仅支持interface代理的桎梏,因为它的设计注定了这个遗憾。回想一下那些动态生成的代理类的继承关系图,它们已经注定有一个共同的父类叫Proxy。Java的继承机制注定了这些动态代理类们无法实现对class的动态代理,原因是多继承在Java中本质上就行不通。有很多条理由,人们可以否定对 class代理的必要性,但是同样有一些理由,相信支持class动态代理会更美好。接口和类的划分,本就不是很明显,只是到了Java中才变得如此的细化。如果只从方法的声明及是否被定义来考量,有一种两者的混合体,它的名字叫抽象类。实现对抽象类的动态代理,相信也有其内在的价值。此外,还有一些历史遗留的类,它们将因为没有实现任何接口而从此与动态代理永世无缘。如此种种,不得不说是一个小小的遗憾。但是,不完美并不等于不伟大,伟大是一种本质,Java动态代理就是佐例。

java代理的深入浅出(一)-Proxy的更多相关文章

  1. java代理的深入浅出(二)-CGLIB

    java代理的深入浅出(二)-CGLIB 1.基本原理 CGLIB的原理就是生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法.在子类中拦截所有父类方法的调用,拦截下来交给设置的Me ...

  2. java代理的深入浅出(三)-JavaAssist,ASM

    简介 类似字节码操作方法还有ASM.几种动态编程方法相比较,在性能上Javassist高于反射,但低于ASM,因为Javassist增加了一层抽象.在实现成本上Javassist和反射都很低,而ASM ...

  3. java 动态代理范例 InvocationHandler与Proxy

    java 动态代理范例 InvocationHandler与Proxy,拦截与代理 java.lang.reflect.Proxy,Proxy 提供用于创建动态代理类和实例的静态方法.newProxy ...

  4. 关于利用动态代理手写数据库连接池的异常 java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to java.sql.Connection

    代码如下: final Connection conn=pool.remove(0); //利用动态代理改造close方法 Connection proxy= (Connection) Proxy.n ...

  5. 浅谈Java代理一:JDK动态代理-Proxy.newProxyInstance

    浅谈Java代理一:JDK动态代理-Proxy.newProxyInstance java.lang.reflect.Proxy:该类用于动态生成代理类,只需传入目标接口.目标接口的类加载器以及Inv ...

  6. 使用browsermob代理出现错误java.lang.NoClassDefFoundError: org/littleshoot/proxy/HttpFiltersSource

    使用browsermob代理做埋点数据,maven配置的包如下 <dependency> <groupId>net.lightbody.bmp</groupId> ...

  7. Java-Spring:java.lang.ClassCastException: com.sun.proxy.$Proxy* cannot be cast to***问题解决方案

    java.lang.ClassCastException: com.sun.proxy.$Proxy* cannot be cast to***问题解决方案 临床表现: 病例: 定义代理类: @Tra ...

  8. Java代理模式/静态代理/动态代理

    代理模式:即Proxy Pattern,常用的设计模式之一.代理模式的主要作用是为其他对象提供一种代理以控制对这个对象的访问. 代理概念 :为某个对象提供一个代理,以控制对这个对象的访问. 代理类和委 ...

  9. Java代理(jdk静态代理、动态代理和cglib动态代理)

    一.代理是Java常用的设计模式,代理类通过调用被代理类的相关方法,并对相关方法进行增强.加入一些非业务性代码,比如事务.日志.报警发邮件等操作. 二.jdk静态代理 1.业务接口 /** * 业务接 ...

随机推荐

  1. no method declared with objective-c selector error

    down voteaccepted Swift 2.2 / Xcode 7.3 has a new way to use selector:Selector("funcName") ...

  2. response.setContentType与 request.setCharacterEncoding 区别

    1.request.setCharacterEncoding()是设置从request中取得的值或从数据库中取出的值的编码 2.response.setContentType指定 HTTP 响应的编码 ...

  3. C#调用托管C++类(DLL)

    毕设是做一个网络摄像头的相关应用.界面用WPF,图像处理部分是OpenCV.没用EmguCV的原因是国内EmguCV的资料相对比较少,EmguCV虽然提供了Winform的控件,在做UI上有一定优势, ...

  4. iOS身份证号码识别

    一.前言   身份证识别,又称OCR技术.OCR技术是光学字符识别的缩写,是通过扫描等光学输入方式将各种票据.报刊.书籍.文稿及其它印刷品的文字转化为图像信息,再利用文字识别技术将图像信息转化为可以使 ...

  5. 身份证js检测

    var vcity={ 11:"北京",12:"天津",13:"河北",14:"山西",15:"内蒙古&quo ...

  6. Shell错误[: missing `]'

    shell 文件运行时出现错误:     [: missing `]' 原因可能是 if [ ! -d $date] then mkdir ./$date fi 代码中的 ] 方括号内部必须要有个空格 ...

  7. Django中Admin样式定制

    Django自带的admin在展示数据是样式有点单一,我们可以自己定义数据的展示样式. 一.自定义数据展示样式 1.后台查询书记列表时,同时列出出版社和出版时间: admin.py文件 from dj ...

  8. webapi中的Route的标签的命名参数name的使用

    Route Names In Web API, every route has a name. Route names are useful for generating links, so that ...

  9. LeetCode OJ 54. Spiral Matrix

    Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral or ...

  10. 放弃阿里云主机,选择高性价比Vultr VPS免备案

    阿里云主机ECS推广多年后,质量有所改善,但我依然强烈不推荐阿里云主机.考虑性价比带宽速度等因素后,我推荐的vps品牌有vultr和digitalocean,还有大名鼎鼎的linode,是中国用户的最 ...