cglib用法
CGLib动态代理的介绍及用法(单回调、多回调、不处理、固定值、懒加载)
参照: https://blog.csdn.net/difffate/article/details/70552056
前面介绍了代理模式,讲了动态代理常见的实现方式,包含了JDK的动态代理和CGLib的动态代理。本文将介绍下CGLib动态代理及几种用法。CGLib(Code Generation Library)是一个高效的代码生成库,底层实现是使用asm来转换字节码生成类。在生成代理类的场景中,由于JDK动态代理必须要求源对象有实现接口,而实际场景中,并不是所有类都有实现接口,因此使用CGLib可以用在未实现接口的类上。
值得注意的几点是:
1)使用CGLib代理的类不能是final修饰的,因为代理类需要继承主题类;
2)final修饰的方法不会被切入;
3)如果主题类的构造函数不是默认空参数的,那么在使用Enhancer类create的时候,选择create(java.lang.Class[] argumentTypes, java.lang.Object[] arguments) 方法。
接下来认识实现动态代理最重要的一个接口 MethodInteceptor
- package net.sf.cglib.proxy;
- /**
- * General-purpose {@link Enhancer} callback which provides for "around advice".
- * @author Juozas Baliuka <a href="mailto:baliuka@mwm.lt">baliuka@mwm.lt</a>
- * @version $Id: MethodInterceptor.java,v 1.8 2004/06/24 21:15:20 herbyderby Exp $
- */
- public interface MethodInterceptor
- extends Callback
- {
- /**
- * All generated proxied methods call this method instead of the original method.
- * The original method may either be invoked by normal reflection using the Method object,
- * or by using the MethodProxy (faster).
- * @param obj "this", the enhanced object
- * @param method intercepted Method
- * @param args argument array; primitive types are wrapped
- * @param proxy used to invoke super (non-intercepted method); may be called
- * as many times as needed
- * @throws Throwable any exception may be thrown; if so, super method will not be invoked
- * @return any value compatible with the signature of the proxied method. Method returning void will ignore this value.
- * @see MethodProxy
- */
- public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
- MethodProxy proxy) throws Throwable;
- }
MethodInterceptor,从名字上方法拦截器,就是对方法做切入的。intercept方式的4个参数分别对应增强对象、调用方法、方法参数以及调用父类方法的代理。使用MethodProxy速度会更快,所以后面将用
下面介绍几种用法,这里使用spring包中cglib,其实和引单独的cglib包是一样,只不过spring为了版本不冲突,将cglib包含在自己的包中。
先定义一个主题对象
- /**
- * Create by zxb on 2017/4/23
- */
- public class DBQuery {
- public DBQuery() {
- }
- public DBQuery(Integer i) {
- System.out.println("Here's in DBQuery Constructor");
- }
- public String getElement(String id) {
- return id + "_CGLib";
- }
- public List<String> getAllElements() {
- return Arrays.asList("Hello_CGLib1", "Hello_CGLib2");
- }
- public String methodForNoop() {
- return "Hello_Noop";
- }
- public String methodForFixedValue(String param) {
- return "Hello_" + param;
- }
- public final String sayHello() {
- return "Hello Everyone!";
- }
- }
(一)单回调
切入类:
- /**
- * Create by zxb on 2017/4/22
- */
- public class DBQueryProxy implements MethodInterceptor {
- public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
- System.out.println("Here in interceptor !");
- return methodProxy.invokeSuper(o, objects);
- }
- }
测试类:
- public class TestCGLibProxy {
- public static void main(String[] args) {
- DBQueryProxy dbQueryProxy = new DBQueryProxy();
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(DBQuery.class);
- enhancer.setCallback(dbQueryProxy);
- // DBQuery dbQuery = (DBQuery)enhancer.create(new Class[]{Integer.class}, new Object[]{1});
- DBQuery dbQuery = (DBQuery) enhancer.create();
- System.out.println(dbQuery.getElement("Hello"));
- System.out.println();
- System.out.println(dbQuery.getAllElements());
- System.out.println();
- System.out.println(dbQuery.sayHello());
- }
- }
执行结果:
(二)多回调
在前面的基础上,加个切入类,并通过CallbackFilter来决定是使用哪个切入类
- /**
- * Create by zxb on 2017/4/22
- */
- public class DBQueryProxy2 implements MethodInterceptor {
- public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
- System.out.println("Here in interceptor 2!");
- return methodProxy.invokeSuper(o, objects);
- }
- }
测试类:
- /**
- * Create by zxb on 2017/4/22
- */
- public class TestCGLibProxy {
- public static void main(String[] args) {
- DBQueryProxy dbQueryProxy = new DBQueryProxy();
- DBQueryProxy2 dbQueryProxy2 = new DBQueryProxy2();
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(DBQuery.class);
- enhancer.setCallbacks(new Callback[]{dbQueryProxy, dbQueryProxy2});
- enhancer.setCallbackFilter(new CallbackFilter() {
- public int accept(Method method) {
- if (method.getName().equals("getElement")) {
- return 0;
- } else {
- return 1;
- }
- }
- });
- DBQuery dbQuery = (DBQuery) enhancer.create();
- System.out.println("========Inteceptor By DBQueryProxy ========");
- System.out.println(dbQuery.getElement("Hello"));
- System.out.println();
- System.out.println("========Inteceptor By DBQueryProxy2 ========");
- System.out.println(dbQuery.getAllElements());
- }
- }
执行结果:
(三)不处理
利用枚举常量 Callback noopCb = NoOp.INSTANCE;
测试类:
- public class TestCGLibProxy {
- public static void main(String[] args) {
- DBQueryProxy dbQueryProxy = new DBQueryProxy();
- DBQueryProxy2 dbQueryProxy2 = new DBQueryProxy2();
- Callback noopCb = NoOp.INSTANCE;
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(DBQuery.class);
- enhancer.setCallbacks(new Callback[]{dbQueryProxy, dbQueryProxy2, noopCb});
- enhancer.setCallbackFilter(new CallbackFilter() {
- public int accept(Method method) {
- if (method.getName().equals("getElement")) {
- return 0;
- } else if (method.getName().equals("getAllElements")) {
- return 1;
- } else {
- return 2;
- }
- }
- });
- DBQuery dbQuery = (DBQuery) enhancer.create();
- System.out.println("========Inteceptor By DBQueryProxy ========");
- System.out.println(dbQuery.getElement("Hello"));
- System.out.println();
- System.out.println("========Inteceptor By DBQueryProxy2 ========");
- System.out.println(dbQuery.getAllElements());
- System.out.println();
- System.out.println("========Return Original Value========");
- System.out.println(dbQuery.methodForNoop());
- }
- }
执行结果:
(四)固定值
需要实现FixedValue接口,会忽略原来函数的返回值,使用固定值来替换。
- /**
- * 返回固定的值
- * Create by zxb on 2017/4/23
- */
- public class DBQueryProxyFixedValue implements FixedValue {
- public Object loadObject() throws Exception {
- System.out.println("Here in DBQueryProxyFixedValue ! ");
- return "Fixed Value";
- }
- }
测试类:
- public class TestCGLibProxy {
- public static void main(String[] args) {
- DBQueryProxy dbQueryProxy = new DBQueryProxy();
- DBQueryProxy2 dbQueryProxy2 = new DBQueryProxy2();
- Callback noopCb = NoOp.INSTANCE;
- Callback fixedValue = new DBQueryProxyFixedValue();
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(DBQuery.class);
- enhancer.setCallbacks(new Callback[]{dbQueryProxy, dbQueryProxy2, noopCb, fixedValue});
- enhancer.setCallbackFilter(new CallbackFilter() {
- public int accept(Method method) {
- if (method.getName().equals("getElement")) {
- return 0;
- } else if (method.getName().equals("getAllElements")) {
- return 1;
- } else if (method.getName().equals("methodForNoop")) {
- return 2;
- } else if (method.getName().equals("methodForFixedValue")) {
- return 3;
- } else {
- return 0;
- }
- }
- });
- DBQuery dbQuery = (DBQuery) enhancer.create();
- System.out.println("========Inteceptor By DBQueryProxy ========");
- System.out.println(dbQuery.getElement("Hello"));
- System.out.println();
- System.out.println("========Inteceptor By DBQueryProxy2 ========");
- System.out.println(dbQuery.getAllElements());
- System.out.println();
- System.out.println("========Return Original Value========");
- System.out.println(dbQuery.methodForNoop());
- System.out.println();
- System.out.println("========Return Fixed Value========");
- System.out.println(dbQuery.methodForFixedValue("myvalue"));
- }
- }
执行结果:
(五)懒加载
CGLib的懒加载,可以用在一些不需要立即加载完整对象实例的场景,比如说Hibernate中的查询对象,如果这个对象有关联其他对象,这个时候不会马上将关联对象一起查询出来关联,要等到调用到这个关联对象时才去做查询。利用CGLib的懒加载机制,可以很好的实现这个需求。需要了解2个接口,LazyLoader和Dispatcher。这两个接口的定义如下:
- public interface LazyLoader extends Callback {
- Object loadObject() throws Exception;
- }
- public interface Dispatcher extends Callback {
- Object loadObject() throws Exception;
- }
它们都继承了Callback接口,都有一个loadObject的方法,区别在于LazyLoader只有在第一次调用时,会执行loadObject获取对象,而Dispatcher会在每次调用时都触发loadObject方法,不理解?没关系,后面代码示例上可以看到明显的区别。假定有个学生类(Student),学术类包含2门课的课程表对象,分别是英语课程表(EnglishSchedule)和数学课程表(MathSchedule),它们都是课程表类的实例
- /**
- * 课程表
- * Create by zxb on 2017/4/23
- */
- @Data
- @NoArgsConstructor
- @AllArgsConstructor
- public class Schedule {
- private String courseName;
- private Date courseTime;
- }
EnglishSchedule属性的加载依赖于ScheduleLazyLoader
- /**
- * Create by zxb on 2017/4/23
- */
- public class ScheduleLazyLoader implements LazyLoader {
- public Object loadObject() throws Exception {
- System.out.println("before LazyLoader init...you can query from db...");
- Schedule schedule = new Schedule();
- schedule.setCourseName("English");
- Calendar calendar = Calendar.getInstance();
- calendar.set(2017,3,28);
- schedule.setCourseTime(calendar.getTime());
- System.out.println("after LazyLoader init...");
- return schedule;
- }
- }
MathSchedule属性的加载依赖于ScheduleDispatcher
- /**
- * Create by zxb on 2017/4/23
- */
- public class ScheduleDispatcher implements Dispatcher {
- public Object loadObject() throws Exception {
- System.out.println("before Dispatcher init...you can query from db...");
- Schedule schedule = new Schedule();
- schedule.setCourseName("Math");
- Calendar calendar = Calendar.getInstance();
- calendar.set(2017,4,1);
- schedule.setCourseTime(calendar.getTime());
- System.out.println("after Dispatcher init...");
- return schedule;
- }
- }
学生类:
定义时,需要对EnglishSchedule和MathSchedule先初始为动态代理的对象
- package org.zheng.proxy.cglib.lazyload;
- import lombok.Data;
- import org.springframework.cglib.proxy.Enhancer;
- /**
- * Create by zxb on 2017/4/23
- */
- @Data
- public class Student {
- private int id;
- private String name;
- /**
- * 英语课时间表
- */
- private Schedule EnglishSchedule;
- /**
- * 数学课时间表
- */
- private Schedule MathSchedule;
- public Student(int id, String name) {
- this.id = id;
- this.name = name;
- this.EnglishSchedule = createEnglishSchedule();
- this.MathSchedule = createMathSchedule();
- }
- private Schedule createEnglishSchedule() {
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(Schedule.class);
- enhancer.setCallback(new ScheduleLazyLoader());
- return (Schedule) enhancer.create();
- }
- private Schedule createMathSchedule() {
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(Schedule.class);
- enhancer.setCallback(new ScheduleDispatcher());
- return (Schedule) enhancer.create();
- }
- }
测试类:
- /**
- * 延迟加载属性
- * Create by zxb on 2017/4/23
- */
- public class LazyLoadTest {
- public static void main(String[] args) {
- Student student = new Student(666, "XiaoMing");
- System.out.println("id=" + student.getId());
- System.out.println("name=" + student.getName());
- // LazyLoader 只有第一次,Dispatcher是每次都会进loadObject的方法
- System.out.println("========First Get EnglishSchedule ========");
- System.out.println(student.getEnglishSchedule());
- System.out.println();
- System.out.println("========First Get MathSchedule ========");
- System.out.println(student.getMathSchedule());
- System.out.println();
- System.out.println("========Second Get EnglishSchedule ========");
- System.out.println(student.getEnglishSchedule());
- System.out.println();
- System.out.println("========Second Get MathSchedule ========");
- System.out.println(student.getMathSchedule());
- }
- }
执行结果:
可以看到第二次取懒加载对象的时候,实现LoadLazy接口不会重新执行loadObject,而实现Dispatcher的会重新执行LoadObject方法:)
以上,就是使用CGLib的几种常见用法。
项目完整代码:
https://github.com/difffate/JavaProject
cglib用法的更多相关文章
- Spring-core中的cglib小用法
对象复制听说用这个更高效 /** * 拷贝对象 * @param src 源对象 * @param dist 需要赋值的对象 */ public static void copy(Object src ...
- Spring AOP 实现原理与 CGLIB 应用
https://www.ibm.com/developerworks/cn/java/j-lo-springaopcglib/ AOP(Aspect Orient Programming),也就是面向 ...
- Spring AOP 实现原理与 CGLIB 应用--转
AOP(Aspect Orient Programming),作为面向对象编程的一种补充,广泛应用于处理一些具有横切性质的系统级服务,如事务管理.安全检查.缓存.对象池管理等.AOP 实现的关键就在于 ...
- Spring-AOP用法总结
前言 Spring AOP的实现方法很多,在项目开发中具体采用什么方式,需要按实际情况来选择,每一种的用法,有其一定的实用价值,所以本文将各种使用方法进行了具体实现.主要包括Advice的be ...
- Cglib及其基本使用
前言 最近一直在看Spring源码,其实我之前一直知道AOP的基本实现原理: 如果针对接口做代理默认使用的是JDK自带的Proxy+InvocationHandler 如果针对类做代理使用的是Cgli ...
- 使用CGlib实现Bean拷贝(BeanCopier)
在做业务的时候,我们有时为了隔离变化,会将DAO查询出来的Entity,和对外提供的DTO隔离开来.大概90%的时候,它们的结构都是类似的,但是我们很不喜欢写很多冗长的b.setF1(a.getF1( ...
- 框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)
一.AOP的核心概念回顾 https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#a ...
- cglib之Enhancer
1. 背景 cglib库的Enhancer在Spring AOP中作为一种生成代理的方式被广泛使用.本文针对Enhancer的用法以实际代码为例作一些介绍. 2. Enhancer是啥 Enhance ...
- Spring AOP切点表达式用法总结
1. 简介 面向对象编程,也称为OOP(即Object Oriented Programming)最大的优点在于能够将业务模块进行封装,从而达到功能复用的目的.通过面向对象编程,不同的模 ...
随机推荐
- 两个map合并
两个map合并所用函数为:putAll package myProject; import java.util.HashMap; import java.util.Map; public class ...
- Ninject 2.x细说---2.绑定和作用域
Ninject 2.x细说---2.绑定和作用域 转载weixin_33725272 最后发布于2011-11-06 00:03:00 阅读数 9 收藏 Ninject中提供多种接口和实现类的绑 ...
- go-redis 基于beego正确使用序列化存储数据和反序列化获取数据
安装go-redis // 安装命令 go get github.com/gomodule/redigo/redis // 导入使用 import( "github.com/gomodule ...
- 查看ie版本
window10系统还好说,一般都是IE11版本,其他系统或服务器看=查看ie版本就很烦 方法一:按组合键 ALT+H 打开“帮助”,再按 A 选择“关于Internet Explorer” 方法二 ...
- Apache Kafka(五)- Safe Kafka Producer
Kafka Safe Producer 在应用Kafka的场景中,需要考虑到在异常发生时(如网络异常),被发送的消息有可能会出现丢失.乱序.以及重复消息. 对于这些情况,我们可以创建一个“safe p ...
- [刷题] Leetcode算法 (2020-2-27)
1.最后一个单词的长度(很简单) 题目: 给定一个仅包含大小写字母和空格 ' ' 的字符串 s,返回其最后一个单词的长度. 如果字符串从左向右滚动显示,那么最后一个单词就是最后出现的单词. 如果不存在 ...
- sublime 最常用的快捷键.gif
Ctrl+D 把光标放在文本上,按下⌘+ D将选择这个文本.多次按下⌘+ D则会增加匹配项 Alt+F3 会选中光标所在文本的所有匹配项 Ctrl+Shift+' 这是一个法宝,也许你希望所有的属性保 ...
- 【做题笔记】P2871 [USACO07DEC]手链Charm Bracelet
就是 01 背包.大意:给您 \(T\) 个空间大小的限制,有 \(M\) 个物品,第 \(i\) 件物品的重量为 \(c_i\) ,价值为 \(w_i\) .要求挑选一些物品,使得总空间不超过 \( ...
- bugku 输入密码查看flag
首先进入网页会看到一个网页 然后用bp进行爆破 首先点击clear 然后选中刚下随便输入进去的密码点击add然后设置一下类型 然后进行开始攻击 攻击过程中点击length 根据长度可以判断出密码然后回 ...
- AcWing 871. 约数之和
#include <iostream> #include <algorithm> #include <unordered_map> #include <vec ...