定义一个注解“Name”,Name注解可以接收一个String类型的属性,并且可以使用在类或者属性上
@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Name {
String value() default "";
}
定义一个Spring配置类,并使用@PostConstruct注解方法实现在Spring启动过程中处理注解
@Configuration
public class NameConfig {
List classList = new ArrayList<>();
@PostConstruct
public void init() {
//处理注解
}
}
通过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反射机制来注入静态变量。
局限性是只能注入静态属性。
这也是本文所使用的方法。
博主个人博客原文