寫一個自己的註解@MyConfiguration,讀取配置文件

自己寫的一個的註解,使用方便簡潔^_^

源碼地址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

 

 

覺得有用的老鐵贊一下唄~

發佈了86 篇原創文章 · 獲贊 144 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章