如何使用Spring Boot在H2控制台中设置默认的JDBC URL值

时间:2019-05-21 20:25:31

标签: spring spring-boot h2

我已在春季启动中启用了H2控制台。但是,当我打开控制台连接页面时,默认URL是保存在H2控制台历史记录中的URL。在项目开始时,如何配置项目以填充与 spring.datasource.url 相同的URL?目前,我在控制台中手动设置了url,但我想由项目本身自动设置它。

yaml:

spring:
  h2:
    console:
      enabled: true
      path: /admin/h2


  datasource:
    url: jdbc:h2:mem:foobar

更新: 我知道最后的连接设置已保存到〜/ .h2.server.properties,但我需要从启动应用程序中设置属性,可能会在其中几个之间切换

2 个答案:

答案 0 :(得分:3)

没有提供挂钩来填写设置。

好消息是我们可以用一些代码来更改它。

当前状态

登录屏幕是在WebApp.index()

中创建的
String[] settingNames = server.getSettingNames();
String setting = attributes.getProperty("setting");
if (setting == null && settingNames.length > 0) {
    setting = settingNames[0];
}
String combobox = getComboBox(settingNames, setting);
session.put("settingsList", combobox);
ConnectionInfo info = server.getSetting(setting);
if (info == null) {
    info = new ConnectionInfo();
}
session.put("setting", PageParser.escapeHtmlData(setting));
session.put("name", PageParser.escapeHtmlData(setting));
session.put("driver", PageParser.escapeHtmlData(info.driver));
session.put("url", PageParser.escapeHtmlData(info.url));
session.put("user", PageParser.escapeHtmlData(info.user));
return "index.jsp";

我们想利用server.getSettingNames(),并精确利用下面使用的server.getSettings()

synchronized ArrayList<ConnectionInfo> getSettings() {
    ArrayList<ConnectionInfo> settings = new ArrayList<>();
    if (connInfoMap.size() == 0) {
        Properties prop = loadProperties();
        if (prop.size() == 0) {
            for (String gen : GENERIC) {
                ConnectionInfo info = new ConnectionInfo(gen);
                settings.add(info);
                updateSetting(info);
            }
        } else {
            for (int i = 0;; i++) {
                String data = prop.getProperty(Integer.toString(i));
                if (data == null) {
                    break;
                }
                ConnectionInfo info = new ConnectionInfo(data);
                settings.add(info);
                updateSetting(info);
            }
        }
    } else {
        settings.addAll(connInfoMap.values());
    }
    Collections.sort(settings);
    return settings;
}

计划

  • 禁用ServletRegistrationBean<WebServlet>创建的H2ConsoleAutoConfiguration
  • 将其替换为带有WebServlet子类的config类
  • 我们的CustomH2WebServlet将覆盖init并注册CustomH2WebServerWebServer的子类)
  • CustomH2WebServer中,我们覆盖getSettings(),我们已经完成了

代码

@EnableConfigurationProperties({H2ConsoleProperties.class, DataSourceProperties.class})
@Configuration
public class H2Config {

    private final H2ConsoleProperties h2ConsoleProperties;

    private final DataSourceProperties dataSourceProperties;

    public H2Config(H2ConsoleProperties h2ConsoleProperties, DataSourceProperties dataSourceProperties) {
        this.h2ConsoleProperties = h2ConsoleProperties;
        this.dataSourceProperties = dataSourceProperties;
    }

    @Bean
    public ServletRegistrationBean<WebServlet> h2Console() {
        String path = this.h2ConsoleProperties.getPath();
        String urlMapping = path + (path.endsWith("/") ? "*" : "/*");
        ServletRegistrationBean<WebServlet> registration = new ServletRegistrationBean<>(
                new CustomH2WebServlet(this.dataSourceProperties.getUrl()), urlMapping);
        H2ConsoleProperties.Settings settings = this.h2ConsoleProperties.getSettings();
        if (settings.isTrace()) {
            registration.addInitParameter("trace", "");
        }
        if (settings.isWebAllowOthers()) {
            registration.addInitParameter("webAllowOthers", "");
        }
        return registration;
    }
}

package org.h2.server.web;

import javax.servlet.ServletConfig;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Enumeration;

