自定义注解注入属性值(基于反射和静态变量)

如何通过Spring配置类处理注解

  1. 定义注解
  2. 定义一个Spring配置类
  3. 在配置类中使用Java反射注入静态属性

定义注解

定义一个注解“Name”,Name注解可以接收一个String类型的属性,并且可以使用在类或者属性上

@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Name {

    String value() default "";
}

定义Sping 配置类

定义一个Spring配置类,并使用@PostConstruct注解方法实现在Spring启动过程中处理注解

@Configuration
public class NameConfig {

    List classList = new ArrayList<>();

    @PostConstruct
    public void init() {
        //处理注解
    }
}

使用Java反射注入静态属性

通过Java反射获取指定类的属性,并判断其中是否使用Name注解,有的话就获取Name注解的值,并注入到该静态属性中

tips:这里要指定会使用到Name注解的类,例子中就是Xiaohong.class

@Configuration
public class NameConfig {

    List classList = new ArrayList<>();

    @PostConstruct
    public void init() {
        classList.add(Xiaohong.class);
        classList.add(Xiaobai.class);
        classList.forEach(e -> setName(e));
    }

    public void setName(Class clazz) {
        //处理注解在类上
        if (clazz.isAnnotationPresent(Name.class)) {
            Name annotation = (Name) clazz.getAnnotation(Name.class);
            try {
                Field field = clazz.getDeclaredField("name");
                field.setAccessible(true);
                field.set(null, annotation.value());
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return;
        }
        //处理注解在属性上
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            Name annotation = declaredField.getAnnotation(Name.class);
            if (null == annotation) {
                continue;
            }
            declaredField.setAccessible(true);
            try {
                declaredField.set(null, annotation.value());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
}

在属性上中使用注解

在类中的某个属性上使用注解Name,并指定值为“小红”

public class Xiaohong {

    @Name("小红")
    static String name;

    public void sayHi() {
        System.out.println("hello, my name is " + name);
    }
}

测试一下

如下例子会打印出:
hello, my name is 小红

@RunWith(SpringRunner.class)
@SpringBootTest
public class HelloTest {

    @Test
    public void HelloXiaohongTest() {
        Xiaohong xiaohong = new Xiaohong();
        xiaohong.sayHi();
    }
}

在类上中使用注解

在类上使用注解Name,并指定值为“小白”

@Name("小白")
public class Xiaobai {

    static String name;

    public void sayHi() {
        System.out.println("hello, my name is " + name);
    }
}

测试一下

如下例子会打印出:
hello, my name is 小白

@RunWith(SpringRunner.class)
@SpringBootTest
public class HelloTest {

    @Test
    public void HelloXiaobaiTest() {
        Xiaobai xiaobai = new Xiaobai();
        xiaobai.sayHi();
    }
}

为什么只能注解在作用与静态属性

通过上面的方法我们自动,Java注入属性的时候是在Spring启动过程中,并不是运行过程,所以这时候只知道类,而不知道对象。
而只有静态属性的注入是不需要指定对象的,所以静态属性可以通过这种方式来注入。

假如我们想要处理非静态属性的注入,可以封装一个注解处理方法,在运行过程中创建对象之后(例如构造方法中),调用注解处理方法,即可达到效果。

例如:

在构造方法中处理注解

定义一个方法setName(),用来处理Name注解,并以使用了Name注解的对象作为参数;在构造方法中调用这个方法。

@Name("小蓝")
public class Xiaolan {

    private String name;

    public Xiaolan() {
        setName(this);
    }

    public void sayHi() {
        System.out.println("hello, my name is " + name);
    }

    public void setName(Xiaolan xiaolan) {
        Class clazz = this.getClass();
        if (clazz.isAnnotationPresent(Name.class)) {
            Name annotation = (Name) clazz.getAnnotation(Name.class);
            xiaolan.name = annotation.value();
            return;
        }
    }
}

测试一下

如下例子会打印出:
hello, my name is 小蓝

@RunWith(SpringRunner.class)
@SpringBootTest
public class HelloTest {

    @Autowired
    NameConfig nameConfig;

    @Test
    public void HelloXiaolanTest() {
        Xiaolan xiaolan = new Xiaolan();
        xiaolan.sayHi();
    }
}

tips: 实际使用过程中建议抽象一个超类,使用超类的构造方法来处理注解。

总结

沿着上文的思路我们可以发现,自定义注解的重点在于 根据注解要达到的目的,考虑如何处理注解。

例如: 本文的需求是通过注解来注入一个属性,考虑到属性可以为static,所以通过Spring配置加反射的方式; 如果属性不能为static,则可以通过构造方法来实现。

在实际开发中,处理注解还有其他多种方式,各有优缺点。

例如:

  • 在Bean的后置处理器中处理注解
    通过实现Bean的后置处理器方法,配合Java反射。
    这种注解可以使用在类、方法、属性上。
    局限性是这种注解只能在Spring Bean中使用。
    参考SpringBoot之自定义注解(基于BeanPostProcessor接口实现)

  • 通过Spring AOP处理
    原理是通过注解来定义切点。
    优点是编写简单。
    但这种注解也只能使用在Spring Bean中,且只能作用于方法上
    参考SpringBoot之自定义注解(基于AOP实现)

  • 通过AspectJ切面处理
    通过SspectJ切面编程实现,类似SPring AOP切面。而已没有只能作用在方法上的局限

  • 通过Spring配置类处理
    通过配置类实现在Spring启动过程中处理注解,配置Java反射机制来注入静态变量。
    局限性是只能注入静态属性。
    这也是本文所使用的方法。

    博主个人博客原文

你可能感兴趣的:(java,spring)