В прошлых статьях я уже рассказал о том, что такое MBean, и что такое MXBean. Настало время применить полученные знания с современными технологиями.
Для начала создадим новый проект Spring Boot с помощью Spring Initializr. В поле “Group” введём ru.urvanov.javaexamples. В поле “Artifact” введём springbootjmx. Версию Spring Boot выбираем 2.0.1. Кликаем по кнопке “Generate Project”. Сохраняем его куда-нибудь.
Откроем скачанную заготовку в IDE (Eclipse, IDEA или NetBeans). Первым делом открываем “SpringbootjmxApplication.java” и прописываем Thread.sleep, чтобы приложение НЕ завершалось сразу после старта:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
package ru.urvanov.javaexamples.springbootjmx; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringbootjmxApplication { public static void main(String[] args) throws InterruptedException { SpringApplication.run(SpringbootjmxApplication.class, args); // Run forever. Thread.sleep(Long.MAX_VALUE); } } |
Создадим пакет ru.urvanov.javaexamples.springbootjmx.mbeans и в нём создадим “SimpleMBean.java”:
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 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
package ru.urvanov.javaexamples.springbootjmx.mbeans; import org.springframework.jmx.export.annotation.ManagedAttribute; import org.springframework.jmx.export.annotation.ManagedOperation; import org.springframework.jmx.export.annotation.ManagedResource; import org.springframework.stereotype.Component; @ManagedResource @Component public class SimpleMBean { private int intValue; private double doubleValue; private String stringValue; private Monster monster = new Monster(); { monster.setLevel(10); monster.setHealth(100); } @ManagedAttribute public int getIntValue() { return intValue; } @ManagedAttribute public void setIntValue(int intValue) { this.intValue = intValue; } @ManagedAttribute public double getDoubleValue() { return doubleValue; } @ManagedAttribute public void setDoubleValue(double doubleValue) { this.doubleValue = doubleValue; } @ManagedAttribute public String getStringValue() { return stringValue; } @ManagedAttribute public void setStringValue(String stringValue) { this.stringValue = stringValue; } @ManagedAttribute public Monster getMonster() { return monster; } @ManagedAttribute public void setMonster(Monster monster) { this.monster = monster; } @ManagedOperation public String writeToConsole(String str1, String str2) { String str3 = str1 + str2; System.out.println(str3); return str3; } @Override public int hashCode() { final int prime = 31; int result = 1; long temp; temp = Double.doubleToLongBits(doubleValue); result = prime * result + (int) (temp ^ (temp >>> 32)); result = prime * result + intValue; result = prime * result + ((monster == null) ? 0 : monster.hashCode()); result = prime * result + ((stringValue == null) ? 0 : stringValue.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; SimpleMBean other = (SimpleMBean) obj; if (Double.doubleToLongBits(doubleValue) != Double .doubleToLongBits(other.doubleValue)) return false; if (intValue != other.intValue) return false; if (monster == null) { if (other.monster != null) return false; } else if (!monster.equals(other.monster)) return false; if (stringValue == null) { if (other.stringValue != null) return false; } else if (!stringValue.equals(other.stringValue)) return false; return true; } @Override public String toString() { return "SimpleMBean [intValue=" + intValue + ", doubleValue=" + doubleValue + ", stringValue=" + stringValue + ", monster=" + monster + "]"; } } |
Класс Monster:
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 |
package ru.urvanov.javaexamples.springbootjmx.mbeans; public class Monster { private int level; private int health; public int getLevel() { return level; } public void setLevel(int level) { this.level = level; } public int getHealth() { return health; } public void setHealth(int health) { this.health = health; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + health; result = prime * result + level; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Monster other = (Monster) obj; if (health != other.health) return false; if (level != other.level) return false; return true; } @Override public String toString() { return "Monster [level=" + level + ", health=" + health + "]"; } } |
Обратите внимание, что мы создали самый обычный бин Spring Framework-а и аннотировали его дополнительно аннотацией @ManagedResource. Аннотация @ManagedResource позволяет регистрировать любые бины Spring Framework в качестве MBean-ов. С помощью аннотаций @ManagedAttribute, @ManagedOperation мы помечаем экспортируемые MBean-ом атрибуты и операции соответственно.
Запустим наше приложение и подключимся к нему через JConsole. Как подключаться к приложению с помощью JConsole я уже описывал в статье «Введение в JMX и MBeans в Java».
Обратите внимание, что мы не можем посмотреть значение атрибута monster, так как он имеет тип Monster, который не доступен из classpath самого JConsole.
Теперь создадим MXBean. Для этого создадим интерфейс MyMXBean:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package ru.urvanov.javaexamples.springbootjmx.mbeans; public interface MyMXBean { int getIntValue(); void setIntValue(int intValue); double getDoubleValue(); void setDoubleValue(double doubleValue); String getStringValue(); void setStringValue(String stringValue); Monster getMonster(); void setMonster(Monster monster); String writeToConsole(String str1, String str2); } |
Теперь создадим класс My, реализующий интерфейс MyMXBean:
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 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
package ru.urvanov.javaexamples.springbootjmx.mbeans; import org.springframework.jmx.export.annotation.ManagedAttribute; import org.springframework.jmx.export.annotation.ManagedOperation; import org.springframework.jmx.export.annotation.ManagedResource; import org.springframework.stereotype.Component; @ManagedResource @Component public class My implements MyMXBean { private int intValue; private double doubleValue; private String stringValue; private Monster monster = new Monster(); { this.monster.setLevel(3); this.monster.setHealth(100); } @ManagedAttribute @Override public int getIntValue() { return this.intValue; } @ManagedAttribute @Override public void setIntValue(int intValue) { this.intValue = intValue; } @ManagedAttribute @Override public double getDoubleValue() { return this.doubleValue; } @ManagedAttribute @Override public void setDoubleValue(double doubleValue) { this.doubleValue = doubleValue; } @ManagedAttribute @Override public String getStringValue() { return this.stringValue; } @ManagedAttribute @Override public void setStringValue(String stringValue) { this.stringValue = stringValue; } @ManagedAttribute @Override public Monster getMonster() { return this.monster; } @ManagedAttribute @Override public void setMonster(Monster monster) { this.monster = monster; } @ManagedOperation @Override public String writeToConsole(String str1, String str2) { String str3 = str1 + str2; System.out.println(str3); return str3; } @Override public int hashCode() { final int prime = 31; int result = 1; long temp; temp = Double.doubleToLongBits(doubleValue); result = prime * result + (int) (temp ^ (temp >>> 32)); result = prime * result + intValue; result = prime * result + ((monster == null) ? 0 : monster.hashCode()); result = prime * result + ((stringValue == null) ? 0 : stringValue.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; My other = (My) obj; if (Double.doubleToLongBits(doubleValue) != Double .doubleToLongBits(other.doubleValue)) return false; if (intValue != other.intValue) return false; if (monster == null) { if (other.monster != null) return false; } else if (!monster.equals(other.monster)) return false; if (stringValue == null) { if (other.stringValue != null) return false; } else if (!stringValue.equals(other.stringValue)) return false; return true; } @Override public String toString() { return "My [intValue=" + intValue + ", doubleValue=" + doubleValue + ", stringValue=" + stringValue + ", monster=" + monster + "]"; } } |
Теперь запустим приложение SpringbootjmxApplication и подключимся к нему через JConsole. Обратите внимание, что мы можем смотреть содержимое My.Monster, так как оно теперь автоматически конвертируется в так называемые Open Types.
Вот и всё на сегодня. May the 4th be with you:). Рекомендую изредка почитывать учебник и нововведения.
На последок осталось создать только файл “README.md” с корректным синтаксисом Markdown:
1 2 3 |
Spring Boot JMX example ======================= См. [статью про создание проекта](https://urvanov.ru/2018/05/09/spring-boot-%d0%b8-jmx/). |
Теперь точно всё. Ссылки на исходные коды:
Скачать исходники ZIP-архив 54 Кбайт