使用Plastic框架进行代码生成时的Tricky ClassCastException

时间:2012-06-06 20:05:11

标签: java classloader

有什么关系 大家好 - 我正在看一个名为“有你的蛋糕并且也吃它的演示文稿:Java中的元编程”

主持人是Tapestry的作者Howard M. Lewis Ship(一个?),并且在制作时,一个名为“塑料”的子项目被用来利用ASM来改变字节码。

我不会假装成为专家,但最终结果应该是我可以编写代码,以便可以使用带注释的类,方法和字段来生成更多的Java代码,从而减少样板代码。

我的问题 下面的代码是一个完整的示例来演示我的问题。测试示例应修改EqualsDemo类,使其包含equals()和hashCode()的实现。 在运行它时,我得到一个错误,它基本上表明我不能将'com.example.plastic.transformed.EqualsDemo'类型的对象强制转换为'com.example.plastic.transformed.EqualsDemo'(是的,同一个类)

主持人刚才提到,这些错误很烦人,并没有暗示他们来自哪里 - 我的搜索到目前为止表明它们属于不同的类加载器。 但是,我完全无法解决问题,因此我的问题在这里(!)

com.example.plastic.transformed.EqualsDemo cannot be cast to com.example.plastic.transformed.EqualsDemo
        at MainClass.main(MainClass.java:28)

那我该怎么办?替换类加载器? (如果是这样,怎么样?)或者是塑料的某些部分我没有得到?生成代理对象或类似物的一些方法,我需要使用它来使事情顺利进行?

PS! 到目前为止,我发现的示例都使用了我认为在最终使用带注释的实例时的Groovy。

希望有人比我更有能力:)

链接: Tapestry主页(塑料在下载中包含在jar中):http://tapestry.apache.org/

Main.java

import java.util.ArrayList;
import java.util.List;

import org.apache.tapestry5.internal.plastic.StandardDelegate;
import org.apache.tapestry5.plastic.ClassInstantiator;
import org.apache.tapestry5.plastic.PlasticManager;

import com.example.plastic.transformer.EqualsHashCodeTransformer;
import com.example.plastic.transformed.EqualsDemo;

public class MainClass {

    public static void main(String[] args) {


        List<String> pList = new ArrayList<String>();
        pList.add("com.example.plastic.transformed");

        PlasticManager pm = PlasticManager
                .withContextClassLoader()
                .delegate( new StandardDelegate(new EqualsHashCodeTransformer()) )
                .packages(pList)
                .create();


        final String EQUALSDEMO = "com.example.plastic.transformed.EqualsDemo";
        ClassInstantiator<EqualsDemo> i = pm.getClassInstantiator(EQUALSDEMO);
        i.newInstance().hashCode();
        /*
        com.example.plastic.transformed.EqualsDemo cannot be cast to com.example.plastic.transformed.EqualsDemo
        at MainClass.main(MainClass.java:28)
        */
    }
}

ImplementEqualsHashCode.java

package com.example.plastic.annotations;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ImplementEqualsHashCode {

}

EqualsHashCodeTransformer.java

package com.example.plastic.transformer;

import java.util.ArrayList;
import java.util.List;

import org.apache.tapestry5.plastic.FieldHandle;
import org.apache.tapestry5.plastic.MethodAdvice;
import org.apache.tapestry5.plastic.MethodDescription;
import org.apache.tapestry5.plastic.MethodInvocation;
import org.apache.tapestry5.plastic.PlasticClass;
import org.apache.tapestry5.plastic.PlasticClassTransformer;
import org.apache.tapestry5.plastic.PlasticField;

import com.example.plastic.annotations.*;

public class EqualsHashCodeTransformer implements PlasticClassTransformer {
    private MethodDescription EQUALS = new MethodDescription("boolean", "equals", "java.lang.Object");

    private MethodDescription HASHCODE = new MethodDescription("int", "hashCode");

    private static final int PRIME = 37;

    public void transform(PlasticClass plasticClass){

        //check that the class is annotated
        if(!plasticClass.hasAnnotation(ImplementEqualsHashCode.class)) {
            return;
        }

        List<PlasticField> fields = plasticClass.getAllFields();


        final List<FieldHandle> handles = new ArrayList<FieldHandle>();
        for(PlasticField field : fields){
            handles.add(field.getHandle());
        }

        //HashCode method introduction :)
        plasticClass.introduceMethod(HASHCODE).addAdvice(new MethodAdvice() {
            public void advise(MethodInvocation invocation){
                Object instance = invocation.getInstance();
                int result = 1;

                for(FieldHandle handle : handles){
                    Object fieldValue = handle.get(instance);

                    if(fieldValue != null)
                        result = (result * PRIME) + fieldValue.hashCode();
                }

                invocation.setReturnValue(result);

                //Don't proceed to the empty introduced method
            }

        });

        plasticClass.introduceMethod(EQUALS).addAdvice(new MethodAdvice() {
            public void advise(MethodInvocation invocation) {
                Object thisInstance = invocation.getInstance();
                Object otherInstance = invocation.getParameter(0);

                invocation.setReturnValue(isEqual(thisInstance, otherInstance));

                //Don't proceed to the empty introduced method
            }

            private boolean isEqual(Object thisInstance, Object otherInstance) {

                if(thisInstance == otherInstance)
                    return true;

                if(otherInstance == null)
                    return false;

                if(!(thisInstance.getClass() == otherInstance.getClass())) 
                    return false;

                for(FieldHandle handle : handles){
                    Object thisValue = handle.get(thisInstance);
                    Object otherValue = handle.get(otherInstance);

                    if(!(thisValue == otherValue || thisValue.equals(otherValue)))
                        return false;
                }

                return true;
            }
        });
    }
}

EqualsDemo.java

package com.example.plastic.transformed;

import com.example.plastic.annotations.ImplementEqualsHashCode;


@ImplementEqualsHashCode
public class EqualsDemo {
    private int intValue;
    private String stringValue;

    public int getIntValue(){
        return intValue;
    }

    public void setIntValue(int intValue){
        this.intValue = intValue;
    }

    public String getStringValue(){
        return stringValue;
    }

    public void setStringValue(String stringValue){
        this.stringValue = stringValue;
    }
}

2 个答案:

答案 0 :(得分:1)

你不想将包添加到塑料管理器 - 它使用不同的类加载器并将加载这些类,在这些包中创建两个类的副本(一个在父类加载器中,一个在塑料类加载器中) )它将为您提供框架尝试强制转换为类时所看到的ClassCastException。试试这个:

import org.apache.tapestry5.internal.plastic.StandardDelegate;
import org.apache.tapestry5.plastic.ClassInstantiator;
import org.apache.tapestry5.plastic.PlasticManager;

public class MainClass {

  public static void main(String[] args) {
      PlasticManager pm = PlasticManager
            .withContextClassLoader()
            .delegate(new StandardDelegate())
            .create();
      ClassInstantiator<EqualsDemo> ci = pm.createClass(EqualsDemo.class, new EqualsHashCodeTransformer());
      System.out.println(ci.newInstance().hashCode());
   }
}

答案 1 :(得分:0)

我想而不是

PlasticManager.withContextClassLoader()...

使用以下内容可以解决您的问题:

PlasticManager.withClassLoader(getClass().getClassLoader())...