public class CustomH2WebServlet extends WebServlet {

    private final String dbUrl;

    public CustomH2WebServlet(String dbUrl) {
        this.dbUrl = dbUrl;
    }

    @Override
    public void init() {
        ServletConfig config = getServletConfig();
        Enumeration<?> en = config.getInitParameterNames();
        ArrayList<String> list = new ArrayList<>();
        while (en.hasMoreElements()) {
            String name = en.nextElement().toString();
            String value = config.getInitParameter(name);
            if (!name.startsWith("-")) {
                name = "-" + name;
            }
            list.add(name);
            if (value.length() > 0) {
                list.add(value);
            }
        }
        String[] args = list.toArray(new String[0]);
        WebServer server = new CustomH2WebServer(dbUrl);
        server.setAllowChunked(false);
        server.init(args);
        setServerWithReflection(this, server);
    }

    private static void setServerWithReflection(final WebServlet classInstance, final WebServer newValue) {
        try {
            final Field field = WebServlet.class.getDeclaredField("server");
            field.setAccessible(true);
            field.set(classInstance, newValue);
        }
        catch (SecurityException|NoSuchFieldException|IllegalArgumentException|IllegalAccessException ex) {
            throw new RuntimeException(ex);
        }
    }
}
package org.h2.server.web;

import java.util.ArrayList;
import java.util.Collections;

class CustomH2WebServer extends WebServer {

    private final String connectionInfo;

    CustomH2WebServer(String dbUrl) {

        this.connectionInfo = "Test H2 (Embedded)|org.h2.Driver|" +dbUrl+"|sa";
    }

    synchronized ArrayList<ConnectionInfo> getSettings() {
        ArrayList<ConnectionInfo> settings = new ArrayList<>();
        ConnectionInfo info = new ConnectionInfo(connectionInfo);
        settings.add(info);
        updateSetting(info);
        Collections.sort(settings);
        return settings;
    }
}
spring.h2.console.enabled=false
spring.datasource.url=jdbc:h2:mem:foobar

除了需要通过反射设置的一个私有字段之外,所有内容都变得模糊不清。

提供的代码适用于H2 1.4.199

答案 1 :(得分:1)

使用简单易用的配置类来启发@Lesiak的答案

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import lombok.extern.apachecommons.CommonsLog;
import org.h2.server.web.ConnectionInfo;
import org.h2.server.web.WebServer;

import org.h2.server.web.WebServlet;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;

import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@CommonsLog
@Configuration
@ConditionalOnProperty(prefix = "spring.h2.console", name = "enabled", havingValue = "true", matchIfMissing = false)
public class H2ConsoleConfiguration {

    @Bean
    ServletRegistrationBean<WebServlet> h2ConsoleRegistrationBean(final ServletRegistrationBean<WebServlet> h2Console, final DataSourceProperties dataSourceProperties) {
        h2Console.setServlet(new WebServlet() {
            @Override
            public void init() {
                super.init();
                updateWebServlet(this, dataSourceProperties);
            }
        });
        return h2Console;
    }

    public static void updateWebServlet(final WebServlet webServlet, DataSourceProperties dataSourceProperties) {
        try {
            updateWebServer(getWebServer(webServlet), dataSourceProperties);
        } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException | NoSuchMethodException | InvocationTargetException | NullPointerException ex) {
            log.error("Unable to set a custom ConnectionInfo for H2 console", ex);
        }
    }

    public static WebServer getWebServer(final WebServlet webServlet) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
        final Field field = WebServlet.class.getDeclaredField("server");
        field.setAccessible(true);
        return (WebServer) field.get(webServlet);
    }

    public static void updateWebServer(final WebServer webServer, final DataSourceProperties dataSourceProperties) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        final ConnectionInfo connectionInfo = new ConnectionInfo(String.format("Generic Spring Datasource|%s|%s|%s", dataSourceProperties.determineDriverClassName(), dataSourceProperties.determineUrl(), dataSourceProperties.determineUsername()));
        final Method method = WebServer.class.getDeclaredMethod("updateSetting", ConnectionInfo.class);
        method.setAccessible(true);
        method.invoke(webServer, connectionInfo);
    }

}