Spring的AOP其就是通过动态代理的机制实现的,所以理解动态代理尤其重要。

动态代理比静态代理的好处:

1.一个动态代理类可以实现多个业务接口。静态代理的一个代理类只能对一个业务接口的实现类进行包装,如果有多个业务接口的话就要定义很多实现类和代理类才行。

2.提供统一的方法前后处理。如果代理类对业务方法的预处理、调用后操作都是一样的(比如:调用前输出提示、调用后自动关闭连接),则多个静态代理类就会有很多重复代码。这时我们可以定义这样一个代理类,它能代理所有实现类的方法调用:根据传进来的业务实现类和方法名进行具体调用。——那就是动态代理。

一.JDK动态代理(针对接口)

一个类(BookManage)实现了一个接口(IBookManage),这个类就是我们需要代理的真实对象。

proxy = Proxy.newProxyInstance()创建动态代理对象,然后proxy.真实对象的方法。这时就会进入动态代理对象的invoke()方法里。

动态代理类:实现InvocationHandler接口,并实现里面的invoke()方法。invoke()方法里传入的是真实对象(BookManage),通过代理对象来调用方法时,是委托由其关联到的 handler 对象的invoke方法中来调用,并不是自己来真实调用,而是通过代理的方式来调用的。

代码实现:

1.1 接口类

package jdkproxy;

/**
* @author admin
*接口类
*/
public interface IBookManage {
//添加图书
public void addBook();
}

1.2 接口实现类

package jdkproxy;

/**
* @author admin
*接口实现类,真实对象
*/
public class BookManage implements IBookManage { @Override
public void addBook() {
System.out.println("addBook...");
} }

1.3 动态代理类

package jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author 梦里南柯
* 动态代理类
*/
public class DynamicProxy implements InvocationHandler {
private Object target;// 这其实业务实现类对象,用来调用具体的业务方法
/**
* 绑定业务对象并返回一个代理类
*
* @param target
* @return
*/
public Object bind(Object target) {
this.target = target; // 接收业务实现类对象参数
/*
* 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数 第一个参数
* handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
* 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,
* 表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了 第三个参数handler, 我们这里将这个代理对象关联到了上方的
* InvocationHandler 这个对象上
*/
IBookManage proxy = (IBookManage) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
return proxy;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在代理真实对象前我们可以添加一些自己的操作
System.out.println("before add book...");
System.out.println("开始调用真实对象的方法: :" + method);
// 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
Object result = method.invoke(target, args);
// 在代理真实对象后我们也可以添加一些自己的操作
System.out.println("after add book...");
return result;
}
}

代理对象最重要的方法:invoke()。

作用:当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用

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

proxy:  指代我们所代理的那个真实对象
method:  指代的是我们所要调用真实对象的某个方法的Method对象
args:  指代的是调用真实对象某个方法时接受的参数
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

loader:  一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载

interfaces:  一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了

h:  一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

1.4 测试

package jdkproxy;

public class Client
{
public static void main(String[] args) {
BookManage bookManage = new BookManage();
DynamicProxy proxy = new DynamicProxy();
IBookManage iBookManage = (IBookManage) proxy.bind(bookManage);
System.out.println("代理对象的类名: " + iBookManage.getClass().getName());
iBookManage.addBook();
}
}

运行结果:

代理对象的类名: com.sun.proxy.$Proxy0
before add book...
开始调用真实对象的方法: :public abstract void jdkproxy.IBookManage.addBook()
addBook...
after add book...

其中com.sun.proxy.$Proxy0,就是我们代理对象的名字,它是jvm运行时动态生成的一个对象。

那为什么Proxy.newProxyInstance()方法可以转型为IBookManage?因为这个方法里面的第二个参数给代理对象提供的是我们真实对象所实现的接口,所以代理对象实现了这组接口,当然可以转化为IBookManage类型了。然后就是代理对象(IBookManage).方法(addBook)调用。

通过代理对象调用方法的时候,其实就是委托由其关联到的 handler 对象的invoke方法中来调用,并不是自己来真实调用,而是通过代理的方式来调用的。

弊端:

1. 如果业务实现类是没有实现接口而是直接定义业务方法的话,就无法使用JDK动态代理了。

2. 如果业务实现类中新增了接口中没有的方法,这些方法是无法被代理的(因为无法被调用)。

二.CGLIB动态代理(针对类)

2.1 创建一个类不需要实现任何接口

2.2 绑定一个业务类

  • 创建一个加强器:Enhancer enhancer = new Enhancer();//用来创建动态代理类
  • 指定加强器要代理的业务类(父类)
  • 设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦截 enhancer.setCallback(this);
  • return enhancer.create(); // 创建动态代理类对象并返回
// 实现回调方法
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("预处理——————");
proxy.invokeSuper(obj, args); //调用业务类(父类中)的方法
System.out.println("调用后操作——————");
return null;
}

2.3 测试:创建业务类和代理类对象,然后通过  代理类对象.getInstance(业务类对象)  返回一个动态代理类对象(它是业务类的子类,可以用业务类引用指向它)。最后通过动态代理类对象进行方法调用。

三.比较

静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;

JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;

CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理;不能对 final类进行继承

源码级分析见:

https://blog.csdn.net/john_lw/article/details/79539070

http://ifeve.com/jdk动态代理代理与cglib代理原理探究/

https://www.jianshu.com/p/9a61af393e41?from=timeline&isappinstalled=0

