java代理的深入浅出(二)-CGLIB
java代理的深入浅出(二)-CGLIB
1.基本原理
CGLIB的原理就是生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中拦截所有父类方法的调用,拦截下来交给设置的MethodInterceptor去执行。 由于是采用继承来实现的代理,所以不能对final修饰的类进行代理,其它都可以代理。
子类的生成它采用了非常底层的字节码技术(ASM节码处理框架),转换字节码来完成。它要求必须对JVM内部结构包括class文件的格式和指令集都很熟悉。
代理一个类一般会动态生成三个类
第一个是生成的新子类,对原始类里每一个方法都会在动态的子类里生成一个对应的MethodProxy。
另外两个动态生成的FastClass类,一个是针对应原始类的方法生成方法索引表,一个针对应新生成子类的方法生成方法索引表。这2个额外生成的类主要作用在于当我们调用一个方法时,不通过反射来调用,而是通过类似于数组下标的方式来定位方法,直接进行类方法的执行。因此CGLib创建的动态代理对象性能比JDK创建的动态代理对象的性能高不少,但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。
2.核心类
cglib主要包含4个概念,BeanGenerator、Enhancer、MethodInterceptor、LazyLoader、Dispatcher。
beangenerator主要用于动态生成一个类的子类,可以给子类动态添加一些成员变量,自动生成Getter、Setter方法。缺点是它只能生成含默认构造函数的子类。
BeanGenerator gen = BeanGenerator();
gen.setSuperclass(SuperClass.class);
gen.addProperty("name", String.class);
Class subClazz = (Class)gen.createClass();
SuperClass obj = (SuperClass)gen.create();
enhancer用于实现某个方法调用的aop功能。enhancer生成对象会包含很多cglib自动添加进去的属性,所以最后生成的对象会比较大。
MethodInterceptor
public class MethodInterceptorImpl implements MethodInterceptor{
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
return proxy.invokeSuper(obj, args);
}
}
//代理invoke方法
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallbacks(new Callback[]{NoOp.INSTANCE, new MethodInterceptorImpl()});
enhancer.setCallbackFilter(new CallbackFilter() {
@Override
public int accept(Method method) {
//只拦截Algorithm接口定义的invoke方法
if(method.getName().equals("invoke"))
return 1;
return 0;
}
});
enhancer.setUseFactory(false);
//new 出对象
Object proxy = enhancer.create();
lazyLoader是cglib的代理延迟初始化接口。当接口方法被第一次调用时,才确定实际要访问的对象。
public class ConcreteClassLazyLoader implements LazyLoader{
public class PropertyBean {
private String propertyName;
public String getPropertyName() {
return propertyName;
}
public void setPropertyName(String propertyName) {
this.propertyName = propertyName;
}
}
@Override
public Object loadObject() throws Exception {
System.out.println("LazyLoader loadObject() ...");
PropertyBean bean=new PropertyBean();
bean.setPropertyName("lazy-load object propertyName!");
return bean;
}
public static void main(String[] args){
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(PropertyBean.class);
PropertyBean propertyBean = (PropertyBean)enhancer.create(PropertyBean.class,new ConcreteClassLazyLoader());
//此处会回调loadObject
System.out.println(propertyBean.getPropertyName());
System.out.println("after...");
//之后不再回调loadObejct,直接访问第一次返回的对象
System.out.println(propertyBean.getPropertyName());
}
}
Dispatcher功能与LazyLoader相同,只是dispatcher每次都会被回调。
public class ConcreteDispatcher implements Dispatcher{
public class PropertyBean {
private String propertyName;
public String getPropertyName() {
return propertyName;
}
public void setPropertyName(String propertyName) {
this.propertyName = propertyName;
}
}
@Override
public Object loadObject() throws Exception {
System.out.println("Dispatcher loadObject() ...");
PropertyBean bean=new PropertyBean();
bean.setPropertyName("Dispatcher object propertyName!");
return bean;
}
public static void main(String[] args){
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(PropertyBean.class);
PropertyBean propertyBean = (PropertyBean)enhancer.create(PropertyBean.class,new ConcreteDispatcher());
//此处会回调loadObject
System.out.println(propertyBean.getPropertyName());
System.out.println("after...");
//每次都回调loadObejct
System.out.println(propertyBean.getPropertyName());
}
}
3.示例
目标类 CrudServiceImpl
public class CrudServiceImpl implements CrudService {
@Override
public Long create(String content) {
System.out.println("create......");
return 1L;
}
@Override
public List<String> retrieve(String condition) {
System.out.println("retrieve......");
return null;
}
@Override
public boolean update(Long id, String content) {
System.out.println("update......");
return true;
}
@Override
public boolean delete(Long id) {
System.out.println("delete......");
return false;
}
}
拦截处理类
public class MyMethodIntercepter implements MethodInterceptor{
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("brfore1 cglib invoke");
Object result = proxy.invokeSuper(obj, args);
System.out.println("after2 cglib invoke");
return result;
}
}
回调过滤类
public class ConcreteClassCallbackFilter implements CallbackFilter {
@Override
public int accept(Method method) {
if(method.getName().equals("create")){
return 1;
}
return 0;
}
}
生成动态代理类
public class TestCglib {
public static void main(String[] args){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(CrudServiceImpl.class);
enhancer.setCallbacks(new Callback[]{NoOp.INSTANCE,new MyMethodIntercepter()});
enhancer.setCallbackFilter(new ConcreteClassCallbackFilter());
CrudService proxy = (CrudService)enhancer.create();
System.out.println("proxy class name>>>>>"+proxy.getClass().getName());
System.out.println(proxy.create("ddd"));
System.out.println("==============================");
System.out.println(proxy.delete(1L));
}
}
反编译动态代理类
用agent(premain)将加载类导出到指定目录下,加下面的代码打包到jar里,增加MANIFEST.INF描述内容
Can-Redefine-Classes: true
Premain-Class: com.longchao.agent.CustomAgent
在程序启动的vm参数里加agent参数,-javaagent:D:/work/code/middleware/study/agent.jar
public class CustomAgent implements ClassFileTransformer {
public static void premain(String agentArgs, Instrumentation inst) {
inst.addTransformer(new CustomAgent());
}
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
if (!className.startsWith("java") && !className.startsWith("sun")) {
// 既不是java也不是sun开头的
// 导出代码
int lastIndexOf = className.lastIndexOf("/") + 1;
String fileName = className.substring(lastIndexOf) + ".class";
exportClazzToFile("d:/aop/", fileName, classfileBuffer);
System.out.println(className + " --> EXPORTED Succeess!");
}
return classfileBuffer;
}
/**
* @param dirPath 目录以/结尾,且必须存在
* @param fileName
* @param data
*/
private void exportClazzToFile(String dirPath, String fileName, byte[] data) {
try {
File file = new File(dirPath + fileName);
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream fos = new FileOutputStream(file);
fos.write(data);
fos.close();
} catch (Exception e) {
System.out.println("exception occured while doing some file operation");
e.printStackTrace();
}
}
}
4.总结
BeanGenerator适合给子类加成员变量
MethodInterceptor 适合做方法拦截
LazyLoader、Dispatcher适合做对象路由
ps:代码的地址:https://github.com/zhulongchao/proxy.git
java代理的深入浅出(二)-CGLIB的更多相关文章
- java代理的深入浅出(一)-Proxy
java代理的深入浅出(一)-Proxy 1.什么是代理 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事 ...
- Java 代理模式 (二) 动态代理
代理模式 代理(Proxy)是一种设计模式, 提供了对目标对象另外的访问方式:即通过代理访问目标对象. 这样好处: 可以在目标对象实现的基础上,增强额外的功能操作.(扩展目标对象的功能). 代理模式的 ...
- Java 代理模式(二) Java中的动态代理
动态代理类 Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类: 1.Interface InvocationHandler 该接口中仅定义了一个方法: Objec ...
- java代理的深入浅出(三)-JavaAssist,ASM
简介 类似字节码操作方法还有ASM.几种动态编程方法相比较,在性能上Javassist高于反射,但低于ASM,因为Javassist增加了一层抽象.在实现成本上Javassist和反射都很低,而ASM ...
- 浅谈Java代理二:Cglib动态代理-MethodInterceptor
浅谈Java代理二:Cglib动态代理-MethodInterceptor CGLib动态代理特点: 使用CGLib实现动态代理,完全不受代理类必须实现接口的限制,而且CGLib底层采用ASM字节码生 ...
- Java代理(jdk静态代理、动态代理和cglib动态代理)
一.代理是Java常用的设计模式,代理类通过调用被代理类的相关方法,并对相关方法进行增强.加入一些非业务性代码,比如事务.日志.报警发邮件等操作. 二.jdk静态代理 1.业务接口 /** * 业务接 ...
- [z]Java代理(jdk静态代理、动态代理和cglib动态代理)
一.代理是Java常用的设计模式,代理类通过调用被代理类的相关方法,并对相关方法进行增强.加入一些非业务性代码,比如事务.日志.报警发邮件等操作. 二.jdk静态代理 1.业务接口 1 2 3 4 5 ...
- Spring学习总结(二)——静态代理、JDK与CGLIB动态代理、AOP+IoC
一.为什么需要代理模式 假设需实现一个计算的类Math.完成加.减.乘.除功能,如下所示: package com.zhangguo.Spring041.aop01; public class Mat ...
- Java代理模式精讲之静态代理,动态代理,CGLib代理
代理(Proxy)是一种设计模式,通俗的讲就是通过别人达到自己不可告人的目的(玩笑). 如图: 代理模式的关键点是:代理对象与目标对象.代理对象是对目标对象的扩展,并会调用目标对象 这三个代理模式,就 ...
随机推荐
- 在.Net MVC中自定义ValidationAttribute标签对Model中的属性做验证
写一个继承与ValidationAttribute类的自定义的验证方法 MVC中传递数据时,大多数都会用Model承载数据,并且在传到控制器后,对Model进行一系列的验证. 我平时经常使用的判断方法 ...
- 【Python之路】第九篇--Python基础之线程、进程和协程
进程与线程之间的关系 线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除.线程可与属于同一进程的其它线程共享进程所拥有的全 ...
- selenium+python在Windows的环境搭建
1 python下载安装 python早已安装,不再多说.因为开发使用的python2.7,所以同样使用2.7 2 打开Powershell, 输入python -m pip install sele ...
- 简单的socket方法
void sockfunc() { WSADATA Ws; WSAStartup(MAKEWORD(,), &Ws); SOCKET hs = socket(AF_INET, SOCK_STR ...
- UITableViewCell上面添加UIWebView
首先创建一个webView对象和 private var webViewcell : UIWebView? 在viewDidLoad里面设置创建webViewcell,设置属性并用kvo监听webVi ...
- Ubuntu配置Samba
一.安装Ubuntu samba服务器 $ sudo apt-get install samba $ sudo apt-get install smbclient # Linux客户端测试用 二.创建 ...
- pthon之异常、文件练习题
1.在当前目录下查找文件夹“电摄班”,如果不存在则创建2.在电摄班下创建boys.girls.两个txt文件3.将字典中属于电摄班的同学按男女区分,分别放到boys.girls文件中,每个名字在文件中 ...
- Mac MySQLdb模块安装,可算解决了
转载:http://blog.csdn.net/janronehoo/article/details/25207825 短评:这篇文章感觉是比较全面解决Mac MySQLdb模块安装问题的文章了,特别 ...
- Linux下安装awstats日志统计分析
1. 下载安装 cd /data/software wget http://prdownloads.sourceforge.net/awstats/awstats-7.0-1.noarch.rpm 2 ...
- noip2015Day2T1-跳石头
题目描述 Description 一年一度的“跳石头”比赛又要开始了! 这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石.组委会已经选择好了两块岩石作为比赛起点和终点.在起点和终点之间,有N ...