java反射动态_Java反射高级特性---动态加载

It is possible to load and reload classes at runtime in Java,

though it is not as straightforward as one might have hoped. This

text will explain when and how you can load and reload classes in

Java.

You can argue whether Java's dynamic class loading features are

really part of Java Reflection, or a part of the core Java

platform. Anyways, the article has been put in the Java Reflection

trail in lack of a better place to put it.

Here is a list of the topics covered in this text:

The ClassLoader

All classes in a Java application are loaded using some subclass

of java.lang.ClassLoader. Loading classes dynamically

must therefore also be done using a

java.lang.ClassLoader subclass.

When a class is loaded, all classes it references are loaded

too. This class loading pattern happens recursively, until all

classes needed are loaded. This may not be all classes in the

application. Unreferenced classes are not loaded until the time

they are referenced.

The ClassLoader Hierarchy

Class loaders in Java are organized into a hierarchy. When you

create a new standard Java ClassLoader you must

provide it with a parent ClassLoader. If a

ClassLoader is asked to load a class, it will ask its

parent class loader to load it. If the parent class loader can't

find the class, the child class loader then tries to load it

itself.

Class Loading

The steps a given class loader uses when loading classes

are:

Check if the class was already loaded.

If not loaded, ask parent class loader to load the class.

If parent class loader cannot load class, attempt to load it in

this class loader.

When you implement a class loader that is capable of reloading

classes you will need to deviate a bit from this sequence. The

classes to reload should not be requested loaded by the parent

class loader. More on that later.

Dynamic Class Loading

Loading a class dynamically is easy. All you need to do is to

obtain a ClassLoader and call its

loadClass() method. Here is an example:

public class MainClass {

public static void main(String[] args){

ClassLoader classLoader = MainClass.class.getClassLoader();

try {

Class aClass = classLoader.loadClass("com.jenkov.MyClass");

System.out.println("aClass.getName() = " + aClass.getName());

} catch (ClassNotFoundException e) {

e.printStackTrace();

}

}

Dynamic Class Reloading

Dynamic class reloading is a bit more challenging. Java's

builtin Class loaders always checks if a class is already loaded

before loading it. Reloading the class is therefore not possible

using Java's builtin class loaders. To reload a class you will have

to implement your own ClassLoader subclass.

Even with a custom subclass of ClassLoader you have

a challenge. Every loaded class needs to be linked. This is done

using the ClassLoader.resolve() method. This method is

final, and thus cannot be overridden in your

ClassLoader subclass. The resolve()

method will not allow any given ClassLoader instance

to link the same class twice. Therefore, everytime you want to

reload a class you must use a new instance of your

ClassLoader subclass. This is not impossible, but

necessary to know when designing for class reloading.

Designing your Code for Class Reloading

As stated earlier you cannot reload a class using a

ClassLoader that has already loaded that class once.

Therefore you will have to reload the class using a different

ClassLoader instance. But this poses som new

challenges.

Every class loaded in a Java application is identified by its

fully qualified name (package name + class name), and the

ClassLoader instance that loaded it. That means, that

a class MyObject loaded by class loader A, is not the

same class as the MyObject class loaded with class

loader B. Look at this code:

MyObject object = (MyObject)

myClassReloadingFactory.newInstance("com.jenkov.MyObject");

Notice how the MyObject class is referenced in the

code, as the type of the object variable. This causes

the MyObject class to be loaded by the same class

loader that loaded the class this code is residing in.

If the myClassReloadingFactory object factory

reloads the MyObject class using a different class

loader than the class the above code resides in, you cannot cast

the instance of the reloaded MyObject class to the

MyObject type of the object variable.

Since the two MyObject classes were loaded with

different class loaders, the are regarded as different classes,

even if they have the same fully qualified class name. Trying to

cast an object of the one class to a reference of the other will

result in a ClassCastException.

It is possible to work around this limitation but you will have

to change your code in either of two ways:

Use an interface as the variable type, and just reload the

implementing class.

Use a superclass as the variable type, and just reload a

subclass.

Here are two coresponding code examples:

MyObjectInterface object = (MyObjectInterface)

myClassReloadingFactory.newInstance("com.jenkov.MyObject");

MyObjectSuperclass object = (MyObjectSuperclass)

myClassReloadingFactory.newInstance("com.jenkov.MyObject");

Either of these two methods will work if the type of the

variable, the interface or superclass, is not reloaded when the

implementing class or subclass is reloaded.

To make this work you will of course need to implement your

class loader to let the interface or superclass be loaded by its

parent. When your class loader is asked to load the

MyObject class, it will also be asked to load the

MyObjectInterface class, or the

MyObjectSuperclass class, since these are referenced

from within the MyObject class. Your class loader must

delegate the loading of those classes to the same class loader that

loaded the class containing the interface or superclass typed

variables.

ClassLoader Load / Reload Example

The text above has contained a lot of talk. Let's look at a

simple example. Below is an example of a simple

ClassLoader subclass. Notice how it delegates class

loading to its parent except for the one class it is intended to be

able to reload. If the loading of this class is delegated to the

parent class loader, it cannot be reloaded later. Remember, a class

can only be loaded once by the same ClassLoader

instance.

As said earlier, this is just an example that serves to show you

the basics of a ClassLoader's behaviour. It is not a

production ready template for your own class loaders. Your own

class loaders should probably not be limited to a single class, but

a collection of classes that you know you will need to reload. In

addition, you should probably not hardcode the class paths

either.

public class MyClassLoader extends ClassLoader{

public MyClassLoader(ClassLoader parent) {

super(parent);

}

public Class loadClass(String name) throws ClassNotFoundException {

if(!"reflection.MyObject".equals(name))

return super.loadClass(name);

try {

String url = "file:C:/data/projects/tutorials/web/WEB-INF/" +

"classes/reflection/MyObject.class";

URL myUrl = new URL(url);

URLConnection connection = myUrl.openConnection();

InputStream input = connection.getInputStream();

ByteArrayOutputStream buffer = new ByteArrayOutputStream();

int data = input.read();

while(data != -1){

buffer.write(data);

data = input.read();

}

input.close();

byte[] classData = buffer.toByteArray();

return defineClass("reflection.MyObject",

classData, 0, classData.length);

} catch (MalformedURLException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

return null;

}

}

Below is an example use of the MyClassLoader.

public static void main(String[] args) throws

ClassNotFoundException,

IllegalAccessException,

InstantiationException {

ClassLoader parentClassLoader = MyClassLoader.class.getClassLoader();

MyClassLoader classLoader = new MyClassLoader(parentClassLoader);

Class myObjectClass = classLoader.loadClass("reflection.MyObject");

AnInterface2 object1 =

(AnInterface2) myObjectClass.newInstance();

MyObjectSuperClass object2 =

(MyObjectSuperClass) myObjectClass.newInstance();

//create new class loader so classes can be reloaded.

classLoader = new MyClassLoader(parentClassLoader);

myObjectClass = classLoader.loadClass("reflection.MyObject");

object1 = (AnInterface2) myObjectClass.newInstance();

object2 = (MyObjectSuperClass) myObjectClass.newInstance();

}

Here is the reflection.MyObject class that is loaded

using the class loader. Notice how it both extends a superclass and

implements an interface. This is just for the sake of the example.

In your own code you would only have to one of the two - extend or

implement.

public class MyObject extends MyObjectSuperClass implements AnInterface2{

//... body of class ... override superclass methods

// or implement interface methods

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值