利用Java 动态代理,自定义注解 读取配置文件中的属性值

Java动态代理在一些中间件中经常用到,或者一些大型项目中都会用到。
这里顺带使用一下自定义注解方式,基于java 反射机制读取.properties格式文件。

demo的大致内容包含以下:
在这里插入图片描述

1.配置文件:config.properties

url=http://www.hrsstd.com
password= root
username= zhanghuilong
port = 8080
isOpen = true

2.自定义注解类

注解中的原生标签具体含义可以自行了解

/**
 * @author zhanghuilong
 * @desc 自定义注解,读取配置文件内容
 * @since 2019/01/02
 */
@Target({ ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ReadConf {
    /**
     * read config.properties context eg: key = value
     * @return
     */
    String value();

}

3.demo接口定义

/**
 * @author zhanghuilong
 * @desc  配置中心
 * @since 2019/01/02
 */
public interface HrsConfigService {

    @ReadConf(value = "url")
    String  getUrl();

    @ReadConf("password")
    String getPwd();

    @ReadConf("username")
    String getUserName();

    @ReadConf("port")
    Integer getPort();

    @ReadConf("isOpen")
    Boolean getOff();
}

4.动态代理核心实现
invocationHandler的实现


/**
 * @author zhanghuilong
 * @desc 动态代理具体实现方法
 * @since 2019/01/02
 */
public class PropertyInvocationHandler implements InvocationHandler {

    private Properties properties;

    public PropertyInvocationHandler(Properties properties) {
        this.properties = properties;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println(" 调用 method = [" + method.getName() +"]");
        ReadConf readConf = method.getAnnotation(ReadConf.class);
        if (readConf == null){
            return null;
        }
        String value = readConf.value();
        String property = properties.getProperty(value);
        if (StringUtils.isEmpty(property)){
            return null;
        }

        Class<?> returnClass = method.getReturnType();
        // 基本原始类型,这里只写了部分类型,满足当前demo接口返回值类型,如遇项目有多重类型,可以添加补全所以类型
        if (returnClass.isPrimitive()){
            if (returnClass.equals(int.class)){
                return Integer.valueOf(property);
            }
            else if (returnClass.equals(long.class)){ return (Long.valueOf(property));}
            else if (returnClass.equals(double.class)) {return (Double.valueOf(property));}
            else if (returnClass.equals(float.class)) { return (Float.valueOf(property)); }
            else if (returnClass.equals(boolean.class)) { return (Boolean.valueOf(property));}
        }else {
            if (returnClass.equals(Integer.class)){
                return Integer.valueOf(property);
            }else if (returnClass.equals(String.class)){
                return String.valueOf(property);
            }else if (returnClass.equals(Boolean.class)){
                return Boolean.valueOf(property);
            }
        }

        return property;
    }
}

5.读取配置的通用工厂方法

/**
 * @author zhanghuilong
 * @desc 读取配置工厂方法
 * @since 2019/01/02
 */
public class HrsConfigFactory {

    public HrsConfigFactory() {
    }

    /**
     * 读取方法
     * @param inputStream
     * @return
     */
    public static HrsConfigService readProperties(final InputStream inputStream){

        final Properties properties = new Properties();
        try {
            properties.load(inputStream);
        } catch (IOException e) {
            System.out.println("load inputStream error ");
            return null;
        }
        
        // java 代理机制
        return  (HrsConfigService)Proxy
            .newProxyInstance(HrsConfigService.class.getClassLoader(), new Class[] { HrsConfigService.class },
                new PropertyInvocationHandler(properties));
    }
}

6.demo 的测试执行类main方法

/**
 * @author zhanghuilong
 * @desc 动态代理+自定义注解
 * @since 2019/01/02
 */
public class DemoTest {

    public static void main(String[] args) {

    try {
        // 文件地址可以直接copypath
        InputStream fileInputStream = new FileInputStream("/Users/zhanghuilong/demo/config.properties");
        HrsConfigService configService = HrsConfigFactory.readProperties(fileInputStream);
        if (configService == null){
            return;
        }
        Integer port = configService.getPort();
        String url = configService.getUrl();
        String userName = configService.getUserName();
        String pwd = configService.getPwd();
        Boolean off = configService.getOff();
        String format = String.format("读取配置信息,url: %s, username: %s, password: %s, port :%s, 开关:%s",
            url, userName, pwd, port, off);

        System.out.println( format );
    } catch (FileNotFoundException e) {
        System.out.println("文件不存在");
    }

    }
}

输出:

 调用 method = [getPort]
 调用 method = [getUrl]
 调用 method = [getUserName]
 调用 method = [getPwd]
 调用 method = [getOff]
读取配置信息,url: http://www.hrsstd.com, username: zhanghuilong, password: root, port :8080, 开关:true

本文主要想简单说明下 java 动态代理在实际工作中的应用 和实践。

### 使用自定义注解读取配置文件属性 为了实现使用自定义注解读取配置文件中的属性,在 Spring Boot 中可以遵循如下模式: 创建自定义注解 `@CustomProperty` 并将其应用于目标字段上。此过程涉及几个步骤,首先是定义该注解本身。 #### 定义自定义注解 ```java import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface CustomProperty { String value(); } ``` 上述代码片段展示了如何声明一个新的注解 `@CustomProperty`,它接受一个字符串参数作为键名[^1]。 #### 实现 PropertyEditorSupport 或者利用 ConversionService 为了让 Spring 能够理解新的注解并从中提取相应的值,需要编写逻辑处理这些被标记的字段。一种常见做法是继承 `PropertyEditorSupport` 来转换指定类型的对象,或者是注册自定义的 `Converter<S, T>` 到全局 `ConversionService` 中去支持更灵活的数据类型映射。 对于较为复杂的场景下推荐后者,因为这允许更加优雅地管理不同类型之间的相互转化而不必为每一个可能的情况都单独写编辑器。 #### 编写处理器类 Processor 接下来要做的就是构建一个能够识别带有 `@CustomProperty` 注解成员变量并将它们绑定至相应环境变量上的组件。这里可以通过 AOP (面向切面编程) 技术或者直接在 Bean 初始化阶段完成这项工作。 下面是一个基于 AspectJ 的例子,它可以拦截所有含有此类注解的方法调用或属性访问操作,并执行必要的初始化动作: ```java import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; @Component @Aspect public class CustomPropertyProcessor { @Autowired private Environment env; @Around("@annotation(CustomProperty)") public Object process(ProceedingJoinPoint joinPoint) throws Throwable { // 获取当前正在访问的目标对象及其所属类 var targetClass = joinPoint.getTarget().getClass(); // 遍历所有的字段寻找那些被打上了我们的自定义标签的东西... Arrays.stream(targetClass.getDeclaredFields()) .filter(field -> field.isAnnotationPresent(CustomProperty.class)) .forEach(field -> { try { // 设置可访问权限以便修改私有域的内容 field.setAccessible(true); // 取得关联的 key 名字 final String propertyKey = field.getAnnotation(CustomProperty.class).value(); // 将来自 application.properties 文件里的实际数据赋给这个实例变量 field.set(joinPoint.getTarget(), env.getProperty(propertyKey)); } catch (IllegalAccessException e) { throw new RuntimeException(e); } }); return joinPoint.proceed(); } } ``` 这段代码实现了对任何应用了 `@CustomProperty` 注解的地方自动填充由应用程序外部化配置所提供的具体数值的功能[^2]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值