问题
I'd like to create a subclass programatically. I guess I have few options - Javassist, CGLib, BCEL, or ASM.
The use case is that one app's internals are class-oriented, and extensions are class-based. Therefore I can't have a single class as a base for multiple extensions driven by externalized scripts.
Now - how would I do that? I've found examples with intercepting method calls, field access, initialization etc. But nothing about subclassing.
I'd like to end up with a class which:
- has a name which I want.
- is a (direct, at best) subclass of a given class
- copies the constructor(s) from the parent class (or calls
super(...)
) - eventually, I'd like to give it some annotations.
I know it's possible because various dynamic languages integrations, like GroovyClassLoader
, can do that.
回答1:
One library that I'm particularly fond of may be used here; Bytebuddy.
Example taken directly from the landing page:
Class<?> dynamicType = new ByteBuddy()
.subclass(Object.class)
.method(ElementMatchers.named("toString"))
.intercept(FixedValue.value("Hello World!"))
.make()
.load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
It's incredibly flexible and definitely worth checking out if you'd like to keep your hair, I personally find heavy usage of javassist can get quite ugly and messy at times, bytebuddy feels like a well needed breath of fresh air!
Rafael Winterhalter is also active on StackOverflow which makes finding out anything you're unsure of a breeze.
Edit: my apology for necroposting. Landed here when a friend linked the question and forgot to check the date.
回答2:
It's quite easy with Javassist:
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
static Class<? extends DefinitionBasedMigrator> createClass( String fullName )
throws NotFoundException, CannotCompileException
{
ClassPool pool = ClassPool.getDefault();
// Create the class.
CtClass subClass = pool.makeClass( fullName );
final CtClass superClass = pool.get( DefinitionBasedMigrator.class.getName() );
subClass.setSuperclass( superClass );
subClass.setModifiers( Modifier.PUBLIC );
// Add a constructor which will call super( ... );
CtClass[] params = new CtClass[]{
pool.get( MigratorDefinition.class.getName() ),
pool.get( GlobalConfiguration.class.getName())
};
final CtConstructor ctor = CtNewConstructor.make( params, null, CtNewConstructor.PASS_PARAMS, null, null, subClass );
subClass.addConstructor( ctor );
return subClass.toClass();
}
Maven dependency:
<!-- https://mvnrepository.com/artifact/org.javassist/javassist -->
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.22.0-GA</version>
</dependency>
回答3:
Java Proxies may be able to do what you require - they essentially allow you to dynamically layer functionality on top of an object, as you can intercept any method calls to that object, and either handle them yourself or dispatch the method calls to the underlying class. Depending on what you are looking to do, it may be that you can get the same result as you would by creating a sub-class dynamically
Oracle has a decent introduction on their website (the URL references Java version 1.4.2, but I don't think the behavior of this has changed in more recent versions). Here is a more concise example that gives a good flavor for what proxy code looks like.
It is also possible to do things using direct byte code manipulation (as supported by the ASM framework) however I imagine using proxies would be a simpler approach.
来源:https://stackoverflow.com/questions/17259421/java-creating-a-subclass-dynamically