本地debug的时候,可以实时编译并更新代码,线上也可以不停服来动态更新类,即所说的java热部署。

 
JDK代理的两种方式:
1.premain方式是Java SE5开始就提供的代理方式,但其必须在命令行指定代理jar,并且代理类必须在main方法前启动,它要求开发者在应用启动前就必须确认代理的处理逻辑和参数内容等等
2.agentmain方式是JavaSE6开始提供,它可以在应用程序的VM启动后再动态添加代理的方式
 
agentmain应用场景:
比如正常的生产环境下,一般不会开启代理功能,但是在发生问题时,我们不希望停止应用就能够动态的去修改一些类的行为,以帮助排查问题,这在应用启动前是无法确定的
 
与Permain类似,agent方式同样需要提供一个agent jar,并且这个jar需要满足[可查看附件的jar文件]:
1.在manifest中指定Agent-Class属性,值为代理类全路径
2.代理类需要提供public static void agentmain(String args, Instrumentation inst)或public static void agentmain(String args)方法。并且再二者同时存在时以前者优先。args和inst和premain中的一致。
 
那如何在不停服的情况下动态修改类呢??
实现代码如下:

 Java Code 代码动态更改类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
 
public class JavaAgent {

public static final Logger logger = LoggerFactory.getLogger(JavaAgent.class);

private static String classesPath;
    private static String jarPath;
    private static VirtualMachine vm;
    private static String pid;

static {
        classesPath = JavaAgent.class.getClassLoader().getResource("").getPath();
        logger.error("java agent:classpath:{}", classesPath);
        jarPath = getJarPath();
        logger.error("java agent:jarPath:{}", jarPath);

// 当前进程pid
        String name = ManagementFactory.getRuntimeMXBean().getName();
        pid = name.split(];
        logger.error("当前进程pid:{}", pid);
    }

/**
     * 获取jar包路径
     * @return
     */
    public static String getJarPath() {
        // StringUtils是jar文件内容
        URL url = StringUtils.class.getProtectionDomain().getCodeSource().getLocation();
        String filePath = null;
        try {
            filePath = URLDecoder.decode(url.getPath(), "utf-8");// 转化为utf-8编码
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (filePath.endsWith(".jar")) {// 可执行jar包运行的结果里包含".jar"
            // 截取路径中的jar包名
            filePath = filePath.substring(, filePath.lastIndexOf();
        }

File file = new File(filePath);

filePath = file.getAbsolutePath();//得到windows下的正确路径
        return filePath;
    }

private static void init() throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException {
        // 虚拟机加载
        vm = VirtualMachine.attach(pid);
        vm.loadAgent(jarPath + "/javaagent.jar");

Instrumentation instrumentation = JavaDynAgent.getInstrumentation();
        Preconditions.checkNotNull(instrumentation, "initInstrumentation must not be null");
    }

private static void destroy() throws IOException {
        if (vm != null) {
            vm.detach();
        }
    }

/**
     * 重新加载类
     *
     * @param classArr
     * @throws Exception
     */
    public static void javaAgent(String root, String[] classArr) throws ClassNotFoundException, IOException, UnmodifiableClassException, AttachNotSupportedException, AgentLoadException, AgentInitializationException {
        init();

try {
            // 1.整理需要重定义的类
            List<ClassDefinition> classDefList = new ArrayList<ClassDefinition>();
            for (String className : classArr) {
                Class<?> c = Class.forName(className);
                String classPath = (StringUtils.isNotBlank(root) ? root : classesPath) + className.replace(".", "/") + ".class";
                logger.error("class redefined:" + classPath);
                byte[] bytesFromFile = Files.toByteArray(new File(classPath));
                ClassDefinition classDefinition = new ClassDefinition(c, bytesFromFile);
                classDefList.add(classDefinition);
            }
            // 2.redefine
            JavaDynAgent.getInstrumentation().redefineClasses(classDefList.toArray(new ClassDefinition[classDefList.size()]));
        } finally {
            destroy();
        }
    }

public static void main(String[] args) throws Exception {
        PortUtil.test();

javaAgent(null, new String[] {"com.agileeagle.webgame.framework.util.PortUtil"});

PortUtil.test();
    }
}

 

 Java Code Instrumentation如何获取?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 
public class JavaDynAgent {
    private static Instrumentation instrumentation;
    private static Object lockObject = new Object();

public JavaDynAgent() {
    }

public static void agentmain(String args, Instrumentation inst) {
        Object var2 = lockObject;
        synchronized(lockObject) {
            if(instrumentation == null) {
                instrumentation = inst;
                System.out.println("0->" + inst);
            } else {
                System.out.println("1->" + inst);
            }

}
    }

public static Instrumentation getInstrumentation() {
        return instrumentation;
    }
}

 
实现原理是:
1.绑定pid获得虚拟机对象,然后通过虚拟机加载代理jar包,这样就调用到agentmain,获取得到Instrumentation
2.基于Instrumentation接口可以实现JDK的代理机制,从而实现对类进行动态重新定义。
 
注意:com.sun.tools.attach.VirtualMachine的jar包是 jdk下lib中的tools.jar,所以项目中要引用到这个jar包,而且因为涉及到底层虚拟机,windows和linux机器这个jar不同
 
因此,整个流程就是:
1.项目中引用 jdk/lib/tools.jar,否则无法使用VirtualMachine类
2.项目中引用 javaagent.jar ,它提供了agentmain接口
3.代码实现动态增加JDK代理
 



JAVA代码热部署,在线不停服动态更新的更多相关文章

  1. springboot 不停服动态更新定时任务时间(转)

    转 https://blog.csdn.net/u012129558/article/details/80834303 Spring框架自3.0版本起,自带了任务调度功能,好比是一个轻量级的Quart ...

  2. paip.提升用户体验--提升java的热部署热更新能力

    paip.提升用户体验--提升java的热部署热更新能力 想让java做到php那么好的热部署能力  "fix online"/在线修复吗??直接在服务器上修改源码生效,无需重启应 ...

  3. Java代码自动部署

    注:本文来源于<it小熊> [ ①Java代码自动部署-总结简介] 代码部署是每一个软件开发项目组都会有的一个流程,也是从开发环节到发布功能必不可少的环节.对于Java开发者来说,Java ...

  4. IntelliJ IDEA配置Springboot2.x 通过devtools实现代码热部署,提高调试效率

    1.pom.xml添加依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifa ...

  5. 最简破解-java代码热加载热部署IDEA插件JRebel

    如果经济实力允许的话,还是建议大家去购买收费版.支持原创作者,才能有更好的产品出现. 一.Jrebel插件介绍 JRebel一款帮助我们在开发过程中实现热加载的插件,目前来说,在IDEA中实现热加载最 ...

  6. java的热部署和热加载

    ps:热部署和热加载其实是两个类似但不同的概念,之前理解不深,so,这篇文章重构了下. 一.热部署与热加载 在应用运行的时升级软件,无需重新启动的方式有两种,热部署和热加载. 对于Java应用程序来说 ...

  7. Java服务器热部署的实现原理

    转自:http://blog.csdn.net/chenjie19891104/article/details/42807959 在web应用开发或者游戏服务器开发的过程中,我们时时刻刻都在使用热部署 ...

  8. Jenkins + Maven + Ansible + Tomcat 实现JAVA代码自动部署

    自动部署过程: jenkins从svn拉取代码,调用maven去打war包,用ansible去解压war包,最后重启tomcat. 前情回顾:在前面的文章我的环境已经有Jenkins+ansible ...

  9. java 中 热部署与卸载关系

    今天发现早年在大象笔记中写的一篇笔记,之前放在ijavaboy上的,现在它已经访问不了了.前几天又有同事在讨论这个问题.这里拿来分享一下. 在web应用开发或者游戏服务器开发的过程中,我们时时刻刻都在 ...

随机推荐

  1. Torch 日志文件的保存 logroll

    Torch 日志文件的保存 logroll 怎样将 Torch 在终端显示的信息,保存到 log 文件中 ?   现在介绍一种方法:利用 logroll 的方式.  参考 https://github ...

  2. WPF 打印

    1. System.Windows.Controls.PrintDialog printDialog = new System.Windows.Controls.PrintDialog(); if ( ...

  3. 路由 - ASP.NET MVC 4 系列

           软件开发人员常常对一些细小的细节问题倍加关注,由其在考虑源代码的质量和结构时更是如此.因此,当遇到大部分使用 ASP.NET 技术构建的站点,使用如下的 URL 地址时,可能会有些奇怪: ...

  4. oracle 用户与表空间关系

    oracle用户与表空间关系用户=商家表=商品表空间=仓库1. 1个商家能有很多商品,1个商品只能属于一个商家2. 1个商品可以放到仓库A,也可以放到仓库B,但不能同时放入A和B3. 仓库不属于任何商 ...

  5. int.Parse()、int.TryParse()和Convert.ToInt32()的区别

    1:int.Parse(一个参数)        此参数必须满足: 1 只能是字符串: 2 只能是 “整型” 字符串,即各种整型ToString()之后的形式,也不能为浮点型. 2:int.TryPa ...

  6. python海龟图制作

    海龟画图很好看,先上图形: 依据代码注释随意打印出来就行: #!/usr/bin/python3.4 # -*- coding: utf-8 -*- import turtle # 拿起一支笔 t = ...

  7. ETL利器Kettle实战应用解析系列二 【应用场景和实战DEMO下载】

    本文主要阅读目录如下: 1.应用场景 2.DEMO实战 3.DEMO下载 1.应用场景 这里简单概括一下几种具体的应用场景,按网络环境划分主要包括: 表视图模式:这种情况我们经常遇到,就是在同一网络环 ...

  8. 解决duplicate symbols for architecture x86_64错误

    duplicate symbols for architecture x86_64 两个不第三方SDK之间的文件里面内容重复了,类似 file.h+file.m 和 CHfile.h+CHfile.m ...

  9. sql server2008 检查是否需要挂起计算机重新启动。挂起重新启动会导致安装程序失败。

    解决方法: 1.如果重新启动后,一样无效, 2.那么就进入注册表编辑器,通过修改注册表的方法来解决.除了删除SQL安装时遗留下的LJ文件,还必须进行以下操作: 在"开始"-&quo ...

  10. oracle 12c 创建PDB用户即Local User (PDB与CDB)

    Oracle 12C用户创建与表空间分配  数据库安装完成后,首先用系统用户链接数据库容器(CDB), 在数据库容器(CDB)中创建表空间‘imei’ SQL>create tablespace ...