自己寫的一個的註解,使用方便簡潔^_^
源碼地址:https://github.com/bigBigRiver/MyConfiguration.git
config.properties文件
userName=engineerdong
password=123456
配置文件對應的實體類
import com.river.boot.annotation.MyConfiguration;
import lombok.Data;
import org.springframework.stereotype.Component;
@Data
@Component
@MyConfiguration(value = "/config.properties")
public class MyConfig {
private String userName;
private String password;
}
測試類:
@Slf4j
@SpringBootTest
class BootApplicationTests {
@Autowired
MyConfig myConfig;
@Test
void logSomething() {
log.info(myConfig.getUserName() + ":" + myConfig.getPassword());
}
}
打印:
com.river.boot.BootApplicationTests 2020年01月10日 22:56:58 -- engineerdong:123456
註解類:
/**
* @author river
* 2020/1/10
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyConfiguration {
String value() default "application.properties";
}
註解類處理類(springboot):
import com.river.boot.annotation.MyConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.Properties;
/**
* 在springboot初始化的時候,通過java反射機制,爲配置有@MyConfiguration註解的類的字段賦值
*
* @author river
* 2020/1/10
*/
@Slf4j
@Component
public class HandleMyConfigurationRunner implements CommandLineRunner, ApplicationContextAware {
private ApplicationContext applicationContext;
private Properties configProperties = new Properties();
@Override
public void run(String... args) throws Exception {
/*在容器中搜索被@MyConfiguration修飾的所有類名*/
String[] configNames = applicationContext.getBeanNamesForAnnotation(MyConfiguration.class);
if (StringUtils.isEmpty(configNames)) {
return;
}
Object[] configObjects = new Object[configNames.length];
/*在spring容器中獲取類名對應的對象*/
for (int i = 0; i < configNames.length; i++) {
configObjects[i] = applicationContext.getBean(configNames[i]);
}
for (Object object : configObjects) {
Class<?> clazz = object.getClass();
MyConfiguration myConfiguration = clazz.getAnnotation(MyConfiguration.class);
/*獲取註解的value值並獲取輸入流*/
String path = "/" + myConfiguration.value();
InputStream inputStream = clazz.getResourceAsStream(path.replaceAll("/+", "/"));
if (null == inputStream) {
log.info("未找到資源:" + path);
return;
}
configProperties.load(inputStream);
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);//訪問private字段
field.set(object, configProperties.getProperty(field.getName()));//獲取字段名並查詢字段名在配置文件中對應的value,再設置到字段中去
}
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
這樣新增配置文件的時候,再新建一個實體類與配置文件對應即可以使用,挺方便的!
說明:
1、如果不是springboot項目,可以使用ApplicationListener來實現註解類處理類,如下:
import com.good.frame.annotation.MyConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.Properties;
@Component
public class ConfigListener implements ApplicationListener<ContextRefreshedEvent> {
private ApplicationContext applicationContext;
private Properties configProperties = new Properties();
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
applicationContext = contextRefreshedEvent.getApplicationContext();
try {
handleMyConfiguration();
} catch (Exception e) {
e.printStackTrace();
}
}
private void handleMyConfiguration() throws Exception {
/*在容器中搜索被@MyConfiguration修飾的所有類名*/
String[] configNames = applicationContext.getBeanNamesForAnnotation(MyConfiguration.class);
if (StringUtils.isEmpty(configNames)) {
return;
}
Object[] configObjects = new Object[configNames.length];
/*在spring容器中獲取類名對應的對象*/
for (int i = 0; i < configNames.length; i++) {
configObjects[i] = applicationContext.getBean(configNames[i]);
}
for (Object object : configObjects) {
Class<?> clazz = object.getClass();
MyConfiguration myConfiguration = clazz.getAnnotation(MyConfiguration.class);
/*獲取註解的value值並獲取輸入流*/
String path = "/" + myConfiguration.value();
InputStream inputStream = clazz.getResourceAsStream(path.replaceAll("/+", "/"));
if (null == inputStream) {
System.out.println("未找到資源:" + path);
return;
}
configProperties.load(inputStream);
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);//訪問private字段
field.set(object, configProperties.getProperty(field.getName()));//獲取字段名並查詢字段名在配置文件中對應的value,再設置到字段中去
}
}
}
}
2、可以使用spring切面編程來完成。
/**
* 通過切面,處理配置有@MyConfiguration註解的類的get方法
*
* @author river
* 2020/2/27
*/
@Aspect
@Component
@Slf4j
public class MyConfigurationAspect {
@Autowired
ApplicationContext applicationContext;
private Properties configProperties = new Properties();
/**
* 切點設置爲:註解類標註類的所有方法
*/
@Pointcut("@within(com.river.boot.annotation.MyConfiguration)")
private void myConfiguration() {
}
/**
* 設置get方法的返回值
*/
@Around("myConfiguration()")
public Object beforeHandlerMyConfiguration(ProceedingJoinPoint joinPoint) throws Throwable {
Signature signature = joinPoint.getSignature();
String methodName = signature.getName();
if (methodName.contains("get")) {
Object result = joinPoint.proceed();
if (result == null) {
Object proxyObject = joinPoint.getThis();
Class proxyClass = proxyObject.getClass();//由CGLib動態代理生成的代理對象(目標對象的子類)
Class<?> configClass = proxyClass.getSuperclass();//目標類對象
MyConfiguration myConfiguration = configClass.getAnnotation(MyConfiguration.class);
/*獲取註解的value值並獲取輸入流*/
InputStream inputStream = configClass.getResourceAsStream(myConfiguration.value());
if (null == inputStream) {
log.info("未找到資源:" + myConfiguration.value());
return null;
}
configProperties.load(inputStream);
String keyName = methodName.substring(3);
String value = configProperties.getProperty(changeFirstLetterToLowercase(keyName));
invokeSetMethod(proxyObject, proxyClass, keyName, value);//調用set方法,再次調用get方法不需要訪問資源文件
return value;
} else {
return result;
}
} else {
Object[] args = joinPoint.getArgs();
return joinPoint.proceed(args);
}
}
/**
* 調用Set方法
*
* @param object
* @param clazz
* @param keyName set方法對應的字段
* @param value 值
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
private void invokeSetMethod(Object object, Class clazz, String keyName, String value) throws InvocationTargetException, IllegalAccessException {
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
String methodName = method.getName();
if (methodName.equals("set" + keyName)) {
method.invoke(object, value);
}
}
}
/**
* 將字符串第一個字母變成小寫
*
* @param str
* @return
*/
private String changeFirstLetterToLowercase(String str) {
return str.substring(0, 1).toLowerCase().concat(str.substring(1));
}
}
關於@Data的使用,可以參考:https://blog.csdn.net/river66/article/details/103727367
關於@Slf4j的使用,可以參考:https://blog.csdn.net/river66/article/details/103713397
覺得有用的老鐵贊一下唄~