模型热交换后,Freemarker removeIntrospectionInfo无法与DCEVM一起使用 [英] Freemarker removeIntrospectionInfo does not work with DCEVM after model hotswap

查看:92
本文介绍了模型热交换后,Freemarker removeIntrospectionInfo无法与DCEVM一起使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Freemarker和DCEVM + HotSwapManager代理.基本上,这使我即使添加/删除方法也可以热交换类.

在Freemarker使用热交换类作为模型之前,一切都像魅力一样工作.它会抛出freemarker.ext.beans.InvalidPropertyException:即使反射显示该方法在那里(在调试会话期间检查),我也没有这样的bean属性.

我正在使用

final Method clearInfoMethod = beanWrapper.getClass().getDeclaredMethod("removeIntrospectionInfo", Class.class);
clearInfoMethod.setAccessible(true);
clearInfoMethod.invoke(clazz);

清除缓存,但是它不起作用.我什至尝试获取classCache成员字段并使用反射将其清除,但它也无法正常工作.

我做错了什么? 我只需要强制freemarker放弃对他已经获得的一个或多个模型类的自省.

有什么办法吗?

更新

示例代码

Application.java

// Application.java
public class Application
{
    public static final String TEMPLATE_PATH = "TemplatePath";
    public static final String DEFAULT_TEMPLATE_PATH = "./";

    private static Application INSTANCE;
    private Configuration freemarkerConfiguration;
    private BeansWrapper beanWrapper;

    public static void main(String[] args)
    {
        final Application application = new Application();
        INSTANCE = application;
        try
        {
            application.run(args);
        }
        catch (InterruptedException e)
        {
            System.out.println("Exiting");
        }
        catch (IOException e)
        {
            System.out.println("IO Error");
            e.printStackTrace();
        }
    }

    public Configuration getFreemarkerConfiguration()
    {
        return freemarkerConfiguration;
    }

    public static Application getInstance()
    {
        return INSTANCE;
    }

    private void run(String[] args) throws InterruptedException, IOException
    {
        final String templatePath = System.getProperty(TEMPLATE_PATH) != null
                ? System.getProperty(TEMPLATE_PATH)
                : DEFAULT_TEMPLATE_PATH;

        final Configuration configuration = new Configuration();
        freemarkerConfiguration = configuration;

        beanWrapper = new BeansWrapper();
        beanWrapper.setUseCache(false);
        configuration.setObjectWrapper(beanWrapper);
        try
        {
            final File templateDir = new File(templatePath);
            configuration.setTemplateLoader(new FileTemplateLoader(templateDir));
        }
        catch (IOException e)
        {
            throw new RuntimeException(e);
        }

        final RunnerImpl runner = new RunnerImpl();
        try
        {
            runner.run(args);
        }
        catch (RuntimeException e)
        {
            e.printStackTrace();
        }
    }

    public BeansWrapper getBeanWrapper()
    {
        return beanWrapper;
    }
}

RunnerImpl.java

// RunnerImpl.java
public class RunnerImpl implements Runner
{
    @Override
    public void run(String[] args) throws InterruptedException
    {
        long counter = 0;
        while(true)
        {
            ++counter;
            System.out.printf("Run %d\n", counter);
//          Application.getInstance().getFreemarkerConfiguration().setObjectWrapper(new BeansWrapper());
            Application.getInstance().getBeanWrapper().clearClassIntrospecitonCache();
            final Worker worker = new Worker();
            worker.doWork();
            Thread.sleep(1000);
        }
    }

Worker.java

// Worker.java
public class Worker
{
    void doWork()
    {
        final Application application = Application.getInstance();
        final Configuration freemarkerConfiguration = application.getFreemarkerConfiguration();

        try
        {
            final Template template = freemarkerConfiguration.getTemplate("test.ftl");
            final Model model = new Model();
            final PrintWriter printWriter = new PrintWriter(System.out);

            printObjectInto(model);
            System.out.println("-----TEMPLATE MACRO PROCESSING-----");
            template.process(model, printWriter);
            System.out.println();
            System.out.println("-----END OF PROCESSING------");
            System.out.println();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        catch (TemplateException e)
        {
            e.printStackTrace();
        }
    }

    private void printObjectInto(Object o)
    {
        final Class<?> aClass = o.getClass();
        final Method[] methods = aClass.getDeclaredMethods();
        for (final Method method : methods)
        {
            System.out.println(String.format("Method name: %s, public: %s", method.getName(), Modifier.isPublic(method.getModifiers())));
        }
    }
}

Model.java

// Model.java    
public class Model
{
    public String getMessage()
    {
        return "Hello";
    }

