在java spring中,是否可以动态将一个新的class加载到beanFactory中?
阿神
阿神 2017-04-18 10:26:24
0
5
488

在java spring中,是否可以动态将一个新的class加载到beanFactory中?是否可以将class类以字节流的方式存到redis中,再由类加载器重新加载到java运行实例中?
目前项目中有一个需求,需要可以动态地加载类到spring的beanFactory中或者说applicationContext里面,这个类是可以由开发人员动态上传到生产环境中,而无需重新启动生产环境。请问是否可以做到呢?还有一个问题,是否可以将class文件以字节的方式暂存在redis中间件上,需要用这个class时,动态地加载它?

阿神
阿神

闭关修行中......

全部回覆(5)
PHPzhong

在JDK1.5之後要實現這個功能有現成的API java.lang.instrument.Instrumentation。

java.lang.instrument.Instrumentation.redefineClasses(ClassDefinition... definitions)

上面的介面就可以重定義已經存在的class
像下面的方式使用。

private void redefineScripts(File dir) {
    File[] files = dir.listFiles();
    for (File f : files) {
        if (f.isDirectory()) {
            redefineScripts(f);
        } else if (f.getName().endsWith(".class")) {
            String name = getScriptCanonicalName(f);
            String path = name.replaceAll("\.", "/");
            File target = new File(targetDir, path + ".class");

            try {
                InputStream in = new FileInputStream(target);
                byte[] buf = StreamUtils.copyToByteArray(in);
                ClassDefinition cdef = new ClassDefinition(scriptClassLoader.loadClass(name), buf);
                instrumentation.redefineClasses(cdef);
            } catch (Exception ex) {
                throw new ScriptException(ex);
            }
        }
    }
}

取得Instrumentation物件的方法:

private void loadAgent() {
    try {
        String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
        VirtualMachine vm = VirtualMachine.attach(pid);
        vm.loadAgent("<jar file>", OBJECT_NAME.toString());
        vm.detach();
    } catch (Exception ex) {
        throw new RuntimeException(
                "无法加载代理JAR文件["
                + Typhons.getProperty(Constants.AGENT_JAR_PATH) + "]", ex);
    }
}

agent.jar打包时需要指定Agent-ClassCan-Redefine-Classes開啟類別重寫義功能。

<plugin>
    <artifactId>maven-jar-plugin</artifactId>
    <configuration>
        <archive>
            <manifest>
                <addClasspath>true</addClasspath>
            </manifest>
            <manifestEntries>
                <Agent-Class>org.skfiy.typhon.agent.Agent</Agent-Class>
                <Can-Redefine-Classes>true</Can-Redefine-Classes>
            </manifestEntries>
        </archive>
    </configuration>
</plugin>

Agent.java 實作

public class Agent {

    /**
     * 代理主函数入口. 通过传入的{@code str }{@code ObjectName }执行其{@code setInstrumentation }
     * 方法.
     * <p>
     * e.g.<pre>
     * public void setInstrumentation(Instrumentation instrumentation) {
     *  this.instrumentation = instrumentation;
     * }
     * </pre>
     * 
     * @param str 一个{@link ObjectName }字符串
     * @param inst {@link Instrumentation }实现
     * @throws Exception 异常
     */
    public static void agentmain(String str, Instrumentation inst) throws Exception {
        ObjectName objectName = ObjectName.getInstance(str);
        MBeanServer mbeanServer = MBeanServerFactory.findMBeanServer(null).get(0);
        mbeanServer.invoke(objectName, "setInstrumentation",
                new Object[]{inst},
                new String[]{Instrumentation.class.getName()});
    }
}

這裡我是透過JMXJMXInstrumentation对象注入过去的,当时为什么要这样来做呢。
是因为vm.loadAgent();运行的环境是一个全新的,我无法使用静态方法来设置属性,虽然类相同但是加载该个类的ClassLoader却不一致。后面我尝试出可以使用JMX物件注入過去的,當時為什麼要這樣來做。

是因為vm.loadAgent();運行的環境是一個全新的,我無法使用靜態方法來設定屬性,雖然類別相同但是加載該類別的ClassLoader卻不一致。 後面我嘗試出可以使用JMX的方式呼叫。

Instrumentation好了,只要拿到

對象,你可以做什麼你之前不能做的事情。 🎜
Ty80

試試看透過ApplicationContext取得AutowireCapableBeanFactory, 然後呼叫createBean()或autowire()方法注入

刘奇

java有object輸入輸出流,所以你把物件以位元組的方式存起來,一定是可以的

阿神

樓上說的都不對。可以明確的告訴你,都是可以的。

刘奇

mark 學習下

熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板