jaxb动态修改注解-类加载器
2016-03-10 16:05
302 查看
最近项目用到jaxb解析xml文件。需要定义一个javaBean模型。
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {"sender","sheet" })
@XmlRootElement(name = "AUTO ")
public class DataFile{
@XmlElement(name="Sender",required = true)
protected Sender sender;
public Sender getSender() {
return sender;
}
public void setSender(Sender sender) {
this.sender = sender;
}
现在遇到个问题,就是@XmlRootElement(name = "AUTO ")这个节点是动态的。但是注解在运行期间是改变不了得。那只有改字节码了。Asm?cglib? javassist?..等等修改字节码的库。比较方便的还是cglib和javassist吧。因为它们都是对Asm的封装。
/**
* 运行时动态修改注解值
* @author c_zhangzengmin002
*
*/
public class ClassUtils {
//缓存加载器加载的calss对象
private static final Map<String, Class<?>> classPoolMap=new ConcurrentHashMap<String, Class<?>>();
private ClassUtils(){}
/**
* 运行时动态修改注解值
* @param Class
*
* @param entityClassName
* 待映射的实体全限定类名
* @param annotation 需要修改的注解全限定类名
* @param annotationAttrName 注解属性名称
* @param annotationVal 注解属性值
*
* @return 映射后的类对象
*/
public static Class<?> updateAnnotationForClass(String entityClassName,String annotationClassName, String annotationAttrName,String annotationVal)throws Exception {
Class<?> c = null;
if(StringUtil.isBlank(entityClassName) || StringUtil.isBlank(annotationClassName)
|| StringUtil.isBlank(annotationAttrName) || StringUtil.isBlank(annotationVal)){
return c;
}
c=classPoolMap.get(annotationVal);
if(null!=c){
return c;
}
try {
ClassPool classPool = ClassPool.getDefault();
classPool.appendClassPath(new ClassClassPath(ClassUtils.class));
//classPool.importPackage("javax.persistence");
CtClass clazz = classPool.get(entityClassName);
clazz.defrost();
ClassFile classFile = clazz.getClassFile();
ConstPool constPool = classFile.getConstPool();
Annotation tableAnnotation = new Annotation(
annotationClassName, constPool);
tableAnnotation.addMemberValue(annotationAttrName, new StringMemberValue(
annotationVal, constPool));
// 获取运行时注解属性
AnnotationsAttribute attribute = (AnnotationsAttribute) classFile
.getAttribute(AnnotationsAttribute.visibleTag);
attribute.addAnnotation(tableAnnotation);
classFile.addAttribute(attribute);
classFile.setVersionToJava5();
// clazz.writeFile();
AnnotationClassLoader loader = new AnnotationClassLoader(
ClassUtils.class.getClassLoader());
//加载器加载class文件,一个加载器只会加载一次路径名称相同的calss
//这里因为只改了注解,类路径是没有改变的。
c = clazz.toClass(loader, null);
if(null!=c){
classPoolMap.put(annotationVal, c);
}
} catch (Exception e) {
e.printStackTrace();
}
return c;
}
public static void main(String[] args)throws Exception { Class<?> clazz3 = ClassUtils.updateAnnotationForClass(
"com.dep.jaxb.DataFile","javax.xml.bind.annotation.XmlRootElement","name", "AUTO_IDS");
System.out.println("修改后的@XmlRootElement: " + clazz3.getAnnotation(XmlRootElement.class).name());
}
}
/**
* <p>
* Description:根据class类型,将文件流转换成对象。在这个过程中会对文件进行编码处理
* </p>
* <p>
* Create Time:2012-9-31
* </p>
*
* @author zzm
* @param Class
* <?>
* @param BufferedReader
* 文件流
* @throws Exception
*/
public static Object unmarshalClaim(Class<?> c, BufferedReader in)
throws Exception {
StringBuilder buffer = null;
JAXBContext jaxbContext;
try {
buffer = new StringBuilder();
String line = "";
if (in != null) {
while ((line = in.readLine()) != null) {
buffer.append(line);
}
}
//所取头结点名称
String dataFileName=StringUtil.getXMlHandleNodeString(buffer);
//获取动态class对象
Class<?> calss=ClassUtils.updateAnnotationForClass(Constant.DATAFILE_CLASS_NAME, Constant.XMLROOTELEMENT_CLASS_NAME,
Constant.NAME_STR, dataFileName);
if(null!=calss){
c=calss;
}
String xmlString = StringUtil.getXMlString(buffer);
StreamSource streamSource = new StreamSource(new StringReader(
xmlString));
// 加载映射bean类
jaxbContext = JAXBContext.newInstance(c);
// 创建解析
Unmarshaller um = jaxbContext.createUnmarshaller();
return unmarshalClaim(um,streamSource);
} catch (Exception e) {
log.error(e);
e.printStackTrace();
throw new BasicException(e.getMessage());
}finally{
in.close();
}
}
问题来了!
public static void unmarshalClaim()throws Exception {
String s = "E:\\Documents and Settings\\c_zhangzengmin002\\桌面\\xml\\1.xml";
//将xml动态的转换成相应的javaBean对象
Object obj = JAXBUtil.unmarshalClaim(DataFile.class, s);
//这样问题来了,这里强转报错了。因为要动态获取的class对象。这里是使用不同的加载器加载的calss对象实例。不同加载器加载的对象实例是不一样的。
//DataFile newStudent = (DataFile) obj
}
不同加载器加载同class对象实例是不一样的。当一个类实例化时,首先会对其父类实例化。
解决:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {"sender","sheet" })
@XmlRootElement(name = "AUTO ")
public class DataFile extends AbstractDataFile{
@XmlElement(name="Sender",required = true)
protected Sender sender;
public Sender getSender() {
return sender;
}
public void setSender(Sender sender) {
this.sender = sender;
}
public abstract class AbstractDataFile {
public Sender getSender() {return null;};
}
public static void unmarshalClaim()throws Exception {
String s = "E:\\Documents and Settings\\c_zhangzengmin002\\桌面\\xml\\1.xml";
//自定义加载器加载的对象
Object newStudent2 = JAXBUtil.unmarshalClaim(DataFile.class, s3);
//默认加载器加载的对象
DataFile dataFile =new DataFile();
AbstractDataFile newStudent = (AbstractDataFile) newStudent2;
System.out.println(dataFile.getClass().getClassLoader());
System.out.println(newStudent.getClass().getClassLoader());
//父类的加载器 System.out.println(dataFile.getClass().getSuperclass().getClassLoader());
//父类的加载器 System.out.println(newStudent.getClass().getSuperclass().getClassLoader());
}
运行结果:
sun.misc.Launcher$AppClassLoader@82ba41
com.dep.util.EntityClassLoader@a39137
sun.misc.Launcher$AppClassLoader@82ba41
sun.misc.Launcher$AppClassLoader@82ba41
结果发现,父类都是由同一加载器加载的。目前解决此问题就想到这个办。应该还有其他的方法。
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {"sender","sheet" })
@XmlRootElement(name = "AUTO ")
public class DataFile{
@XmlElement(name="Sender",required = true)
protected Sender sender;
public Sender getSender() {
return sender;
}
public void setSender(Sender sender) {
this.sender = sender;
}
现在遇到个问题,就是@XmlRootElement(name = "AUTO ")这个节点是动态的。但是注解在运行期间是改变不了得。那只有改字节码了。Asm?cglib? javassist?..等等修改字节码的库。比较方便的还是cglib和javassist吧。因为它们都是对Asm的封装。
/**
* 运行时动态修改注解值
* @author c_zhangzengmin002
*
*/
public class ClassUtils {
//缓存加载器加载的calss对象
private static final Map<String, Class<?>> classPoolMap=new ConcurrentHashMap<String, Class<?>>();
private ClassUtils(){}
/**
* 运行时动态修改注解值
* @param Class
*
* @param entityClassName
* 待映射的实体全限定类名
* @param annotation 需要修改的注解全限定类名
* @param annotationAttrName 注解属性名称
* @param annotationVal 注解属性值
*
* @return 映射后的类对象
*/
public static Class<?> updateAnnotationForClass(String entityClassName,String annotationClassName, String annotationAttrName,String annotationVal)throws Exception {
Class<?> c = null;
if(StringUtil.isBlank(entityClassName) || StringUtil.isBlank(annotationClassName)
|| StringUtil.isBlank(annotationAttrName) || StringUtil.isBlank(annotationVal)){
return c;
}
c=classPoolMap.get(annotationVal);
if(null!=c){
return c;
}
try {
ClassPool classPool = ClassPool.getDefault();
classPool.appendClassPath(new ClassClassPath(ClassUtils.class));
//classPool.importPackage("javax.persistence");
CtClass clazz = classPool.get(entityClassName);
clazz.defrost();
ClassFile classFile = clazz.getClassFile();
ConstPool constPool = classFile.getConstPool();
Annotation tableAnnotation = new Annotation(
annotationClassName, constPool);
tableAnnotation.addMemberValue(annotationAttrName, new StringMemberValue(
annotationVal, constPool));
// 获取运行时注解属性
AnnotationsAttribute attribute = (AnnotationsAttribute) classFile
.getAttribute(AnnotationsAttribute.visibleTag);
attribute.addAnnotation(tableAnnotation);
classFile.addAttribute(attribute);
classFile.setVersionToJava5();
// clazz.writeFile();
AnnotationClassLoader loader = new AnnotationClassLoader(
ClassUtils.class.getClassLoader());
//加载器加载class文件,一个加载器只会加载一次路径名称相同的calss
//这里因为只改了注解,类路径是没有改变的。
c = clazz.toClass(loader, null);
if(null!=c){
classPoolMap.put(annotationVal, c);
}
} catch (Exception e) {
e.printStackTrace();
}
return c;
}
public static void main(String[] args)throws Exception { Class<?> clazz3 = ClassUtils.updateAnnotationForClass(
"com.dep.jaxb.DataFile","javax.xml.bind.annotation.XmlRootElement","name", "AUTO_IDS");
System.out.println("修改后的@XmlRootElement: " + clazz3.getAnnotation(XmlRootElement.class).name());
}
}
/**
* <p>
* Description:根据class类型,将文件流转换成对象。在这个过程中会对文件进行编码处理
* </p>
* <p>
* Create Time:2012-9-31
* </p>
*
* @author zzm
* @param Class
* <?>
* @param BufferedReader
* 文件流
* @throws Exception
*/
public static Object unmarshalClaim(Class<?> c, BufferedReader in)
throws Exception {
StringBuilder buffer = null;
JAXBContext jaxbContext;
try {
buffer = new StringBuilder();
String line = "";
if (in != null) {
while ((line = in.readLine()) != null) {
buffer.append(line);
}
}
//所取头结点名称
String dataFileName=StringUtil.getXMlHandleNodeString(buffer);
//获取动态class对象
Class<?> calss=ClassUtils.updateAnnotationForClass(Constant.DATAFILE_CLASS_NAME, Constant.XMLROOTELEMENT_CLASS_NAME,
Constant.NAME_STR, dataFileName);
if(null!=calss){
c=calss;
}
String xmlString = StringUtil.getXMlString(buffer);
StreamSource streamSource = new StreamSource(new StringReader(
xmlString));
// 加载映射bean类
jaxbContext = JAXBContext.newInstance(c);
// 创建解析
Unmarshaller um = jaxbContext.createUnmarshaller();
return unmarshalClaim(um,streamSource);
} catch (Exception e) {
log.error(e);
e.printStackTrace();
throw new BasicException(e.getMessage());
}finally{
in.close();
}
}
问题来了!
public static void unmarshalClaim()throws Exception {
String s = "E:\\Documents and Settings\\c_zhangzengmin002\\桌面\\xml\\1.xml";
//将xml动态的转换成相应的javaBean对象
Object obj = JAXBUtil.unmarshalClaim(DataFile.class, s);
//这样问题来了,这里强转报错了。因为要动态获取的class对象。这里是使用不同的加载器加载的calss对象实例。不同加载器加载的对象实例是不一样的。
//DataFile newStudent = (DataFile) obj
}
不同加载器加载同class对象实例是不一样的。当一个类实例化时,首先会对其父类实例化。
解决:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {"sender","sheet" })
@XmlRootElement(name = "AUTO ")
public class DataFile extends AbstractDataFile{
@XmlElement(name="Sender",required = true)
protected Sender sender;
public Sender getSender() {
return sender;
}
public void setSender(Sender sender) {
this.sender = sender;
}
public abstract class AbstractDataFile {
public Sender getSender() {return null;};
}
public static void unmarshalClaim()throws Exception {
String s = "E:\\Documents and Settings\\c_zhangzengmin002\\桌面\\xml\\1.xml";
//自定义加载器加载的对象
Object newStudent2 = JAXBUtil.unmarshalClaim(DataFile.class, s3);
//默认加载器加载的对象
DataFile dataFile =new DataFile();
AbstractDataFile newStudent = (AbstractDataFile) newStudent2;
System.out.println(dataFile.getClass().getClassLoader());
System.out.println(newStudent.getClass().getClassLoader());
//父类的加载器 System.out.println(dataFile.getClass().getSuperclass().getClassLoader());
//父类的加载器 System.out.println(newStudent.getClass().getSuperclass().getClassLoader());
}
运行结果:
sun.misc.Launcher$AppClassLoader@82ba41
com.dep.util.EntityClassLoader@a39137
sun.misc.Launcher$AppClassLoader@82ba41
sun.misc.Launcher$AppClassLoader@82ba41
结果发现,父类都是由同一加载器加载的。目前解决此问题就想到这个办。应该还有其他的方法。
相关文章推荐
- BitLocker使用: 不重启,关闭已解锁加密驱动器。
- Cisco & H3C 交换机 DHCP 中继
- 51nod 1007 正整数分组
- iOS 7中实现模糊效果
- 软件推荐:扫码格式检测系统
- 在KEIL realview MDK下为单一C文件生成.LIB文件
- 蓝牙基础知识进阶——物理链路和逻辑传输
- ThinkPHP实现支付宝接口功能
- centos中apache-tomcat的配置
- jQuery选择器的那些事儿
- 编译器四:GCC&G++简介
- PAT (Basic Level)1046. 划拳
- ViewHolder
- C++头文件的重定义问题以及头文件互相包含问题
- Manacher算法 线性时间求最大回文子串长度
- win7安装omnetpp-4.6
- node.js环境
- 【练习】内容提供器一、访问其他程序的数据
- 002_第三部分_[函数 / 高阶函数 / 装饰器]
- Andriod 反编译 重新打包 技巧感想记录