Is there any simple way, to load a class given a class instance?
I know that I can load a class by calling
Class.forName(nameOfClass);
where the nameOfClass is the name of the class which I want to be loaded.
I also know that by instantiating a class this way
clazz.newInstance();
where clazz is an instance of Class, the class loads if it was not loaded before, but I don't want to instantiate if it's not necessary.
My question is that, is there any simple way to load a class given a Class instance?
I could solve the problem this way:
try {
Class.forName(clazz.getName());
} catch (ClassNotFoundException ex) {
assert false : "No way!";
}
but I think this is overcomplicated.
From your comment:
I don't want to instantiate... just load
the class – Bartis Áron 15 mins ago
Then you can just do fallow:
Add method yo your class:
public static final void init() {
}
And then just call it:
YourClass.init();
Related
I hava A Class in another jar and i use it in B bean.
Now i want to add log for method in A Class. How can i do this in my project without fix the jar.
My mind:
use ApplicationListener to redefine class before bean init.
do something in onApplicationEvent() to redefine the A class. // this is my question.
I know that can use asm or other tool to fix bytecode. I hava see instrument and do not find solution . https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/Instrumentation.html
How to obtain instance of Instrumentation in Java
A class.
public class A {
public void find(){
System.out.println("aaa");
//i want to add log here.
}
B bean
#Service public class B {
public A get(){
return new A();
}
ApplicationListener
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if(event.getApplicationContext().getParent() != null){
return;
}
// redefine class
}
then when i use b.get().find() it will print the log i add.
find my solution.
using javassist modify the class. and load it (using CtClass.toClass())in advance.
https://www.javassist.org/tutorial/tutorial.html
I am trying to access annotations of a class before applying some transformation in a java agent implemented with ByteBuddy.
To access the annotations, I am trying to load the Class object, but it seems that this creates a duplicate class definition.
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.matcher.ElementMatchers;
import java.lang.instrument.Instrumentation;
public class SimpleTestAgent {
public static void premain(String arg, Instrumentation inst) {
new AgentBuilder.Default()
.type(ElementMatchers.isAnnotatedWith(SomeAnnotationType.class))
.transform((builder, type, classLoader, javaModule) -> {
try {
Class loadedClass = Class.forName(type.getName(), true, classLoader);
} catch (ClassNotFoundException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return builder;
})
.installOn(inst);
}
}
A simple class that just creates an instance of the class TestClass that is annotated with the expected annotation, throws the following exception:
Exception in thread "main" java.lang.LinkageError: loader (instance of
sun/misc/Launcher$AppClassLoader): attempted duplicate class
definition for name: "TestClass"
public class AgentTest {
public static void main (String[] args) {
TestClass pet = new TestClass();
}
}
I am trying to implement the agent like in the example described in: Easily-Create-Java-Agents-with-ByteBuddy
Is there a way to load the Class object without causing this issue or a way to access the annotations using the arguments passed to the transformation() method?
The transformation should be able to implement new interfaces and not only override methods this is why I think I cannot use "ForAdvice".
UPDATE
The following loop finds the TestClass only after the Class.forName() is executed. This means that the class has not been loaded and so probably there is no hope to use Class.forName to get the annotations.
for (Class<?> t : inst.getAllLoadedClasses()) {
System.out.println("Class name: " + t.getName());
}
It is possible to get the annotations and the full information about a class using the net.bytebuddy.description.type.TypeDescription instance passed to the transform() method.
The problem is that, for example, I need the Method objects that can be called using reflection. It would be easier if I could somehow access the Class object for the class that is being transformed.
Byte Buddy exposes all information on annotations of a class via the TypeDescription API already, you should not load classes as a class that is loaded during a transformation loads this class before the transformation is applied and aborts class loading with the error you observe. Instead, implement your own matcher:
.type(type -> check(type.getDeclaredAnnotations().ofType(SomeAnnotation.class).load())
Byte Buddy will represent the annotation for you without loading the carrier class itself but rather represent it using its own class file processor.
You should make sure the annotation class is loaded before installing your agent builder to avoid a circularity for this exact annotation.
Let's say I want to create a custom class at runtime that another class can make use of.
package redefineconcept;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import java.lang.reflect.InvocationTargetException;
public class LoadingTest {
public static final String HelloWorldTag = "$HelloWorld";
public static void main(String[] args){
new LoadingTest().run();
}
private void run(){
InstanceUser u = new InstanceUser();
u.start();
Class <?> createdClass = createAndLoadFor(InstanceUser.class);
System.out.println(String.format("created new class %s", createdClass.getName()));
InstanceUser.canAccess = true;
try {
u.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private Class<?> createAndLoadFor(Class<?> clazz){
ByteBuddy byteBuddy = new ByteBuddy();
String newClassName = clazz.getName() + LoadingTest.HelloWorldTag;
DynamicType.Builder builder = byteBuddy
.subclass(Object.class)
.name(newClassName)
;
DynamicType.Unloaded<?> newType = builder.make();
return newType
.load(clazz.getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
.getLoaded();
}
}
class InstanceUser extends Thread{
public static volatile boolean canAccess = false;
Object instance;
#Override
public void run() {
while(!canAccess){}
String cn = this.getClass().getName() + LoadingTest.HelloWorldTag;
Class clazz;
try{
clazz = Class.forName(cn);
}catch(ClassNotFoundException e){
e.printStackTrace();
throw new RuntimeException();
}
try{
instance = clazz.getConstructor().newInstance();
}catch(NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e){
e.printStackTrace();
throw new RuntimeException();
}
}
}
This works.
However, the ByteBuddy tutorial, suggests
You might consider the chance of encountering circular dependencies to be of minor relevance since you are creating one dynamic type at a time. However, the dynamic creation of a type might trigger the creation of so-called auxiliary types.
These types are created by Byte Buddy automatically to provide access to the dynamic type you are creating.
because of this, we recommend you to load dynamically created classes by creating a specific ClassLoader instead of injecting them into an existing one, whenever possible.
I don't know terribly much about classloaders -- or ByteBuddy, for that matter -- but the tutorial seems to suggest that classloaders are hierachically ordered.
If so, it should be possbible to chain the new class loader to clazz.getClassLoader(), right?
Well, I've had no such luck with neither ClassLoadingStrategy.Default.WRAPPER nor ClassLoadingStrategy.Default.CHILD_FIRST.
Both result in
created new class redefineconcept.InstanceUser$HelloWorld
java.lang.ClassNotFoundException: redefineconcept.InstanceUser$HelloWorld
which led me to believe that
Normally, Java class loaders query their parent ClassLoader before attempting to directly load a type of a given name.
means they only query the parent ClassLoaders but not the children.
Is that so?
And is it at all possible to avoid using ClassLoadingStrategy.Default.INJECTION, here?
Class loaders are (normally) hierarchical. If you are using the INJECTION strategy, Byte Buddy manually defines type by type by explicitly defining the classes. Depending on the JVM and class loader, this might trigger a class loading.
Consider a situation where A references B and B references A. If Byte Buddy injects A before B, the injection of A might cause a loading of B which is not yet injected at that point. At this point, the class loader that is the target of the injection will prematurly and unsuccessfully try to look up B and fail with a NoClassDefFoundError.
When using the WRAPPER strategy, Byte Buddy creates a new class loader that is aware of both types and can look up B when A is loaded as no injection is required.
The problem you encounter is caused by your use of Class.forName(name). This method is caller sensitive meaning that the calling class's class loader is used. From your thread, this will most likely be the system class loader which is the same class loader you injected before.
That said, typically a JVM is loading types lazily and injection should not render a big problem for 99% of all use cases.
I am having a small issue calling a Class file into my main class
I want to have a separate class to contain all my recipes, but i need to call it up in the common class.
The Recipe is called Recipes.
How would i call that class to load?
The functions loads on #PreInit
#EventHandler
public void preInit(FMLPreInitializationEvent event) { }
Should i change this to Init or #PostInit, as the blocks are all created in #PreInit and #Init is completely empty ?
you can just instanciate this class and use as you want.
Recipes recipes = new Recipes();
in this case is not necessary a injection
you can still get it with the constructor like:
Class YourClass{
//do what you need here, for example instanciate the class and execute something.
public YourClass(){
}
}
You can also create a static class if you prefer.
I am attempting to load a groovy class by name using a classloader, and the class fails to load in the case that the class has a reference to a static inner class in another class.
Inside my groovy class I have the following:
def classLoader = getClass().classLoader
try {
classLoader.loadClass( "com.test.TestClass" )
} catch(Throwable e) {
Sigil.logger.error("Error loading class: $it >> ${e.message}", e)
}
In the above, my groovy file TestClass has a static inner class inside it, that extends a static inner class of another file. When I try to run the above code I get the message:
ERROR [05 Aug 2013 06:53:28,851] (invoke0:?) - Error loading class: com.test.TestClass >> startup failed:
unable to resolve class UserValidity.Validator
# line 85, column 5.
public static class Validator extends UserValidity.Validator{
^
1 error
Has anyone come across any problems dealing with static inner classes and class loading in groovy before? The classes all compile correctly and unit tests run etc. I would have thought that when I try to load the class TestClass explicitly in my classloader, it would also load the other necessary classes from the source tree as needed?
UPDATE:
Here is a snippet of the class that is failing to load:
class TestClass{
//... Other normal class stuff here
public static class Validator extends UserValidity.Validator
#Override
def validate(u) {
def result = super.validate(u)
if(!u.valid ){
result += [isValid:false]
}
result
}
}
}
And this fails as it says it cannot resolve the reference to the UserValidity.Validator, which is also pretty simple:
class UserValidity {
//normal class stuff here
public static class Validator {
def validate(u){
//do validation stuff
result
}
}
}
Both are just regular groovy classes.
UPDATE 2:
If I extract the static inner class UserValidity.Validator out in to a standalone class, and just extend that with the static inner class in TestClass then it appears to work, so definitely seems to be some issue with the parent of the inner class being another inner class