Java中jdk代理和cglib代理
代理模式
给某一个对象提供一个代理,并由代理对象控制对原对象的引用。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
在Java中代理模式从实现方式上可以分为两个类别:静态代理和动态代理
静态代理: 也就是我们学习设计模式之代理模式时常见的事例,具体不在赘述,参见:【Java设计模式-13代理模式】
动态代理: 在静态代理中,因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类。同时,一旦接口增加方法,目标对象与代理对象都要维护。那么如何解决这种问题呢?答案就是动态代理,下面会使用两种动态代理(jdk代理、cglib代理)分别实现同一个事例来体会一下动态代理的实现。
动态代理
动态代理具有以下特性:
1.代理对象不需要实现接口
2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象
jdk代理
只支持对接口的的实现,其实现主要通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。
Proxy类主要用来获取动态代理对象,InvocationHandler接口用来约束调用者实现。
JDK的动态代理用起来非常简单,但它有一个限制,就是使用动态代理的对象必须实现一个或多个接口
package com.lkf.parttern.proxy.dynamic;
/**
* 账户操作接口
*
* @author kaifeng
*/
public interface Account {
/**
* 查询余额
*/
void queryAccountBalance();
/**
* 充值话费
*/
void updateAccountBalance();
}
package com.lkf.parttern.proxy.dynamic;
/**
* 账户操作实现
*
* @author kaifeng
*/
public class AccountImpl implements Account {
/**
* 查询余额
*/
@Override
public void queryAccountBalance() {
System.out.println("【AccountImpl::queryAccountBalance】-查询账户余额");
}
/**
* 充值话费
*/
@Override
public void updateAccountBalance() {
System.out.println("【AccountImpl::updateAccountBalance】-话费充值");
}
}
代理对象实现接口InvocationHandler
package com.lkf.parttern.proxy.dynamic.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 这里使用的是Jdk的动态代理,其必须实现接口,这也是jdk代理的缺陷,不过cglib代理会修补这个缺陷
*
* @author kaifeng
*/
public class JDKAccountProxyFactory implements InvocationHandler {
private Object target;
/**
* 代理方式实例化对象
*/
public Object bind(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
boolean objFlag = method.getDeclaringClass().getName().equals("java.lang.Object");
Object result = null;
if (!objFlag) {
System.out.println("【JDKAccountProxyFactory::invoke】-代理before");
}
//真实方法调用
result = method.invoke(this.target, args);
if (!objFlag) {
System.out.println("【JDKAccountProxyFactory::invoke】-代理after");
}
return result;
}
}
package com.lkf.parttern.proxy.dynamic.jdk;
import com.lkf.parttern.proxy.dynamic.Account;
import com.lkf.parttern.proxy.dynamic.AccountImpl;
/**
* jdk动态代理测试
*/
public class JDKAccountProxyFactoryTest {
public static void main(String[] args) {
Account account = (Account) new JDKAccountProxyFactory().bind(new AccountImpl());
account.queryAccountBalance();
System.out.println("***************************");
account.updateAccountBalance();
}
}
【JDKAccountProxyFactory::invoke】-代理before
【AccountImpl::queryAccountBalance】-查询账户余额
【JDKAccountProxyFactory::invoke】-代理after
***************************
【JDKAccountProxyFactory::invoke】-代理before
【AccountImpl::updateAccountBalance】-话费充值
【JDKAccountProxyFactory::invoke】-代理after
CGLIB代理
对于上面说到JDK仅支持对实现接口的委托类进行代理的缺陷,CGLIB解决了这个问题,使其委托类也可是非接口实现类。
CGLIB内部使用到ASM,所以我们下面的例子需要引入cglib-3.2.5.jar
Cglib的原理是对指定的目标类动态生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类和final方法进行代理。
营业员实体类,没有实现接口的普通类
package com.lkf.parttern.proxy.dynamic;
import java.util.UUID;
/**
* 营业员
*
* @author kaifeng
*/
public class SalesPerson {
private String jobNum = String.valueOf(UUID.randomUUID());
public String getJobNum() {
return jobNum;
}
}
cglib动态代理实现了接口MethodInterceptor
package com.lkf.parttern.proxy.dynamic.cglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* cglib动态生成代理对象
*
* @author kaifeng
*/
public class CglibAccountProxyFactory implements MethodInterceptor {
private Object target;
/**
* 获取对象实例
*/
public Object getInstance(Object target) {
this.target = target;
return Enhancer.create(this.target.getClass(), this);
}
/**
* 方法拦截
* <p>
* 一般使用proxy.invokeSuper(obj,args)方法
* proxy.invoke(obj,args),这是执行生成子类的方法。
* 如果传入的obj就是子类的话,会发生内存溢出,因为子类的方法不进入intercept方法,而这个时候又去调用子类的方法,两个方法直接循环调用了
* </p>
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 排除Object类中的toString等方法
System.out.println(method.getDeclaringClass().getName());
boolean objFlag = method.getDeclaringClass().getName().equals("java.lang.Object");
if (!objFlag) {
System.out.println("【CglibAccountProxyFactory::intercept】-代理before");
}
Object result = null;
result = methodProxy.invokeSuper(obj, args);
if (!objFlag) {
System.out.println("【CglibAccountProxyFactory::intercept】-代理after");
}
return result;
}
}
cglib动态代理测试
package com.lkf.parttern.proxy.dynamic.cglib;
import com.lkf.parttern.proxy.dynamic.Account;
import com.lkf.parttern.proxy.dynamic.AccountImpl;
import com.lkf.parttern.proxy.dynamic.SalesPerson;
/**
* cglib动态代理测试
*
* @author kaifeng
*/
public class CglibAccountProxyFactoryTest {
public static void main(String[] args) {
// 下面是用cglib的代理
// 1.支持实现接口的类
Account account = (Account) new CglibAccountProxyFactory().getInstance(new AccountImpl());
account.queryAccountBalance();
// 2.支持未实现接口的类
SalesPerson salesPerson = (SalesPerson) new CglibAccountProxyFactory().getInstance(new SalesPerson());
System.out.println("我的工号是:" + salesPerson.getJobNum());
}
}
com.lkf.parttern.proxy.dynamic.AccountImpl
【CglibAccountProxyFactory::intercept】-代理before
【AccountImpl::queryAccountBalance】-查询账户余额
【CglibAccountProxyFactory::intercept】-代理after
com.lkf.parttern.proxy.dynamic.SalesPerson
【CglibAccountProxyFactory::intercept】-代理before
【CglibAccountProxyFactory::intercept】-代理after
我的工号是:5cee95e6-9032-463c-8a4a-139d1f882b4f
总结
- Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.
- JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.
- Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP,为他们提供方法的interception(拦截)
- Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.
在Spring的AOP编程中:
如果加入容器的目标对象有实现接口,会使用JDK代理;
如果目标对象没有实现接口,会使用Cglib代理
Java中jdk代理和cglib代理的更多相关文章
- spring的AOP动态代理--JDK代理和CGLIB代理区分以及注意事项
大家都知道AOP使用了代理模式,本文主要介绍两个代理模式怎么设置以及区别,对原文一些内容进行了引用后加入了自己的理解和更深入的阐述: 一.JDK代理和CGLIB代理的底层实现区别* JDK代理只能 ...
- 总结两种动态代理jdk代理和cglib代理
动态代理 上篇文章讲了什么是代理模式,为什么用代理模式,从静态代理过渡到动态代理. 这里再简单总结一下 什么是代理模式,给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问,即客户不直接操控原 ...
- jdk代理和cglib代理源代码之我见
以前值是读过一遍jdk和cglib的代理,时间长了,都忘记入口在哪里了,值是记得其中的一些重点了,今天写一篇博客,当作是笔记.和以前一样,关键代码,我会用红色标记出来. 首先,先列出我的jdk代理对象 ...
- jdk代理和cglib代理
1.jdk静态代理(静态代理和动态代理) 本质:在内存中构建出接口的实现类. 缺陷:只能对实现接口的类实现动态代理, 使用cglib可以对没有实现接口的类进行动态代理. 2.cglib动态代理 ...
- Spring中AOP的两种代理方式(Java动态代理和CGLIB代理)
第一种代理即Java的动态代理方式上一篇已经分析,在这里不再介绍,现在我们先来了解下GCLIB代理是什么?它又是怎样实现的?和Java动态代理有什么区别? cglib(Code Generation ...
- 设计模式---JDK动态代理和CGLIB代理
Cglig代理设计模式 /*测试类*/ package cglibProxy; import org.junit.Test; public class TestCglib { @Test public ...
- JDK动态代理和 CGLIB 代理
JDK动态代理和 CGLIB 代理 JDK动态代理:其代理对象必须是某个接口的实现,它是通过在运行期期间创建一个接口的实现类来完成对目标对象的代理. 代码示例 接口 public interface ...
- Java动态代理和CGLib代理
本文参考 在上一篇"Netty + Spring + ZooKeeper搭建轻量级RPC框架"文章中涉及到了Java动态代理和CGLib代理,在这篇文章中对这两种代理方式做详解 下 ...
- SpringAOP-JDK 动态代理和 CGLIB 代理
在 Spring 中 AOP 代理使用 JDK 动态代理和 CGLIB 代理来实现,默认如果目标对象是接口,则使用 JDK 动态代理,否则使用 CGLIB 来生成代理类. 1.JDK 动态代理 那么接 ...
随机推荐
- 牛客 203B tree(树形dp)
大意: 给定树, 对于每个节点, 求包含该节点的连通子集数. 显然有$dp[x]=\prod (dp[y]+1), ans[x]=(\frac{ans[fa[x]]}{dp[x]+1}+1)dp[x] ...
- [http]HTTP请求过程
我们在浏览器输入http://www.baidu.com想要进入百度首页,但是这是个域名,没法准确定位到服务器的位置,所以需要通过域名解析,把域名解析成对应的ip地址,然后通过ip地址查找目的主机.整 ...
- spring-cloud 学习三 服务提供者
基于spring-boot创建一个module提供服务 使用mysql数据库,dao使用mybatis,数据库连接池使用阿里的druid 添加maven依赖 <parent> <gr ...
- python+django学习三
在这个网站看https://sshwsfc.github.io/xadmin/ xadmin结果一堆的坑,文档找不到界面,dome登陆就报错permission denied for rela ...
- 异常:Invalid character found in the request target. The valid characters are defined in RFC 3986
一.背景 事情是这样的,前几天做一个基本的数据库“增删改查”的需求,前端传参的方式是“JSON字符串”,后端接收到此参数后,使用阿里巴巴fastjson进行解析,然后入库.需求很简单吧,但是偏偏遇到问 ...
- git冲突Pull is not possible because you have unmerged files
本地的push和merge会形成MERGE-HEAD(FETCH-HEAD), HEAD(PUSH-HEAD)这样的引用.HEAD代表本地最近成功push后形成的引用.MERGE-HEAD表示成功pu ...
- win10环境安装配置Nginx
前言: 参考 https://blog.csdn.net/kisscatforever/article/details/73129270 Nginx的应用场景 1. http服务器.Ngin ...
- Asp .Net Core 2.0 登录授权以及前后台多用户登录
用户登录是一个非常常见的应用场景 .net core 2.0 的登录方式发生了点变化,应该是属于是良性的变化,变得更方便,更容易扩展. 配置 打开项目中的Startup.cs文件,找到Configur ...
- Image Processing and Computer Vision_Review:Local Invariant Feature Detectors: A Survey——2007.11
翻译 局部不变特征探测器:一项调查 摘要 -在本次调查中,我们概述了不变兴趣点探测器,它们如何随着时间的推移而发展,它们如何工作,以及它们各自的优点和缺点.我们首先定义理想局部特征检测器的属性.接下来 ...
- linux 设备驱动与应用程序异步通知
一.异步通知机制简介 异步通知机制的意思:一旦设备准备就绪,可以主动的通知应用程序进行相应的操作,从而使得应用程序不必去查询设备的状态. 异步通知比较准确的称谓是"信号驱动的异步IO&quo ...