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设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在 ...
随机推荐
- python私有工具库小结
1.一些试用py工具清单 https://www.zhihu.com/question/60402355/answer/752917744?utm_source=wechat_session& ...
- MySQL高可用架构应该考虑什么? 你认为应该如何设计?
一.MySQL高可用架构应该考虑什么? 对业务的了解,需要考虑业务对数据库一致性要求的敏感程度,切换过程中是否有事务会丢失 对于基础设施的了解,需要了解基础设施的高可用的架构.例如 单网线,单电源等情 ...
- docker 安装prometheus
使用到的命令: [root@lgswork ~]# docker search prometheus NAME DESCRIPTION STARS OFFICIAL AUTOMATED prom/pr ...
- 大数据集群环境 zookeeper集群环境安装
大数据集群环境 zookeeper集群环境准备 zookeeper集群安装脚本,如果安装需要保持zookeeper保持相同目录,并且有可执行权限,需要准备如下 编写脚本: vi zkInstall.s ...
- Synchronized关键字和锁升级,详细分析偏向锁和轻量级锁的升级
原文链接:https://blog.csdn.net/tongdanping/article/details/79647337 1.锁升级锁的4中状态:无锁状态.偏向锁状态.轻量级锁状态.重量级锁状态 ...
- python3中用HTMLTestRunner.py报ImportError: No module named 'StringIO'的解决方法:
全文转载至:http://www.cnblogs.com/testyao/p/5658200.html python3中用HTMLTestRunner.py报ImportError: No modul ...
- c++输出中文乱码解决方案
问题的原因应该在cmd的编码和c++程序编码(源文件编码)的不同.cmd默认的是gbk编码,而我用的vs code默认是utf-8编码,因而在输出中文文本时会出现乱码. 但我也遇到了一个比较怪异的情况 ...
- 安卓Termux安装ssh及jupyter编程
软件名称:Termux ssh安装 安装openssh apt update apt install openssh 启动ssh服务 sshd 配置公钥私钥 将电脑的公钥(id_rsa.pub)放入/ ...
- C#使用ODP.NET(Oracle.ManagedDataAccess.dll)操作Oracle数据库
在刚接触C#的时候由于公司使用的就是Oracle数据库,那么C#怎么连接Oracle数据库就成了首要去掌握的知识点了.在那时没有ODP.NET,但visual studio却对Oralce数据库的调用 ...
- hak的使用
autohotkey简称ahk 它是一款轻量级的脚本语言文件,它可以干任何事情,如做dnf的连发脚本,类似按键精灵的自动化点击,按键自动打开文件一系列事情,文件需要按照ahk自己的语言,实现自定义的脚 ...