09-10-2013, 09:28 PM
Маленький гайд для начинающих
своих серверов: как же легко все вынести в файлик, не напрягаясь к тому же быстро.
Горождение новых закапсованных переменных и копи-паст тонн getProperty и прочего может вызвать ошибки и еще больше вопросов.
Итак, в яве есть одна старушка, зовут ее Reflection. она умеет работать с классом, изменяя его как ей будет угодно, прямо ведьма. с ее помощью можно сделать множество интересных фич, но в этом случае я ее буду использовать для создания конфиг файлов.
Что нужно для начала?
Первый файл - это файл самого конфига, в моем примере он назван ConfigMain
состоит он из
[src=java]public class ConfigMain {
@Property public static int param1;
@Property(name = "extraConfigParamater") public static String param2;
}[/src]
В нем можно добавлять сколь угодно параметров, используя примитивы в виде полей.
А как же его связать с "конфиг файлом в папочке конфиг"? - просто.
[src=java]public class ConfigLoader {
public static void join(Class<?>... configs) throws Exception {
for (Class<?> c : configs) {
Constructor<?> cnt = c.getConstructor();
cnt.setAccessible(true);
Object b = cnt.newInstance();
initFields(b);
}
}
private static void initFields(Object obj) throws Exception {
Properties prop = new Properties();
try (InputStream is = Files.newInputStream(Paths.get(String.format("./config/%s.properties", obj.getClass().getSimpleName().substring("config".length()).toLowerCase())))) {
prop.load(is);
}
for (Field field : obj.getClass().getDeclaredFields()) {
Property property = field.getAnnotation(Property.class);
if (property != null) {
String keyname = property.name().equals("@") ? field.getName() : property.name();
if (!prop.containsKey(keyname))
throw new RuntimeException(String.format("config class '%s' param '%s' was not defined", obj.getClass().getSimpleName(), keyname));
Object value = field.get(obj);
String classType = field.getType().getSimpleName().toLowerCase(), keyvalue = prop.getProperty(keyname);
switch (classType) {
case "string":
field.set(value, prop.getProperty(keyname));
break;
case "integer":
case "int":
field.set(value, Integer.parseInt(keyvalue));
break;
case "boolean":
field.set(value, Boolean.parseBoolean(keyvalue));
break;
case "byte":
field.set(value, Byte.parseByte(keyvalue));
break;
case "double":
field.set(value, Double.parseDouble(keyvalue));
break;
case "float":
field.set(value, Float.parseFloat(keyvalue));
break;
case "long":
field.set(value, Long.parseLong(keyvalue));
break;
default:
System.err.println(String.format("ConfigLoader.initFields() unk field: '%s', key: '%s'", classType, keyvalue));
break;
}
}
}
}
}[/src]
В первой части метода initFields я связываю название класса с названием файла, к примеру файл ConfigMain.java будет интерпретирован как /config/main.properties
Вкратце наша старушка перечисляет все доступные поля в классе ConfigMain, аннотированные @Property, и проверяет наличие этих параметров в файле, если находит - переносит значение из файла в класс.
А теперь надо бы посмотреть что получилось?
- создаем файл для вашего конфиг класса, вносим в него данные
[src=java] public static void main(String[] args) {
System.out.printf("param1: %d, param2: %s \n", ConfigMain.param1, ConfigMain.param2);
try {
ConfigLoader.join(ConfigMain.class);
} catch (NoSuchFileException nsf) {
System.err.printf("config file '%s' was not found \n", nsf.getFile());
throw new RuntimeException();
} catch (Exception e) {
e.printStackTrace();
}
System.out.printf("param1 %d, param2: %s \n", ConfigMain.param1, ConfigMain.param2);
}[/src]
И в моем случае на выходе теста я вижу
Надо обратить внимание на то, что аннотация @Property берет имя переменной в файле по названию поля, а аннотация @Property(name = "extraConfigParamater") - берет название переменной из параметра аннотации name.
rghost
Горождение новых закапсованных переменных и копи-паст тонн getProperty и прочего может вызвать ошибки и еще больше вопросов.
Итак, в яве есть одна старушка, зовут ее Reflection. она умеет работать с классом, изменяя его как ей будет угодно, прямо ведьма. с ее помощью можно сделать множество интересных фич, но в этом случае я ее буду использовать для создания конфиг файлов.
Что нужно для начала?
Первый файл - это файл самого конфига, в моем примере он назван ConfigMain
состоит он из
[src=java]public class ConfigMain {
@Property public static int param1;
@Property(name = "extraConfigParamater") public static String param2;
}[/src]
В нем можно добавлять сколь угодно параметров, используя примитивы в виде полей.
А как же его связать с "конфиг файлом в папочке конфиг"? - просто.
[src=java]public class ConfigLoader {
public static void join(Class<?>... configs) throws Exception {
for (Class<?> c : configs) {
Constructor<?> cnt = c.getConstructor();
cnt.setAccessible(true);
Object b = cnt.newInstance();
initFields(b);
}
}
private static void initFields(Object obj) throws Exception {
Properties prop = new Properties();
try (InputStream is = Files.newInputStream(Paths.get(String.format("./config/%s.properties", obj.getClass().getSimpleName().substring("config".length()).toLowerCase())))) {
prop.load(is);
}
for (Field field : obj.getClass().getDeclaredFields()) {
Property property = field.getAnnotation(Property.class);
if (property != null) {
String keyname = property.name().equals("@") ? field.getName() : property.name();
if (!prop.containsKey(keyname))
throw new RuntimeException(String.format("config class '%s' param '%s' was not defined", obj.getClass().getSimpleName(), keyname));
Object value = field.get(obj);
String classType = field.getType().getSimpleName().toLowerCase(), keyvalue = prop.getProperty(keyname);
switch (classType) {
case "string":
field.set(value, prop.getProperty(keyname));
break;
case "integer":
case "int":
field.set(value, Integer.parseInt(keyvalue));
break;
case "boolean":
field.set(value, Boolean.parseBoolean(keyvalue));
break;
case "byte":
field.set(value, Byte.parseByte(keyvalue));
break;
case "double":
field.set(value, Double.parseDouble(keyvalue));
break;
case "float":
field.set(value, Float.parseFloat(keyvalue));
break;
case "long":
field.set(value, Long.parseLong(keyvalue));
break;
default:
System.err.println(String.format("ConfigLoader.initFields() unk field: '%s', key: '%s'", classType, keyvalue));
break;
}
}
}
}
}[/src]
В первой части метода initFields я связываю название класса с названием файла, к примеру файл ConfigMain.java будет интерпретирован как /config/main.properties
Вкратце наша старушка перечисляет все доступные поля в классе ConfigMain, аннотированные @Property, и проверяет наличие этих параметров в файле, если находит - переносит значение из файла в класс.
А теперь надо бы посмотреть что получилось?
- создаем файл для вашего конфиг класса, вносим в него данные
Цитата:param1 = 23- создаем тест
extraConfigParamater = hello reflection
[src=java] public static void main(String[] args) {
System.out.printf("param1: %d, param2: %s \n", ConfigMain.param1, ConfigMain.param2);
try {
ConfigLoader.join(ConfigMain.class);
} catch (NoSuchFileException nsf) {
System.err.printf("config file '%s' was not found \n", nsf.getFile());
throw new RuntimeException();
} catch (Exception e) {
e.printStackTrace();
}
System.out.printf("param1 %d, param2: %s \n", ConfigMain.param1, ConfigMain.param2);
}[/src]
И в моем случае на выходе теста я вижу
Цитата:param1: 0, param2: null
param1 23, param2: hello reflection
Надо обратить внимание на то, что аннотация @Property берет имя переменной в файле по названию поля, а аннотация @Property(name = "extraConfigParamater") - берет название переменной из параметра аннотации name.
rghost