您的位置:首页 > 其它

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
结果发现,父类都是由同一加载器加载的。目前解决此问题就想到这个办。应该还有其他的方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: