Java:动态属性

女士们先生们,

我是Java的新手,请原谅我,如果这很明显,但我还没发现很多。

我想在运行时为类创建动态属性(变量)(定义一个可以在运行时通过添加或更改属性和方法来更改的对象)。


原因:我想在GAE中存储一个数据模型,可以在编译应用程序后动态扩展(是的,DataStore允许)。 应该添加哪些属性也存储在DataStore中(这就像使用机器人来构建机器人……有趣)。

Python允许我在运行时添加属性。 Groovy似乎也允许这样做。 “纯粹的”Java世界中唯一指向该方向的东西似乎是“动态代理”。

但是,如果他们能做到这一点,我还无法弄明白。

Java无法动态添加属性。 它也没有能力在运行时动态创建类或在运行时更改它们。 Java强烈且静态地键入。 您可以做的最好的事情是将这些属性放入Map或类似的地方。

编辑:好的,显然有些澄清是有序的。 OP特别提到GAE,这些方法都不会起作用,但我会提到它们,因为有些人似乎对他们的缺席不以为然。

Java Compiler API (Java 6+)允许您在运行时编译Java类。 从技术上讲,您可以编写一个Java源文件来查看您想要的内容,编译并加载它。

Java字节码库可以在运行时重写类。 这被JPA(和其他)等库使用。 你可以用这种方式修改类。

然而,OP指的是a)参考GAE和b)更多按照Javascript如何允许您通过动态添加,删除或更改属性在运行时修改类或特定实例的顺序。 Java肯定不会这样做,特别是在GAE上没有。

以上情况也不例外,就像在C ++中将类转换为char * ,因此您可以阅读私有成员并不意味着C ++没有私有成员。 您实际上是使用这两种方法绕过Java运行时,即使它们是Java的一部分。

Java不支持它。 最好的办法是在一些外部数据存储中存储/管理,您可以从Java代码中访问这些数据存储。 作为基本和内置示例,您可以使用java.util.Properties API,您可以在每个请求上加载,或者以定时间隔缓存和重新加载,或者以编程方式重新加载。 然后,您可以将键值对存储在.properties文件中,该文件只放在类路径中。 这是关于该主题的Sun教程 。

属性文件可能看起来像

键1 =值
键2 =值
 KEY3 =值3

如果将它放在类路径中,则可以将其加载为

 Properties properties = new Properties(); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); properties.load(classLoader.getResourceAsStream("file.properties")); String key1 = properties.getProperty("key1"); // value1 

其他替代方法是例如XML文件(您可以使用任何Java XML API访问)或仅使用数据库(您可以使用JDBC API访问)。

我不知道这是否是GAE的一个选项(我没有检查限制),如果这符合您的需求,但可能会看看CGLIB中的BeanGenerator类( BeanUtils中丑陋的DynaBean的替代品)。 引用“DynaBeans的死亡” (看看post):

不是让我的CGLIB Golden Hammer浪费,我已经将BeanGenerator类签入了CVS。 你这样使用它:

 BeanGenerator bg = new BeanGenerator(); bg.addProperty("foo", Double.TYPE); bg.addProperty("bar", String.class); Object bean = bg.create(); 

生成的类是一个真正的 JavaBean,这意味着您可以使用标准bean实用程序。 这包括net.sf.cglib.beans包( BeanCopierBeanMapBulkBean )中的所有类。 尽你所能结束DynaBeans的暴政!

可以使用Dynamic Proxies。 也可以在GAE上执行此操作。

首先创建类“SomeObject”,公开获取和设置属性值的方法(即getProperty(name)和setProperty(name,value))。

然后,创建一个接口“PropertyModel”,其中包含您希望生成的对象具有的方法。

调用TransparentProxy.newInstance(someObjectInstance,MyPropertyModel.class)来创建动态代理。

会发生什么是Java将使用指定的接口扩展您的对象someObjectInstance(顺便说一下。您可以指定多个)。 当您在代理对象上调用方法时,方法调用将被重定向到下面定义的“invoke(…)”方法,您需要修改该代码以处理getter和setter并包含一些exception处理等但总的来说,这是动态代理在Java中的工作方式。

 public class TransparentProxy implements InvocationHandler { private final SomeObject someObject; private TransparentProxy(SomeObject someObject) { this.someObject = someObject; } public static Object newInstance(SomeObject someObject, Class propertyModel) { return Proxy.newProxyInstance(someObject.getClass().getClassLoader(), new Class[] { propertyModel }, new TransparentProxy(someObject)); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return this.someObject.getProperty(method.getName()); } } 

有一个DynaClass库可以用这种方式动态创建JavaBeans

 Map properties = new HashMap(); roperties.put("title", "The Italian Job"); roperties.put("dateOfRelease", "new GregorianCalendar(1969, 0, 1).getTime()"); Object movieBean = BeanCreator.createBeanFromMap(properties);