1. 起因

在前两天,为了解决websphere和JDK8上部署的应用发起webservice调用(框架用的cxf)时报错的问题,跟了一些代码,最终发现可以通过加上参数-Dcom.sun.xml.bind.v2.bytecode.ClassTailor.noOptimize=true来解决。

2. ClassTailor.noOptimize优化了什么

分析jaxb的代码分析,由于webservice调用要用到xml与bean对象的转换,于是就是用到对bean字段的get set。通常的想法此处用反射便可以完成。但是jaxb在这里用了动态生成字节码的方式直接调用bean的get set方法,以达到节提升性能的目的,本质上就是换了class文件中常量池的UFT8字符串

具体可以参见com.sun.xml.bind.v2.bytecode.ClassTailor类,com.sun.xml.bind.v2.runtime.reflect.opt.AccessorInjector 类。

3. 按他的方式做一个demo

demo代码打包在这里下载,可以跑起来的

假设需要操作的bean是User 如下:

/*
* 文 件 名: User.java
* 版 权: . Copyright 2008-2017, All rights reserved Co.,Ltd.
* 描 述: <描述>
* 修 改 人: simon
* 修改时间: 2018年4月20日
*/
package com.cnblogs.simoncook.jaxb.bean; /**
* <一句话功能简述>
* <功能详细描述>
*
* @author simon
* @version [版本号, 2018年4月20日]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
public class User
{ private String name; public String getName()
{
return name;
} public void setName(String name)
{
this.name = name;
} }

转换的模板类与接口

/*
* 文 件 名: IMethodAccess.java
* 版 权: . Copyright 2008-2017, All rights reserved Co.,Ltd.
* 描 述: <描述>
* 修 改 人: simon
* 修改时间: 2018年4月20日
*/
package com.cnblogs.simoncook.jaxb.helper; /**
* <一句话功能简述>
* <功能详细描述>
*
* @author simon
* @version [版本号, 2018年4月20日]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
public interface IMethodAccess
{
public Object get(Object bean); public void set(Object instance, Object value);
}
/*
* 文 件 名: MethodAcessRef.java
* 版 权: . Copyright 2008-2017, All rights reserved Co.,Ltd.
* 描 述: <描述>
* 修 改 人: simon
* 修改时间: 2018年4月20日
*/
package com.cnblogs.simoncook.jaxb.helper; /**
* <一句话功能简述> <功能详细描述>
*
* @author simon
* @version [版本号, 2018年4月20日]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
public class MethodAcessTemplate implements IMethodAccess
{ public MethodAcessTemplate()
{
} public Object get(Object bean)
{
return ((Bean)bean).getRef();
} public void set(Object instance, Object value)
{
((Bean)instance).setRef((Ref)value);
}
}

动态字节码生成与加载调用类

/*
* 文 件 名: ClassTailor.java
* 版 权: . Copyright 2008-2017, All rights reserved Co.,Ltd.
* 描 述: <描述>
* 修 改 人: simon
* 修改时间: 2018年4月20日
*/
package com.cnblogs.simoncook.jaxb.helper; import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction; import com.cnblogs.simoncook.jaxb.bean.User; /**
* <一句话功能简述> <功能详细描述>
*
* @author simon
* @version [版本号, 2018年4月20日]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
public class ClassTailor
{ /**
* <一句话功能简述> <功能详细描述>
*
* @param args
* @see [类、类#方法、类#成员]
*/
public static void main(String[] args)
{
User u = new User();
ClassLoader classLoader = ClassTailor.class.getClassLoader();
String templateClassName = "com/cnblogs/simoncook/jaxb/helper/MethodAcessTemplate";
String methodAcessRefTemplate = templateClassName + ".class";
InputStream templateClassResource = classLoader.getResourceAsStream(methodAcessRefTemplate);
String[] replacements = {"com/cnblogs/simoncook/jaxb/helper/Bean", "com/cnblogs/simoncook/jaxb/bean/User",
"com/cnblogs/simoncook/jaxb/helper/Ref", "java/lang/String", "()Lcom/cnblogs/simoncook/jaxb/helper/Ref;",
"()Ljava/lang/String;", "(Lcom/cnblogs/simoncook/jaxb/helper/Ref;)V", "(Ljava/lang/String;)V", "getRef",
"getName", "setRef", "setName"}; String newClassName = "com/cnblogs/simoncook/jaxb/bean/User_getset_name";
byte[] accessClassByte = tailor(templateClassResource, templateClassName, newClassName, replacements);
dumpClass(newClassName, accessClassByte); try
{
@SuppressWarnings("unchecked")
Class<IMethodAccess> c = buildGetSetClass(classLoader, newClassName, accessClassByte);
IMethodAccess accessor = (IMethodAccess)c.newInstance();
accessor.set(u, "simon");
System.out.println(u.getName());
}
catch (NoSuchMethodException e)
{
e.printStackTrace();
}
catch (SecurityException e)
{
e.printStackTrace();
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
catch (IllegalArgumentException e)
{
e.printStackTrace();
}
catch (InvocationTargetException e)
{
e.printStackTrace();
}
catch (InstantiationException e)
{
e.printStackTrace();
} } /**
* <一句话功能简述> <功能详细描述>
*
* @param classLoader
* @param newClassName
* @param accessClassByte
* @return
* @throws NoSuchMethodException
* @throws IllegalAccessException
* @throws InvocationTargetException
* @see [类、类#方法、类#成员]
*/
private static Class<IMethodAccess> buildGetSetClass(ClassLoader classLoader, String newClassName,
byte[] accessClassByte)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
{
final Method defineClass =
ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, Integer.TYPE, Integer.TYPE);
final Method resolveClass = ClassLoader.class.getDeclaredMethod("resolveClass", Class.class);
AccessController.doPrivileged(new PrivilegedAction<Void>()
{ public Void run()
{
// TODO: check security implication
// do these setAccessible allow anyone to call these methods freely?s
defineClass.setAccessible(true);
resolveClass.setAccessible(true);
return null;
}
});
ClassLoader filedTypeClassLoader = classLoader; @SuppressWarnings("unchecked")
Class<IMethodAccess> c = (Class<IMethodAccess>)defineClass.invoke(filedTypeClassLoader,
newClassName.replace('/', '.'),
accessClassByte,
0,
accessClassByte.length);
resolveClass.invoke(filedTypeClassLoader, c);
return c;
} /**
* <一句话功能简述> <功能详细描述>
*
* @param newClassName
* @param accessClassByte
* @see [类、类#方法、类#成员]
*/
private static void dumpClass(String newClassName, byte[] accessClassByte)
{
FileOutputStream out = null;
BufferedOutputStream bufW = null;
try
{
File file = new File("/Users/simon/700.temp_/jaxbclass/" + newClassName);
if (!file.getParentFile().exists())
{
file.getParentFile().mkdirs();
}
out = new FileOutputStream(file);
bufW = new BufferedOutputStream(out);
bufW.write(accessClassByte);
bufW.flush();
bufW.close();
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
if (out != null)
{
try
{
out.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
if (bufW != null)
{
try
{
bufW.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
} public static byte[] tailor(InputStream image, String templateClassName, String newClassName,
String... replacements)
{
DataInputStream in = new DataInputStream(image); try
{
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
DataOutputStream out = new DataOutputStream(baos); // skip until the constant pool count
long l = in.readLong();
out.writeLong(l); // read the constant pool size
short count = in.readShort();
out.writeShort(count); // replace constant pools
for (int i = 0; i < count; i++)
{
byte tag = in.readByte();
out.writeByte(tag);
switch (tag)
{
case 0:
// this isn't described in the spec,
// but class files often seem to have this '0' tag.
// we can apparently just ignore it, but not sure
// what this really means.
break; case 1: // CONSTANT_UTF8
{
String value = in.readUTF();
if (value.equals(templateClassName))
value = newClassName;
else
{
for (int j = 0; j < replacements.length; j += 2)
if (value.equals(replacements[j]))
{
value = replacements[j + 1];
break;
}
}
out.writeUTF(value);
}
break; case 3: // CONSTANT_Integer
case 4: // CONSTANT_Float
out.writeInt(in.readInt());
break; case 5: // CONSTANT_Long
case 6: // CONSTANT_Double
i++; // doubles and longs take two entries
out.writeLong(in.readLong());
break; case 7: // CONSTANT_Class
case 8: // CONSTANT_String
out.writeShort(in.readShort());
break; case 9: // CONSTANT_Fieldref
case 10: // CONSTANT_Methodref
case 11: // CONSTANT_InterfaceMethodref
case 12: // CONSTANT_NameAndType
out.writeInt(in.readInt());
break; default:
throw new IllegalArgumentException("Unknown constant type " + tag);
}
} // then copy the rest
byte[] buf = new byte[512];
int len;
while ((len = in.read(buf)) > 0)
out.write(buf, 0, len); in.close();
out.close(); // by now we got the properly tailored class file image
return baos.toByteArray();
}
catch (Exception e)
{
}
return null;
} }

4. 分析

用JD 对模板类class与之前的class(需要dump)进行反编译,结果如下,一目了然。

用classpy查看两个class的结构,也能看出来,本质上就是换了常量池的UFT8字符串

5. 与cglib比较

简单的看了下cglib的BeanMap,原理相同,手法不同。差异在于,他是用asm去动态生成class。

asm对class的生成做了一些封装,比如你要生成什么名字的class 构造函数是什么,版本是是什么,你只要调用他封装的接口,把这些要素当做参数传进去即可。不需要自己一个byte一个byte的去按class的文件格式组织。

动态修改字节码以替换用反射调用get set方法的形式的更多相关文章

  1. android apk 防止反编译技术第二篇-运行时修改字节码

    上一篇我们讲了apk防止反编译技术中的加壳技术,如果有不明白的可以查看我的上一篇博客http://my.oschina.net/u/2323218/blog/393372.接下来我们将介绍另一种防止a ...

  2. 记一次使用修改字节码的方法解决java.lang.NoSuchMethodError

    接兔兔国际sdk ane 充值界面选择兔币充值就会闪退, 观察logcat 04-19 10:10:54.224: E/AndroidRuntime(20315): FATAL EXCEPTION: ...

  3. C#通过反射调用类及方法

    反射有个典型的应用,就是菜单的动态加载,原理就是通过反射调用某个窗体(类).下面演示一下通过反射调用类及方法: 1.新建一个类,命名为:ReflectionHelper,代码如下: #region 创 ...

  4. [转]简单的动态修改RDLC报表页边距和列宽的方法

    本文转自:http://star704983.blog.163.com/blog/static/136661264201161604413204/ 1.修改页边距 XmlDocument XMLDoc ...

  5. 在C#中使用反射调用internal的方法

    MSDN上解释Internal如下: The internal keyword is an access modifier for types and type members. Internal t ...

  6. java 通过反射调用属性,方法,构造器

    package reflection2; import static org.junit.Assert.assertArrayEquals; import java.lang.reflect.Cons ...

  7. java 使用反射调用可变参数方法

    使用反射操作对象-调用可变参数方法 要把可变参数都当做是其对应的数组类型参数; 如 show(XX... is)作为show(XX[] is)调用; 若可变参数元素类型是引用类型: JDK内部接收到参 ...

  8. 动态字节码技术Javassist

    字节码技术可以动态改变某个类的结构(添加/删除/修改  新的属性/方法) 关于字节码的框架有javassist,asm,bcel等 引入依赖 <dependency> <groupI ...

  9. 字节码编程,Byte-buddy篇一《基于Byte Buddy语法创建的第一个HelloWorld》

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 相对于小傅哥之前编写的字节码编程: ASM.Javassist 系列,Byte Bu ...

随机推荐

  1. CSS修改Autocomplete样式

    举个场景:在用户登录成功的时候,浏览器有时候会咨询你是否记住密码,当你记住密码下次登录的时候,标签会自动填充,但是这样会造成样式不统一,解决办法如下: input:-webkit-autofill, ...

  2. 一个页面有相同ID元素的情况分析

    经常会遇到一个页面中有相同定义相同id的情况,从道理上来说,id应该是这个页面中某个元素的唯一标识,所以不应该出现有相同id的情况,否则会产生意想不到的结果.而且各个浏览器的表现也是不一样的.我只做了 ...

  3. Partition(线段树的离线处理)

    有一点类似区间K值的求法. 这里有两颗树,一个是自己建的线段树,一个是题目中给定的树.以线段树和树进行区分. 首先离散化一下,以离散化后的结果建线段树,线段树的节点开了2维,一维保存当前以当前节点为权 ...

  4. mysql添加用户并赋予权限命令

    添加用户: create user 'gouge'@'localhost' identified by 'gouge'; 赋予权限: 给gouge 用户赋予所有test开头的数据库权限 (test% ...

  5. bug 查找 (二) 从前端找到后端

    bug 查找 (二) 从前端找到后端 几天来,组长说我们系统的 apm 数据不正确,最体表现就是前端项目这几天错误统计为 0. 这不正常(没有办法,我们代码写的很烂),因为前端环境很复杂,网络,浏览器 ...

  6. 借助Code Splitting 提升单页面应用性能

    近日的工作集中于一个单页面应用(Single-page application),在项目中尝试了闻名已久的Code splitting,收获极大,特此分享. Why we need code spli ...

  7. 2566. [51nod 1129] 字符串最大值

    [题目描述] 一个字符串的前缀是指包含该字符第一个字母的连续子串,例如:abcd的所有前缀为a, ab, abc, abcd. 给出一个字符串S,求其所有前缀中,字符长度与出现次数的乘积的最大值. 例 ...

  8. DVWA之跨站请求伪造(CSRF)

    CSRF全称是Cross site request forgery ,翻译过来就是跨站请求伪造. CSRF是指利用受害者尚未失效的身份认证信息(cookie,会话信息),诱骗其点击恶意链接或者访问包含 ...

  9. Java、Node.js、PHP还是.Net? 无论你选谁,我都能教你一招!

    七夕如期而至,不该来的终究还是来了.再傲娇的单身贵族恐怕也难免在今天会感觉一丝丝的空虚.还好你关注了我,因为接下来我准备了三大招教你一个人…..也可以优雅地过七夕. 招式一:移形幻影,无中生有 七夕当 ...

  10. 树形dp——覆盖所有边的最少费用(Protecting Zonk)

    一.问题描述 有一个n(n<=10000)个节点的无根树.有两种装置A,B,每种都有无限多个. 1.在某个节点X使用A装置需要C1(C1<=1000)的花费,并且此时与节点X相连的边都被覆 ...