java配置方式

简介

之前使用JMX管理也没有太在意,最近重新看到JMX配置,发现这也是一种比较灵活的配置管理方式。主要是有很多现成的工具可以用。

在看JMX之前我们先回顾一下之前的配置方式。直接写死在程序里就不说了,这个相当于没有配置,可能只是让配置集中一点方便管理而已。每次修改之后都得重新打包,直接可以放弃。

下面我们直接看使用配置文件的方式。

配置文件配置

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

public class ConfigUtil {

    private static Properties config;

    static {
        config = new Properties();
        InputStream is = ConfigUtil.class.getResourceAsStream("config.properties");
        try {
            config.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static String getConfig(String key){
        return config.getProperty(key);
    }

    public static void main(String[] args) {
        for(int i = 0;i<1000;i++) {
            System.out.println(ConfigUtil.getConfig("port"));
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

config.properties直接放在类所在包下面就可以了。这个版本的配置有一个问题就是,修改了配置文件必须重启应用。重启应用也是个比较麻烦的事情。

所有我们修改一下来个动态配置版的,可以使用apach的common.io中的文件监控。不过我觉得没有必要,直接简单判断一下最后修改时间就可以了。直接上代码。

动态配置文件

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

public class ConfigPromoteUtil {

    public static final String FILE_NAME = "config.properties";

    private static Properties config = new Properties();

    private static long lastModify;

    private static File file;

    static {
        URL url = ConfigPromoteUtil.class.getResource(FILE_NAME);
        String path = url.getFile();
        file = new File(path);
        loadConfig();
    }

    private static void loadConfig(){
        lastModify = file.lastModified();
        try {
            FileInputStream fis = new FileInputStream(file);
            config.load(fis);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 配置文件是否被修改
     * @return true 被修改 否则 false
     */
    private static boolean isModify(){
        long lastModified = file.lastModified();
        return lastModify != lastModified;
    }

//    private static boolean isModify() throws IOException {
//        URL url = ConfigPromoteUtil.class.getResource(FILE_NAME);
//        String path = url.getFile();
//        System.out.println(path);
//        Path p = Paths.get(path.substring(1));
//        FileTime ft = Files.getLastModifiedTime(p);
//        long lastModified = ft.toMillis();
//        return lastModify != lastModified;
//    }

    public static String getConfig(String key) throws IOException {
        if(isModify())
            loadConfig();
        return config.getProperty(key);
    }

    public static void main(String[] args) throws IOException {
        for(int i = 0;i<1000;i++) {
            System.out.println(ConfigPromoteUtil.getConfig("port"));
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

逻辑还是蛮简单的,初始化的时候就先加载配置文件,记录最后修改时间,然后每一次获取配置的时候,都去检查文件的最后修改时间有没有变化,如果变了就重新加载配置文件,并修改最后修改时间。

刚刚开始的时候再idea中发现获取的配置文件最后修改时间怎么都不对,尝试了很多方法都没有效果,后来直接用命令行运行了一下,既然好了,然后在idea中也好了,真是奇怪。

不过解决这个问题的过程中也发现一些有趣的事情,比如说Paths,Path,Files,FileTime等类。之前处理文件一般都是使用apach的common.io,今天发现jdk1.7开始的Paths,Path,Files,FileTime这些类也可以很方便的处理遍历,复制等操作了。有兴趣可以尝试一下。

JMX配置

这里我们使用standard MBean,standard MBean它能管理的资源(包括属性,方法,时间)必须定义在接口中,然后MBean必须实现这个接口。它的命名也必须遵循一定的规范,例如我们的MBean为Congfig,则接口必须为CongfigMBean。

先上代码:

standard MBean接口

public interface ConfigMBean {

    String getName();

    void setName(String name);

    int getPort();

    void setPort(int port);

    String printName();

    int printPort();

}

standard MBean

public class Config implements ConfigMBean {

    private String name;

    private int port;

    public String printName(){
        System.out.println(name);
        return name;
    }

    public int printPort(){
        System.out.println(port);
        return port;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public int getPort() {
        return port;
    }

    @Override
    public void setPort(int port) {
        this.port = port;
    }
}

测试代理类

import javax.management.*;
import javax.management.remote.*;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;

public class ConfigAgent {

    public static void main(String[] args) throws Exception {
        MBeanServer server = ManagementFactory.getPlatformMBeanServer();
        ObjectName on = null;
        try {
            on = new ObjectName("jmxBean:name=config");
        } catch (MalformedObjectNameException e) {
            e.printStackTrace();
        }
        try {
            server.registerMBean(new Config(), on);
        } catch (InstanceAlreadyExistsException e) {
            e.printStackTrace();
        } catch (MBeanRegistrationException e) {
            e.printStackTrace();
        } catch (NotCompliantMBeanException e) {
            e.printStackTrace();
        }
        try
        {
            LocateRegistry.createRegistry(9999);
            JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi");
            JMXConnectorServer jcs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, server);
            jcs.start();
        } catch (RemoteException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        final Config config = new Config();
        for(int i=0;i<1000;i++){
            System.out.println(config.getName());
            System.out.println(config.getPort());
            config.printName();
            config.printPort();
            print();
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        final Scanner scanner = new Scanner(System.in);
        scanner.nextLine();
    }

    public static void print() throws Exception {
        JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi");
        JMXConnector jmxc = JMXConnectorFactory.connect(url,null);
        MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
        ObjectName mbeanName = new ObjectName("jmxBean:name=config");
//        String[] domains = mbsc.getDomains();
//        mbsc.getMBeanCount()
//        mbsc.setAttribute(mbeanName, new Attribute("Name","tim"));
//        mbsc.setAttribute(mbeanName, new Attribute("Port","3306"));
//        int port = (Integer) mbsc.getAttribute(mbeanName, "Port");
//        String name = (String)mbsc.getAttribute(mbeanName, "Name");
//        System.out.println("port=" + port + ";name=" + name);
        ConfigMBean proxy = MBeanServerInvocationHandler.newProxyInstance(mbsc, mbeanName, ConfigMBean.class, false);
        proxy.printPort();
        proxy.printName();
        System.out.println(proxy.getName());
        System.out.println(proxy.getPort());

    }
}
MBeanServer server = ManagementFactory.getPlatformMBeanServer();

获取一个MBean管理服务器的实例。

扫描二维码关注公众号,回复: 128923 查看本文章
ObjectName on = new ObjectName("jmxBean:name=config");

给MBean一个标识,方便外部识别和访问。

server.registerMBean(new Config(), on);

在MBean服务器上注册一个Config MBean。

到这里就算是注册MBean完了,并且可以使用JConsole这样的工具连接查看修改了。但是如果要提供一些外部的Adapter和远程的RMI调用就还需要一些工作。

LocateRegistry.createRegistry(9999);
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi");
JMXConnectorServer jcs = JMXConnectorServerFactory.newJMXConnectorServer(url,null, server);
jcs.start();

注册一个JMX服务,并且使用的是rmi协议。

从上面的测试中可以看到直接在new一个Config是不行的,如果要统一配置,获取外部可以修改的Config还是得JMXServiceURL来访问。

public static void print() throws Exception {
        JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi");
        JMXConnector jmxc = JMXConnectorFactory.connect(url,null);
        MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
        ObjectName mbeanName = new ObjectName("jmxBean:name=config");
//        String[] domains = mbsc.getDomains();
//        mbsc.getMBeanCount()
//        mbsc.setAttribute(mbeanName, new Attribute("Name","tim"));
//        mbsc.setAttribute(mbeanName, new Attribute("Port","3306"));
//        int port = (Integer) mbsc.getAttribute(mbeanName, "Port");
//        String name = (String)mbsc.getAttribute(mbeanName, "Name");
//        System.out.println("port=" + port + ";name=" + name);
        ConfigMBean proxy = MBeanServerInvocationHandler.newProxyInstance(mbsc, mbeanName, ConfigMBean.class, false);
        proxy.printPort();
        proxy.printName();
        System.out.println(proxy.getName());
        System.out.println(proxy.getPort());
    }

我们看print方法,还是得通过JMXServiceURL,连接到MBean服务暴露的接口,通过MBeanServerInvocationHandler获取到代理的Config MBean。

这样我们可以通过JConsole等工具连接上MBean服务器,修改了配置,在程序中也能获取到。如下图:

JConsole

上图是在JConsole中修改Config配置,注意属性是Config中的属性,操作是定义在ConfigMBean中的非getter,setter方法。

总结

对于简单的配置直接使用动态配置文件就可以了,如果配置管理比较多久可以考虑JMX的方式。JMX方式的限制也比较多,开发的成本也会大一些。

猜你喜欢

转载自my.oschina.net/u/2474629/blog/1797598