JDK动态代理、CGLIB动态代理详解的更多相关文章

  1. Atitit 代理CGLIB 动态代理 AspectJ静态代理区别

    Atitit 代理CGLIB 动态代理 AspectJ静态代理区别 1.1. AOP 代理主要分为静态代理和动态代理两大类,静态代理以 AspectJ 为代表:而动态代理则以 spring AOP 为 ...

  2. 【Java入门提高篇】Day12 Java代理——Cglib动态代理

    今天来介绍另一种更为强大的代理——Cglib动态代理. 什么是Cglib动态代理? 我们先回顾一下上一篇的jdk动态代理,jdk动态代理是通过接口来在运行时动态创建委托类的代理对象,但是跟静态代理一样 ...

  3. ssh转发代理:ssh-agent用法详解

    SSH系列文章: SSH基础:SSH和SSH服务 SSH转发代理:ssh-agent用法详解 SSH隧道:端口转发功能详解 使用ssh-agent之前 使用ssh公钥认证的方式可以免去ssh客户端(如 ...

  4. 数据结构与算法系列2 线性表 使用java实现动态数组+ArrayList源码详解

    数据结构与算法系列2 线性表 使用java实现动态数组+ArrayList源码详解 对数组有不了解的可以先看看我的另一篇文章,那篇文章对数组有很多详细的解析,而本篇文章则着重讲动态数组,另一篇文章链接 ...

  5. 【原创】JDK 9-17新功能30分钟详解-语法篇-var

    JDK 9-17新功能30分钟详解-语法篇-var 介绍 JDK 10 JDK 10新增了新的关键字--var,官方文档说作用是: Enhance the Java Language to exten ...

  6. Java的三种代理模式:静态代理/JDK动态代理/Cglib动态代理

    1.静态代理:需要定义接口或者父类,目标对象与代理对象均实现同一接口或继承同一父类. 2.JDK动态代理:需要目标对象实现一个接口,通过动态反射的机制,生成代理对象,实现同一个接口 3.Cglib动态 ...

  7. java 静态代理 JDK动态代理 Cglib动态代理

    下面以一个简单的银行账户为例讲述讲述动态代理. 设计一个银行账户类,包含用户的账户余额,实现查询和更新余额功能 这个系统用了一段时间,有客户要求对账说账户余额给弄错了?因为上面没有存取款记录,最后银行 ...

  8. 《Java基础知识》动态代理(InvocationHandler)详解

    1. 什么是动态代理 对象的执行方法,交给代理来负责.比如user.get() 方法,是User对象亲自去执行.而使用代理则是由proxy去执行get方法. 举例:投资商找明星拍广告,投资商是通过经纪 ...

  9. Nginx 反向代理与负载均衡详解

    序言 Nginx的代理功能与负载均衡功能是最常被用到的,关于nginx的基本语法常识与配置已在Nginx 配置详解中有说明,这篇就开门见山,先描述一些关于代理功能的配置,再说明负载均衡详细. Ngin ...

  10. c# 把一个匿名对象赋值给一个Object类型的变量后,怎么取这个变量? c# dynamic动态类型和匿名类 详解C# 匿名对象(匿名类型)、var、动态类型 dynamic 深入浅析C#中的var和dynamic

    比如有一个匿名对象,var  result =......Select( a=>new {  id=a.id, name=a.name});然后Object  obj =  result ;我怎 ...

随机推荐

  1. spring-test跟junit结合单元测试获取ApplicationContext实例的方法

    步骤 1.继承AbstractJUnit4SpringContextTests 2.引入ApplicationContext   示例代码:(可以根据name或者类型获取bean) import or ...

  2. 第二十四篇 jQuery 学习6 删除元素

    jQuery 学习6 删除元素   上节课我们做了添加元素,模拟的是楼主发的文章,路人评论,那么同学们这节课学了删除之后,去之前的代码上添加一个删除,模拟一个楼主删除路人的评论. jQuery的删除方 ...

  3. 基于Openresty+Naxsi的WAF:从小白到实践

    序 2019年2月18日,加入妈妈网,至今已经有四个月的时间,上周进到一个网关项目组,这个项目的主要目的是基于openResty+Naxsi实现WAF,相关技术初定涉及到openResty.Lua.N ...

  4. web端文件上传,预览,下载,删除

      //HTML部分 <div class="item attachment attachmentNew"> <span class="name&quo ...

  5. Delphi DLL文件的静态调用

  6. Jenkins升级版本

    1 Jenkins的管理界面,下载最新版本的war包 2 找到自己部署Jenkins的war包的tomcat目录,替换最新的war包,重启tomcat即可 只需要把之前的war包重命名一个名字,不要以 ...

  7. DP tricks and experiences

    [LeetCode] 关于动态规划的经验与技巧. 很多时候多分配一位空间是为了递推的方便,例如前推两位. 辅助数组的索引,用到的可能是 1 — N,或者是 0 — N-1. 具体要看清 f[i] 每一 ...

  8. 关于GRPC的讲解

    gRPC服务发现&负载均衡 https://segmentfault.com/a/1190000008672912?utm_source=tag-newest GRPC编程指南 gRPC 介绍 ...

  9. HttpServletResponse 返回的json数据不是json字符串,而是json对象

    今天在改一个bug 情况: 在spring boot中写了一个类Result ,用来统一封装 各个API响应结果 , 其中重写了toString()方法来返回 json字符串 . 在正常情况下,从其它 ...

  10. __slots__节约空间

    1.为什么要使用__slots__ Python 使用 dicts(hash table)缓存大量的静态资源(属性). 我们最近在Image类中,用仅仅一行__slots__代码,改变成使用tuple ...