模型热插拔后,Freemarker removeIntrospectionInfo 不适用于 DCEVM
Freemarker removeIntrospectionInfo does not work with DCEVM after model hotswap
我正在使用 Freemarker 和 DCEVM+HotSwapManager 代理。这基本上允许我在 adding/removing 方法时热交换 classes。
在 Freemarker 使用热交换 class 作为模型之前,一切都像魅力一样。它抛出 freemarker.ext.beans.InvalidPropertyException: No such bean 属性 即使反射显示该方法存在(在调试会话期间检查)。
我正在使用
final Method clearInfoMethod = beanWrapper.getClass().getDeclaredMethod("removeIntrospectionInfo", Class.class);
clearInfoMethod.setAccessible(true);
clearInfoMethod.invoke(clazz);
清除缓存,但不起作用。我什至尝试获取 classCache 成员字段并使用反射清除它,但它也不起作用。
我做错了什么?
我只需要强制 freemarker 放弃对他已经获得的模型 class/classes 的任何自省。
有什么办法吗?
更新
示例代码
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)
,而不是反射。 (那是因为它将对象视为 JavaBean。)java.beans.Introspector
有自己的内部缓存,因此它可以 return 陈旧的信息,在这种情况下 BeansWrapper
将重新创建它的拥有 class 基于该陈旧信息的内省数据。至于 java.beans.Introspector
的缓存,它实际上是正确的,因为它建立在 Java 中的 classes 是不可变的假设之上。如果有什么东西违反了这个基本规则,它应该确保 java.beans.Introspector
的缓存被清除(以及许多其他缓存......),否则不仅仅是 FreeMarker 会破坏。例如,在 JRebel,他们付出了很多努力来清除各种缓存。我想 DCEVM 没有这方面的资源。那么,看来还是得自己打电话给Introspector.flushCaches()
了
更新:有一段时间(Java 7,也许 6)java.beans.Introspector
每个线程有一个缓存 group,所以你调用 flushCaches()
来自所有线程组。而这一切实际上都是实施细节,原则上可以随时更改。遗憾的是,Introspector.flushCaches()
的 JavaDoc 没有警告您...
我正在使用 Freemarker 和 DCEVM+HotSwapManager 代理。这基本上允许我在 adding/removing 方法时热交换 classes。
在 Freemarker 使用热交换 class 作为模型之前,一切都像魅力一样。它抛出 freemarker.ext.beans.InvalidPropertyException: No such bean 属性 即使反射显示该方法存在(在调试会话期间检查)。
我正在使用
final Method clearInfoMethod = beanWrapper.getClass().getDeclaredMethod("removeIntrospectionInfo", Class.class);
clearInfoMethod.setAccessible(true);
clearInfoMethod.invoke(clazz);
清除缓存,但不起作用。我什至尝试获取 classCache 成员字段并使用反射清除它,但它也不起作用。
我做错了什么? 我只需要强制 freemarker 放弃对他已经获得的模型 class/classes 的任何自省。
有什么办法吗?
更新
示例代码
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)
,而不是反射。 (那是因为它将对象视为 JavaBean。)java.beans.Introspector
有自己的内部缓存,因此它可以 return 陈旧的信息,在这种情况下 BeansWrapper
将重新创建它的拥有 class 基于该陈旧信息的内省数据。至于 java.beans.Introspector
的缓存,它实际上是正确的,因为它建立在 Java 中的 classes 是不可变的假设之上。如果有什么东西违反了这个基本规则,它应该确保 java.beans.Introspector
的缓存被清除(以及许多其他缓存......),否则不仅仅是 FreeMarker 会破坏。例如,在 JRebel,他们付出了很多努力来清除各种缓存。我想 DCEVM 没有这方面的资源。那么,看来还是得自己打电话给Introspector.flushCaches()
了
更新:有一段时间(Java 7,也许 6)java.beans.Introspector
每个线程有一个缓存 group,所以你调用 flushCaches()
来自所有线程组。而这一切实际上都是实施细节,原则上可以随时更改。遗憾的是,Introspector.flushCaches()
的 JavaDoc 没有警告您...