    public String getAnotherMessage()
    {
        return "Hello World!";
    }
}

此示例完全无效.即使在运行时更改BeansWrapper也不会产生任何效果.

解决方案

BeansWrapper(以及DefaultObjectWrapper等)自省缓存依赖于java.beans.Introspector.getBeanInfo(aClass),而不是反射. (这是因为它将对象视为JavaBeans.)java.beans.Introspector有其自己的内部缓存,因此它可以返回陈旧的信息,在这种情况下,BeansWrapper将仅基于该陈旧的信息重新创建其自己的类自省数据.从java.beans.Introspector的缓存开始,实际上是正确的,因为它基于Java中的类是不可变的假设.如果某些事情违反了该基本规则,则应确保清除了java.beans.Introspector的缓存(以及许多其他缓存...),否则,不仅仅是FreeMarker会崩溃.例如,在JRebel,他们付出了很多努力来清除所有类型的缓存.我猜DCEVM没有足够的资源.因此,看来您必须亲自致电Introspector.flushCaches().

更新:一段时间(Java 7,可能是6)java.beans.Introspector每个线程具有一个缓存,因此您已从所有线程组调用flushCaches().这实际上是实现细节,原则上可以随时更改.可悲的是,Introspector.flushCaches()的JavaDoc并没有警告您...

I am using Freemarker and DCEVM+HotSwapManager agent. This basically allows me to hotswap classes even when adding/removing methods.

Everything works like charm until Freemarker uses hotswapped class as model. It's throwing freemarker.ext.beans.InvalidPropertyException: No such bean property on me even though reflection shows that the method is there (checked during debug session).

I am using

final Method clearInfoMethod = beanWrapper.getClass().getDeclaredMethod("removeIntrospectionInfo", Class.class);
clearInfoMethod.setAccessible(true);
clearInfoMethod.invoke(clazz);

to clear the cache, but it does not work. I even tried to obtain classCache member field and clear it using reflection but it does not work too.

What am I doing wrong? I just need to force freemarker to throw away any introspection on model class/classes he has already obtained.

Is there any way?

UPDATE

Example code

Application.java

// Application.java
public class Application
{
    public static final String TEMPLATE_PATH = "TemplatePath";
    public static final String DEFAULT_TEMPLATE_PATH = "./";

    private static Application INSTANCE;
    private Configuration freemarkerConfiguration;
    private BeansWrapper beanWrapper;

    public static void main(String[] args)
    {
        final Application application = new Application();
        INSTANCE = application;
        try
        {
            application.run(args);
        }
        catch (InterruptedException e)
        {
            System.out.println("Exiting");
        }
        catch (IOException e)
        {
            System.out.println("IO Error");
            e.printStackTrace();
        }
    }

    public Configuration getFreemarkerConfiguration()
    {
        return freemarkerConfiguration;
    }

    public static Application getInstance()
    {
        return INSTANCE;
    }

    private void run(String[] args) throws InterruptedException, IOException
    {
        final String templatePath = System.getProperty(TEMPLATE_PATH) != null
                ? System.getProperty(TEMPLATE_PATH)
                : DEFAULT_TEMPLATE_PATH;

        final Configuration configuration = new Configuration();
        freemarkerConfiguration = configuration;

        beanWrapper = new BeansWrapper();
        beanWrapper.setUseCache(false);
        configuration.setObjectWrapper(beanWrapper);
        try
        {
            final File templateDir = new File(templatePath);
            configuration.setTemplateLoader(new FileTemplateLoader(templateDir));
        }
        catch (IOException e)
        {
            throw new RuntimeException(e);
        }

        final RunnerImpl runner = new RunnerImpl();
        try
        {
            runner.run(args);
        }
        catch (RuntimeException e)
        {
            e.printStackTrace();
        }
    }

    public BeansWrapper getBeanWrapper()
    {
        return beanWrapper;
    }
}

RunnerImpl.java

// RunnerImpl.java
public class RunnerImpl implements Runner
{
    @Override
    public void run(String[] args) throws InterruptedException
    {
        long counter = 0;
        while(true)
        {
            ++counter;
            System.out.printf("Run %d\n", counter);
//          Application.getInstance().getFreemarkerConfiguration().setObjectWrapper(new BeansWrapper());
            Application.getInstance().getBeanWrapper().clearClassIntrospecitonCache();
            final Worker worker = new Worker();
            worker.doWork();
            Thread.sleep(1000);
        }
    }

Worker.java

// Worker.java
public class Worker
{
    void doWork()
    {
        final Application application = Application.getInstance();
        final Configuration freemarkerConfiguration = application.getFreemarkerConfiguration();

        try
        {
            final Template template = freemarkerConfiguration.getTemplate("test.ftl");
            final Model model = new Model();
            final PrintWriter printWriter = new PrintWriter(System.out);

            printObjectInto(model);
            System.out.println("-----TEMPLATE MACRO PROCESSING-----");
            template.process(model, printWriter);
            System.out.println();
            System.out.println("-----END OF PROCESSING------");
            System.out.println();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        catch (TemplateException e)
        {
            e.printStackTrace();
        }
    }

    private void printObjectInto(Object o)
    {
        final Class<?> aClass = o.getClass();
        final Method[] methods = aClass.getDeclaredMethods();
        for (final Method method : methods)
        {
            System.out.println(String.format("Method name: %s, public: %s", method.getName(), Modifier.isPublic(method.getModifiers())));
        }
    }
}

Model.java

// Model.java    
public class Model
{
    public String getMessage()
    {
        return "Hello";
    }

    public String getAnotherMessage()
    {
        return "Hello World!";
    }
}

This example does not work at all. Even changing BeansWrapper during runtime won't have any effect.

解决方案

BeansWrapper (and DefaultObjectWrapper's, etc.) introspection cache relies on java.beans.Introspector.getBeanInfo(aClass), not on reflection. (That's because it treats objects as JavaBeans.) java.beans.Introspector has its own internal cache, so it can return stale information, and in that case BeansWrapper will just recreate its own class introspection data based on that stale information. As of java.beans.Introspector's caching, it's in fact correct, as it builds on the assumption that classes in Java are immutable. If something breaks that basic rule, it should ensure that java.beans.Introspector's cache is cleared (and many other caches...), or else it's not just FreeMarker that will break. At JRebel for example they made a lot of effort to clear all kind of caches. I guess DCEVM doesn't have the resources for that. So then, it seems you have to call Introspector.flushCaches() yourself.

Update: For a while (Java 7, maybe 6) java.beans.Introspector has one cache per thread group, so you have call flushCaches() from all thread groups. And this all is actually implementation detail that, in principle, can change any time. And sadly, the JavaDoc of Introspector.flushCaches() doesn't warn you...

这篇关于模型热交换后,Freemarker removeIntrospectionInfo无法与DCEVM一起使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