013 Enhancer创建动态代理
在上一章使用了Enhancer,没有仔细说明代理的问题,在这一章进行详细的说明。
原本想使用cf包的,后来发现使用framework包下的包也没有问题,程序中就一直使用framework中的类。
一:概述
1.说明
CGLIB是一个强大、高性能的字节码生成库,它用于在运行时扩展Java类和实现接口;
本质上它是通过动态的生成一个子类去覆盖所要代理的类(非final修饰的类和方法)。
Enhancer是一个非常重要的类,它允许为非接口类型创建一个JAVA代理,Enhancer动态的创建给定类的子类并且拦截代理类的所有的方法,和JDK动态代理不一样的是不管是接口还是类它都能正常工作。
2.回调接口
net.sf.cglib.proxy.Callback接口:在cglib包中是一个很关键的接口,所有被net.sf.cglib.proxy.Enhancer类调用的回调(callback)接口都要继承这个接口。
net.sf.cglib.proxy.MethodInterceptor接口: 是通用的回调(callback)类型,他经常被AOP用来实现拦截(intercept)方法的调用;
3.关于MethodInterceptor接口的源代码
是Callback的子接口,所以,实现这个接口的类可以用于回调。
package org.aopalliance.intercept; @FunctionalInterface
public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation var1) throws Throwable;
}
二:示例
1.结构
2.对象
package com.jun.web.enhancer; public class HelloWorld {
public String say(boolean say) throws Exception {
System.out.println("Hello Student");
if(!say) {
throw new Exception("回答错误!");
}
return "回答正确!";
}
}
3.回调
package com.jun.web.enhancer; import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class HelloWorldInterceptor implements MethodInterceptor {
/**
*
* 方法描述 当对基于代理的方法回调时,在调用原方法之前会调用该方法
* 拦截对目标方法的调用
*
* @param obj 代理对象
* @param method 拦截的方法
* @param args 拦截的方法的参数
* @param proxy 代理
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Object result = null;
try {
//前置通知
before();
result = proxy.invokeSuper(obj, args);
//后置通知
after();
} catch (Exception e) {
//异常通知
exception();
} finally {
//方法返回前通知
beforeReturning();
} return result;
} private void before() {
System.out.println("before method invoke...");
}
private void after() {
System.out.println("after method invoke...");
}
private void exception() {
System.out.println("exception method invoke...");
}
private void beforeReturning() {
System.out.println("beforeReturning method invoke...");
}
}
4.代理
package com.jun.web.enhancer; import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; //创建Enhancer增强代理类的回调ProxyFactory类
public class ProxyFactory { //要代理的真实对象
private Object obj; public Object createProxy(Object target) {
Enhancer enhancer = new Enhancer();
//设置代理目标
enhancer.setSuperclass(target.getClass());
//设置单一回调对象,在调用中拦截对目标方法的调用
enhancer.setCallback(new HelloWorldInterceptor());
//设置类加载器
enhancer.setClassLoader(target.getClass().getClassLoader()); return enhancer.create();
} }
5.测试
package com.jun.web.enhancer; public class EnhancerTest {
public static void main(String[] args) throws Exception {
//将要被代理的对象
HelloWorld hello = new HelloWorld();
//代理
ProxyFactory proxy = new ProxyFactory();
//
HelloWorld world = (HelloWorld)proxy.createProxy(hello);
String result = world.say(false);
System.out.println(result);
}
}
6.效果
Connected to the target VM, address: '127.0.0.1:60279', transport: 'socket'
before method invoke...
Hello Student
exception method invoke...
beforeReturning method invoke...
null
Disconnected from the target VM, address: '127.0.0.1:60279', transport: 'socket'
三:其他回调
1.FixedValue
net.sf.cglib.proxy.FixedValue:为提高性能,FixedValue回调对强制某一特定方法返回固定值。
package com.jun.web.enhancer; import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.FixedValue; public class FixedValueTest {
public static void main(String[] args) throws Exception {
HelloWorld hello = new HelloWorld();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(hello.getClass());
//设置单一回调对象,在调用中拦截对目标方法的调用
enhancer.setCallback(new FixedValue() {
@Override
public Object loadObject() throws Exception {
return "FixedValue";
}
});
//设置类加载器
enhancer.setClassLoader(hello.getClass().getClassLoader()); HelloWorld world = (HelloWorld)enhancer.create();
String result = world.say(false);
System.out.println(result);
}
}
2.效果
Connected to the target VM, address: '127.0.0.1:60379', transport: 'socket'
Disconnected from the target VM, address: '127.0.0.1:60379', transport: 'socket'
FixedValue Process finished with exit code 0
3.NoOp
net.sf.cglib.proxy.NoOp:NoOp回调把对方法调用直接委派到这个方法在父类中的实现(也可以理解成真实对象直接调用方法);
package com.jun.web.enhancer; import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.NoOp; public class NoopTest {
public static void main(String[] args) throws Exception {
HelloWorld hello = new HelloWorld();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(hello.getClass());
enhancer.setCallback(NoOp.INSTANCE);
enhancer.setClassLoader(hello.getClass().getClassLoader());
HelloWorld obj = (HelloWorld)enhancer.create();
System.out.println(obj.say(true));
}
}
4.效果
Disconnected from the target VM, address: '127.0.0.1:60418', transport: 'socket'
Hello Student
回答正确! Process finished with exit code 0
5.其他的回调
net.sf.cglib.proxy.LazyLoader:当实际的对象需要延迟装载时,可以使用LazyLoader回调。一旦实际对象被装载,它将被每一个调用代理对象的方法使用;
net.sf.cglib.proxy.Dispatcher:Dispathcer回调和LazyLoader回调有相同的特点,不同的是,当代理方法被调用时,装载对象的方法也总要被调用;
net.sf.cglib.proxy.ProxyRefDispatcher:ProxyRefDispatcher回调和Dispatcher一样,不同的是,它可以把代理对象作为装载对象方法的一个参数传递;
四:CallbackFilter回调
1.说明
net.sf.cglib.proxy.CallbackFilter允许我们在方法层设置回调(callback),根据我们对方法处理的需求设置不同的回调;如下有一个类Hello,里面有两个方法save和update,save方法需要做前置和后置处理,但是update方法不需要:
2.Hello
package com.jun.web.enhancer; public class Hello {
public String save() {
System.out.println("save...");
return "save";
}
public String update() {
System.out.println("update...");
return "update";
}
}
3.
package com.jun.web.enhancer; import org.springframework.cglib.proxy.CallbackFilter; import java.lang.reflect.Method; public class CallBackFilterTest implements CallbackFilter {
/**
* 方法返回的值是和callback回调接口数组一一对应的数组下标
*/
@Override
public int accept(Method method) {
String name = method.getName();
if("save".equals(name)) {
return 0;
}
return 1;
}
}
4.测试
package com.jun.web.enhancer; import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.NoOp; public class CallbackFilterMainTest {
public static void main(String[] args) throws Exception {
Hello hello = new Hello();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(hello.getClass());
enhancer.setCallbackFilter(new CallBackFilterTest());
//创建各个目标代理方法的回调,回调的顺序要与过滤器索引一致
Callback[] callbacks = new Callback[] {new HelloWorldInterceptor(), NoOp.INSTANCE};
//设置单一回调对象,在调用中拦截对目标方法的调用
enhancer.setCallbacks(callbacks);
Hello obj = (Hello)enhancer.create(); System.out.println(obj.update());
System.out.println("=============");
System.out.println(obj.save());
}
}
5.效果
Connected to the target VM, address: '127.0.0.1:60873', transport: 'socket'
update...
update
=============
before method invoke...
Disconnected from the target VM, address: '127.0.0.1:60873', transport: 'socket'
save...
after method invoke...
beforeReturning method invoke...
save Process finished with exit code 0
013 Enhancer创建动态代理的更多相关文章
- Spring BPP中优雅的创建动态代理Bean
一.前言 本文章所讲并没有基于Aspectj,而是直接通过Cglib以及ProxyFactoryBean去创建代理Bean.通过下面的例子,可以看出Cglib方式创建的代理Bean和ProxyFact ...
- 18.5.1使用Proxy和InvocationHandler创建动态代理
package d18_5_1; public interface Person { void walk(); void sayHello(String name); } package d18_5_ ...
- Java动态代理与Cglib库
JDK动态代理 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在 ...
- java动态代理浅析
最近在公司看到了mybatis与spring整合中MapperScannerConfigurer的使用,该类通过反向代理自动生成基于接口的动态代理类. 于是想起了java的动态代理,然后就有了这篇文章 ...
- java的静态代理和动态代理(jdk、cglib)
一.代理模式 代理的概念来自于设计模式中的代理模式,先了解一下代理模式 1.结构图 2.参与者 Subject:接口,定义代理类和实际类的共用接口 RealSubject:实际类,实现Subject这 ...
- 代理模式 & Java原生动态代理技术 & CGLib动态代理技术
第一部分.代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常 ...
- Spring AOP详解 、 JDK动态代理、CGLib动态代理
AOP是Aspect Oriented Programing的简称,面向切面编程.AOP适合于那些具有横切逻辑的应用:如性能监测,访问控制,事务管理以及日志记录.AOP将这些分散在各个业务逻辑中的代码 ...
- java 笔记(3) —— 动态代理,静态代理,cglib代理
0.代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口. 代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等. 代理类与委托类之间通常会存 ...
- JDK动态代理与Cglib库
JDK动态代理 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在 ...
随机推荐
- opencv图像阈值操作
使用threshold方法和adaptivethreshold方法对图像进行阈值分割操作. 1.使用threshold方法,设置一个阈值,将大于阈值的值变换为最大值,小于阈值的值变换为0. #-*- ...
- kubernetes里面的GC--转发
什么是GC GC 是 Garbage Collector 的简称.从功能层面上来说,它和编程语言当中的「GC」 基本上是一样的.它清理 Kubernetes 中「符合特定条件」的 Resource O ...
- Python +appium baseview
封装python+appium 的baseview模块 from selenium.webdriver.support.ui import WebDriverWait from time import ...
- docker学习3-镜像的基本使用
前言 Docker的三大核心概念:镜像.容器.仓库.初学者对镜像和容器往往分不清楚,学过面向对象的应该知道类和实例,这跟面向对象里面的概念很相似 我们可以把镜像看作类,把容器看作类实例化后的对象. d ...
- 【云栖社区001-数据结构】如何实现一个高效的单向链表逆序输出(Java版)
如题 动手之前,发现自己很擅长用C语言来写链表. 不过,既然自己做的是Java开发,那么还是用Java实现这个算法吧:毕竟,以后的若干年里都差不多要跟Java打交道了. 于是,先将Java版的链表自学 ...
- python开发应用笔记-SciPy扩展库使用
SciPy https://www.scipy.org/ SciPy中的数据结构: 1.ndarray(n维数组) 2.Series(变长字典) 3.DataFrame(数据框) NumPy适合于线性 ...
- 为什么需要 Redis 哨兵?
在说哨兵之前,我们先说下主从复制,Redis 的主从复制模式,一旦主节点出现故障无法提供服务,需要人工介入手工将从节点调整为主节点,同时应用端还需要修改新的主节点地址,这种故障转移的方式对于很多应用场 ...
- linux中的alias命令详解
功能说明:设置指令的别名.语 法:alias[别名]=[指令名称]参 数 :若不加任何参数,则列出目前所有的别名设置.举 例 :ermao@lost-desktop:~$ alias ...
- 按键精灵PC端脚本
定义变量的时候不需要定义类型 ,由于是易语言,变量名可以是中文 文本路径 = "C:\Users\Administrator\Desktop\1.txt"//改成自己的文本路径 T ...
- lambda()函数
lambda lambda原型为:lambda 参数:操作(参数) lambda函数也叫匿名函数,即没有具体名称的函数,它允许快速定义单行函数,可以用在任何需要函数的地方.这区别于def定义的函数. ...