1)通用自定义注解
1.创建自定义注解
首先,定义一个自定义注解。注解可以用于类、方法、字段或参数等,具体取决于注解的使用场景。注解本质上是一个普通的Java接口,只是它带有 @interface
关键字。
例如,创建一个自定义注解 @MyAnnotation
:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD) // 注解作用的目标,比如方法、字段、类等
@Retention(RetentionPolicy.RUNTIME) // 注解的保留策略,决定注解在哪个阶段可用
public @interface MyAnnotation {
String value() default "default value"; // 注解的属性,允许提供默认值
}
解释:
@Target
:指定注解可以应用于哪些元素,例如ElementType.METHOD
表示该注解可用于方法上。@Retention
:指定注解的保留策略。RetentionPolicy.RUNTIME
表示该注解在运行时可用(通常Spring的处理器需要在运行时使用它)。value
:这是注解的一个属性,可以自定义多个属性,并提供默认值。
2. 创建注解处理器
为了让Spring能够识别并处理这个注解,你可以通过 @Component
或者其他方式将其注册为Spring Bean。以下是一个基于 AOP
(面向切面编程)的处理器示例,处理自定义注解。
例如,使用 AspectJ
实现一个切面,处理带有 @MyAnnotation
的方法:
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MyAnnotationAspect {
@Before("@annotation(myAnnotation)") // 这里的 @annotation 表示匹配有这个注解的方法
public void beforeMethod(MyAnnotation myAnnotation) {
// 通过myAnnotation获取注解信息并进行相应处理
System.out.println("执行带有MyAnnotation注解的方法,注解的值:" + myAnnotation.value());
}
}
3. 使用自定义注解
现在,你可以在项目的某个方法上使用 @MyAnnotation
:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@MyAnnotation(value = "Hello Annotation!")
@GetMapping("/test")
public String test() {
return "This is a test";
}
}
4. 启用AspectJ支持
确保你已经在Spring Boot项目中引入了AOP支持。通常,你需要在 pom.xml
文件中添加 spring-boot-starter-aop
依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
然后在主类或配置类上添加 @EnableAspectJAutoProxy
注解,启用AspectJ自动代理:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@SpringBootApplication
@EnableAspectJAutoProxy
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
2)注解处理器2 - 在 Bean 创建后执行相关初始化的逻辑
通过实现 BeanPostProcessor 接口,编写 postProcessAfterInitialization 方法。
例子1:类中的注解
- RpcService 注解:用于 rpc 的服务提供者的服务实现类中
/**
* 服务提供者注解(用于注册服务)
* @Author Vanky
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface RpcService {
// 注解字段
}
- 在 Bean 创建完后,希望完成一些初始化的工作
/**
* Rpc 服务提供者服务启动
* @author vanky
* @create 2024/10/11 20:42
*/
@Slf4j
public class RpcProviderBootstrap implements BeanPostProcessor {
/**
* Bean 初始化后执行,注册服务
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
Class<?> beanClass = bean.getClass();
// 获取该 Bean 上的 RpcService 注解
RpcService rpcService = beanClass.getAnnotation(RpcService.class);
if (rpcService != null){
// 该 bean 带有 RpcService 注解
// 完成一些初始化工作
}
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
例子2:属性上的注解
- RpcReference 注解:为该属性注入服务实现类
/**
* 服务消费者注解(用于注入服务)
* @Author Vanky
*/
@Target({ElementType.FIELD}) // 该注解用于属性上
@Retention(RetentionPolicy.RUNTIME)
public @interface RpcReference {
// 注解字段
}
- 初始化 Bean 时,为该 Bean 中带有 RpcReference注解的属性注入服务。
/**
* Rpc 服务消费者启动
*
* 在 Spring 容器中注入 Bean 之后,
* 遍历该 Bean 的字段,查找标注了 @RpcReference 注解的字段,
* 并为这些字段生成代理对象。
* @author vanky
*/
@Slf4j
public class RpcConsumerBootstrap implements BeanPostProcessor {
/**
* Bean 初始化后执行,注入服务
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
Class<?> beanClass = bean.getClass();
// 遍历对象的所有属性
Field[] declaredFields = beanClass.getDeclaredFields();
for (Field field : declaredFields) {
RpcReference rpcReference = field.getAnnotation(RpcReference.class);
if (rpcReference != null) {
// 为属性生成代理对象
Class<?> interfaceClass = rpcReference.interfaceClass();
if (interfaceClass == void.class) {
interfaceClass = field.getType();
}
// 允许通过反射修改私有字段的值,确保能够将代理对象注入
field.setAccessible(true);
Object proxyObject = ServiceProxyFactory.getProxy(interfaceClass);
try {
field.set(bean, proxyObject);
field.setAccessible(false);
} catch (IllegalAccessException e) {
throw new RuntimeException("为字段注入代理对象失败", e);
}
}
}
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}