Can a Java class add a method to itself at runtime

2020-01-23 04:15发布

Can a class add a method to itself at runtime (like from a static block), so that if someone is performing reflection on this class, they'll see the new method, even though it wasn't defined at compile time?

Background:

A framework I'm using expects Action classes to be defined that have a doAction(...) method, by convention. The framework inspects these classes at runtime to see what type of parameters are available in their doAction() method. For example: doAction(String a, Integer b)

I'd like each class to be able to programatically generate its doAction() method with various parameters, just-in-time when it is inspected. The body of the method can be empty.

10条回答
相关推荐>>
2楼-- · 2020-01-23 04:15

I believe you need some byte code altering tool/framework, such as asm, cglib or javassist. You can achieve this via aspects/weaving like it's done Spring, but I believe you still need to have the method defined first.

查看更多
女痞
3楼-- · 2020-01-23 04:16

Andres_D is right, we can very well do so using custom class loading, here is a detailed guide on how to do this: http://www.javaworld.com/javaworld/jw-06-2006/jw-0612-dynamic.html?page=1

The article explains how to write dynamic Java code. It discusses runtime source code compilation, class reloading, and the use of the Proxy design pattern to make modifications to a dynamic class transparent to its caller.

In fact researcher in Austria have written a JVM that even allows reloading classes with different type hierarchies. They have achieved this by using existing thread save points to generate a complete 'side universe' of an object and all it's related references and referenced content and then once fully reshuffled with all required changes simply swap in all changed classes. [1] Here a link to their project http://ssw.jku.at/dcevm/ the oracle sponsorship certainly makes for interesting speculations on future plans.

Less intrusive changes to method bodies and fields are already possible in the standard java VM using the Hot Swap capabilities of the JPDA as introduced in Java 1.4:
docs.oracle.com/javase/1.4.2/docs/guide/jpda/enhancements.html#hotswap

I'm not sure whether it was the first one but this Sun employee's paper from 2001 appears to be one of the early proposals mentioning the capabilities of the HotSpot to Hot Swap. [2]

REFERENCE

[1] T. Würthinger, C. Wimmer, and L. Stadler, “Dynamic Code Evolution for Java,” presented at the 8th International Conference on the Principles and Practice of Programming in Java, Vienna, 2010.

[2] M. Dmitriev, “Towards flexible and safe technology for runtime evolution of java language applications,” in OOPSLA Workshop on Engineering Complex Object-Oriented Systems for Evolution, 2001.

查看更多
你好瞎i
4楼-- · 2020-01-23 04:19

What I suggest should work for your situation: 1. You have an existing class MyClass with n methods 2. You want to include (n+1) th method which is not in the class while compiling in another .java source file

My way to solve it is Inheritance. Create a new .java source file for a Class MyClassPlusOne extending the first class MyClass. Compile this class and use the object. How can I compile and deploy a java class at runtime?

class MyClassPlusOne extends MyClass
{
     void doAction(String a, Integer b)
     {
         int myNPlus1 = a+b;
         //add whatever you want before compiling this code
      }
}
查看更多
我只想做你的唯一
5楼-- · 2020-01-23 04:23

It looks like there is no way to add method dynamically. But you can prepare an class with a list of Methods or an hash like:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;

public class GenericClass {
    private HashMap<String, Method> methodMap = new HashMap<String, Method>();

    public Object call(String methodName,Object ...args) 
               throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Method method = methodMap.get(methodName);
        return method.invoke(null, args);
    }

    public void add(String name,Method method){
        if(Modifier.isStatic(method.getModifiers()))
            methodMap.put(name, method);
    }

    public static void main(String[] args) {
        try   {
            GenericClass task = new GenericClass();
            task.add("Name",Object.class.getMethod("Name", new Class<?>[0]));
        } catch (NoSuchMethodException | SecurityException e) {
            e.printStackTrace();
        }
   }
}

Than, using reflections you can set or unset the attribute.

查看更多
对你真心纯属浪费
6楼-- · 2020-01-23 04:25

Proxy may help. But have to instantiate a Proxy every time you want to add or remove a method.

查看更多
小情绪 Triste *
7楼-- · 2020-01-23 04:28

It's not simple. Once a class is loaded by a classloader, there is no way to change the methods of loaded classes. When a class is requested, a classloader will load it and link it. And there is no way (with Java) to change the linked code or to add/remove methods.

The only trick that comes to my mind is playing with classloaders. If we delete a custom classloader, then the classes loaded by that classloader should be deleted or inaccessible too. The idea that comes to my mind is to

  1. implement one custom classloader
  2. load the dynamic class with that custom classloader
  3. if we have an updated version of this class,
  4. remove the custom classloader and
  5. load the new version of this class with a new instance of the custom classloader

I leave that as food for thought, can't prove, if this leads to a solution or if we have pitfalls.

As a simple answer to the question: No, we can't change a loaded class like we can change the content of fields with reflection. (we can't add or remove fields too).

查看更多
登录 后发表回答