java动态代理中的invoke方法是如何被自动调用的
一、动态代理与静态代理的区别。
(1)Proxy类的代码被固定下来,不会因为业务的逐渐庞大而庞大;
(2)可以实现AOP编程,这是静态代理无法实现的;
(3)解耦,如果用在web业务下,可以实现数据层和业务层的分离。
(4)动态代理的优势就是实现无侵入式的代码扩展。
静态代理这个模式本身有个大问题,如果类方法数量越来越多的时候,代理类的代码量是十分庞大的。所以引入动态代理来解决此类问题
二、动态代理
Java中动态代理的实现,关键就是这两个东西:Proxy、InvocationHandler,下面从InvocationHandler接口中的invoke方法入手,简单说明一下Java如何实现动态代理的。
首先,invoke方法的完整形式如下:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(obj, args);
return null;
}
首先猜测一下,method是调用的方法,即需要执行的方法;args是方法的参数;proxy,这个参数是什么?以上invoke()方法的实现即是比较标准的形式,我们看到,这里并没有用到proxy参数。查看JDK文档中Proxy的说明,如下:
A method invocation on a proxy instance through one of its proxy interfaces will be dispatched to the invoke method of the instance's invocation handler,
passing the proxy instance,a java.lang.reflect.Method object identifying the method that was invoked, and an array of type Object containing the arguments.
由此可以知道以上的猜测是正确的,同时也知道,proxy参数传递的即是代理类的实例。
为了方便说明,这里写一个简单的例子来实现动态代理。
//抽象角色(动态代理只能代理接口)
public interface Subject { public void request();
}
Subject
//真实角色:实现了Subject的request()方法
public class RealSubject implements Subject{ public void request(){
System.out.println("From real subject.");
}
}
RealSubject
//实现了InvocationHandler
public class DynamicSubject implements InvocationHandler
{
private Object obj;//这是动态代理的好处,被封装的对象是Object类型,接受任意类型的对象 public DynamicSubject()
{
} public DynamicSubject(Object obj)
{
this.obj = obj;
} //这个方法不是我们显示的去调用
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
System.out.println("before calling " + method); method.invoke(obj, args); System.out.println("after calling " + method); return null;
} }
DynamicSubject
//客户端:生成代理实例,并调用了request()方法
public class Client { public static void main(String[] args) throws Throwable{
// TODO Auto-generated method stub Subject rs=new RealSubject();//这里指定被代理类
InvocationHandler ds=new DynamicSubject(rs);
Class<?> cls=rs.getClass(); //以下是一次性生成代理 Subject subject=(Subject) Proxy.newProxyInstance(
cls.getClassLoader(),cls.getInterfaces(), ds); //这里可以通过运行结果证明subject是Proxy的一个实例,这个实例实现了Subject接口
System.out.println(subject instanceof Proxy); //这里可以看出subject的Class类是$Proxy0,这个$Proxy0类继承了Proxy,实现了Subject接口
System.out.println("subject的Class类是:"+subject.getClass().toString()); System.out.print("subject中的属性有:"); Field[] field=subject.getClass().getDeclaredFields();
for(Field f:field){
System.out.print(f.getName()+", ");
} System.out.print("\n"+"subject中的方法有:"); Method[] method=subject.getClass().getDeclaredMethods(); for(Method m:method){
System.out.print(m.getName()+", ");
} System.out.println("\n"+"subject的父类是:"+subject.getClass().getSuperclass()); System.out.print("\n"+"subject实现的接口是:"); Class<?>[] interfaces=subject.getClass().getInterfaces(); for(Class<?> i:interfaces){
System.out.print(i.getName()+", ");
} System.out.println("\n\n"+"运行结果为:");
subject.request();
}
}
Client
运行结果如下:此处省略了包名,***代替
true
subject的Class类是:class $Proxy0
subject中的属性有:m1, m3, m0, m2,
subject中的方法有:request, hashCode, equals, toString,
subject的父类是:class java.lang.reflect.Proxy
subject实现的接口是:cn.edu.ustc.dynamicproxy.Subject,
运行结果为:
before calling public abstract void ***.Subject.request()
From real subject.
after calling public abstract void ***.Subject.request()
PS:这个结果的信息非常重要,至少对我来说。因为我在动态代理犯晕的根源就在于将上面的subject.request()理解错了,至少是被表面所迷惑,没有发现这个subject和Proxy之间的联系,一度纠结于最后调用的这个request()是怎么和invoke()联系上的,而invoke又是怎么知道request存在的。其实上面的true和class $Proxy0就能解决很多的疑问,再加上下面将要说的$Proxy0的源码,完全可以解决动态代理的疑惑了。
从以上代码和结果可以看出,我们并没有显示的调用invoke()方法,但是这个方法确实执行了。下面就整个的过程进行分析一下:
从Client中的代码看,可以从newProxyInstance这个方法作为突破口,我们先来看一下Proxy类中newProxyInstance方法的源代码:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
if (h == null) {
throw new NullPointerException();
} /*
* Look up or generate the designated proxy class.
*/
Class cl = getProxyClass(loader, interfaces); /*
* Invoke its constructor with the designated invocation handler.
*/
try {
/*
* Proxy源码开始有这样的定义:
* private final static Class[] constructorParams = { InvocationHandler.class };
* cons即是形参为InvocationHandler类型的构造方法
*/
Constructor cons = cl.getConstructor(constructorParams);
return (Object) cons.newInstance(new Object[] { h });
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
} catch (IllegalAccessException e) {
throw new InternalError(e.toString());
} catch (InstantiationException e) {
throw new InternalError(e.toString());
} catch (InvocationTargetException e) {
throw new InternalError(e.toString());
}
}
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)做了以下几件事.
(1)根据参数loader和interfaces调用方法 getProxyClass(loader, interfaces)创建代理类$Proxy0.$Proxy0类 实现了interfaces的接口,并继承了Proxy类.
(2)实例化$Proxy0并在构造方法中把DynamicSubject传过去,接着$Proxy0调用父类Proxy的构造器,为h赋值,如下:
class Proxy{
InvocationHandler h=null;
protected Proxy(InvocationHandler h) {
this.h = h;
}
...
}
来看一下这个继承了Proxy的$Proxy0的源代码:
public final class $Proxy0 extends Proxy implements Subject {
private static Method m1;
private static Method m0;
private static Method m3;
private static Method m2; static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals",
new Class[] { Class.forName("java.lang.Object") }); m0 = Class.forName("java.lang.Object").getMethod("hashCode",
new Class[0]); m3 = Class.forName("***.RealSubject").getMethod("request",
new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString",
new Class[0]); } catch (NoSuchMethodException nosuchmethodexception) {
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
} catch (ClassNotFoundException classnotfoundexception) {
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
} //static public $Proxy0(InvocationHandler invocationhandler) {
super(invocationhandler);
} @Override
public final boolean equals(Object obj) {
try {
return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
} @Override
public final int hashCode() {
try {
return ((Integer) super.h.invoke(this, m0, null)).intValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
} public final void request() {
try {
super.h.invoke(this, m3, null);
return;
} catch (Error e) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
} @Override
public final String toString() {
try {
return (String) super.h.invoke(this, m2, null);
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}
接着把得到的$Proxy0实例强制转换成Subject,并将引用赋给subject。当执行subject.request()方法时,就调用了$Proxy0类中的request()方法,进而调用父类Proxy中的h的invoke()方法.即InvocationHandler.invoke()。
PS:1、需要说明的一点是,Proxy类中getProxyClass方法返回的是Proxy的Class类。之所以说明,是因为我一开始犯了个低级错误,以为返回的是“被代理类的Class类”- -!推荐看一下getProxyClass的源码,很长=。=
2、从$Proxy0的源码可以看出,动态代理类不仅代理了显示定义的接口中的方法,而且还代理了java的根类Object中的继承而来的equals()、hashcode()、toString()这三个方法,并且仅此三个方法。
Q:到现在为止,还有一个疑问,invoke方法中的第一个参数是Proxy的实例(准确说,最终用到的是$Proxy0的实例),但是有什么用呢?或者说,程序内是怎样显示出作用的?
A:就本人目前的水平看来,这个proxy参数并没有什么作用,在整个动态代理机制中,并没有用到InvocationHandler中invoke方法的proxy参数。而传入的这个参数实际是代理类的一个实例。我想可能是为了让程序员在invoke方法中使用反射来获取关于代理类的一些信息吧。
java动态代理中的invoke方法是如何被自动调用的的更多相关文章
- java动态代理中的invoke方法是如何被自动调用的(转)
一.动态代理与静态代理的区别. (1)Proxy类的代码被固定下来,不会因为业务的逐渐庞大而庞大: (2)可以实现AOP编程,这是静态代理无法实现的: (3)解耦,如果用在web业务下,可以实现数据层 ...
- java动态代理源码解析
众所周知,java动态代理同反射原理一直是许多框架的底层实现,之前一直没有时间来分析动态代理的底层源码,现结合源码分析一下动态代理的底层实现 类和接口 java动态代理的主要类和接口有:java.la ...
- Java动态代理和CGLib代理
本文参考 在上一篇"Netty + Spring + ZooKeeper搭建轻量级RPC框架"文章中涉及到了Java动态代理和CGLib代理,在这篇文章中对这两种代理方式做详解 下 ...
- Spring中AOP的两种代理方式(Java动态代理和CGLIB代理)
第一种代理即Java的动态代理方式上一篇已经分析,在这里不再介绍,现在我们先来了解下GCLIB代理是什么?它又是怎样实现的?和Java动态代理有什么区别? cglib(Code Generation ...
- java反射中Method类invoke方法的使用方法
package com.zsw.test; import java.lang.reflect.Method;import java.lang.reflect.InvocationTargetExcep ...
- Java 动态代理 两种实现方法
AOP的拦截功能是由java中的动态代理来实现的.说白了,就是在目标类的基础上增加切面逻辑,生成增强的目标类(该切面逻辑或者在目标类函数执行之前,或者目标类函数执行之后,或者在目标类函数抛出异常时候执 ...
- Proxy Pattern(Java动态代理和cglib的实现)
代理模式:给某一个对象提供代理对象,由代理对象控制具体对象的引用. 代理,指的就是一个角色对表另一个角色采取行动,就生活中,一个红酒厂商,是不会直接把红酒零销给客户的,都是通过代理完成他的销售业务.而 ...
- JAVA深入研究——Method的Invoke方法。
在写代码的时候,发现Method可以调用子类的对象,但子类即使是改写了的Method,方法名一样,去调用父类的对象也会报错,虽然这是很符合多态的现象,也符合java的动态绑定规范,但还是想弄懂java ...
- JAVA深入研究——Method的Invoke方法
http://www.cnblogs.com/onlywujun/p/3519037.html 在写代码的时候,发现Method可以调用子类的对象,但子类即使是改写了的Method,方法名一样,去调用 ...
随机推荐
- vs查看动态库依赖
dumpbin是VS自带的工具,进入Visual Studio 的命令提示 查看程序或动态库所依赖的动态库 dumpbin /dependents abc.exe 查看动态库的输出函数 dumpbi ...
- pwnable.kr-flag-Writeup
MarkdownPad Document *:first-child { margin-top: 0 !important; } body>*:last-child { margin-botto ...
- ASP.NET Core搭建多层网站架构【0-前言】
2020/01/26, ASP.NET Core 3.1, VS2019 摘要:基于ASP.NET Core 3.1 WebApi搭建后端多层网站架构 目录 0-前言 1-项目结构分层建立 2-公共基 ...
- js加密(六)QB.com
1. url: https://notice.qb.com/detail?noticeId=256 2. target: 3. 分析: 3.1 打开网址,刷新页面,看看都发送了哪些请求. 看到了发送了 ...
- laravel搭建博客实战的教程实例
这篇文章主要给大家介绍了关于利用laravel搭建一个迷你博客的相关资料,文中将一步步的实现步骤通过示例代码介绍的非常详细,对大家具有一定的参考学习价值,需要的朋友们下面跟着来一起学习学习吧. 本文主 ...
- Linux Mysql8每天定时备份数据库并自动删除7天之前的备份
在某一文件夹下创建shell文件: vi /var/spool/cron/mysqlbak.sh 复制如下内容: #!/bin/bash # Name:name.sh backupdir=/www/m ...
- bootstrap下拉选择框倒三角所占宽度
<select id="edit" class="form-control" style="width:42%;padding-right: 3 ...
- 在HTML中实现两个div并排显示
在HTML中让两个div并排显示,通常情况下有三种实现方式,包括: (1)设置为行内样式,display:inline-block (2)设置float浮动 (3)设置position定位属性为abs ...
- Java 多线程学习笔记(一)
一.进程和线程 (1)进程概念的引入 为了能使程序并发执行,并且可以对并发执行的程序加以描述和控制,人们引入了“进程”的概念.---摘之计算机操作系统(第四版)汤小丹编著 (2)进程概念 进程(Pro ...
- Linux 笔记:虚拟控制台
登录后按Alt+F2键这时又可以看到"login:"提示符,这个就是第二个虚拟控制台. 一般新安装的Linux有四个虚拟控制台,可以用Alt+F1~Alt+F4来访问. 虚拟控制台 ...