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:类中的注解

  1. RpcService 注解:用于 rpc 的服务提供者的服务实现类中
/**
 * 服务提供者注解(用于注册服务)
 * @Author Vanky
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface RpcService {

    // 注解字段

}
  1. 在 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:属性上的注解

  1. RpcReference 注解:为该属性注入服务实现类
/**
 * 服务消费者注解(用于注入服务)
 * @Author Vanky
 */
@Target({ElementType.FIELD})	// 该注解用于属性上
@Retention(RetentionPolicy.RUNTIME)
public @interface RpcReference {

    // 注解字段

}
  1. 初始化 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);
    }
}