Spring 注解的实现

2020/04/07 10:59 上午 posted in  Java

在 Java 中挣扎了一个月,每天都充满了 “我怎么这么菜” 的无力感。本着输出倒逼输入的心态,整理第一篇 Java 文章,愿自己能真心说一句 Java 真香。

自定义注解

注解本身只是元数据,描述了数据的数据,描述的对象可以是类、方法、属性、参数或构造器等。而自定义注解的实现是借助了接口的能力,通常有两类实现方式:BeanPostProcessor 和 BeanFactoryPostProcessor。

BeanPostProcessor

作用范围在属性,在类的属性上打上自定义注解,可以扩展类的能力。实现方法如下:

定义自定义注解

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectSideCar {
}

完成 BeanPostProcessor 的实现

@Component
public class InjectSidecarAnnotationBeanPostProcessor implements BeanPostProcessor {

    private Class<? extends Annotation> changeAnnotationType;

    @Autowired
    private SideCar sideCar;

    public InjectSidecarAnnotationBeanPostProcessor() {
        this.changeAnnotationType = InjectSideCar.class;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        ReflectionUtils.doWithFields(bean.getClass(), field -> {
            ReflectionUtils.makeAccessible(field);
            if (field.isAnnotationPresent(changeAnnotationType)) {
                field.set(bean, sideCar);
            }
        });

        return bean;
    }
}

使用自定义注解

@Component
public class Demo {
    @InjectSideCar
    private SideCar sideCar;

    public void doSomething() {
        System.out.println(sideCar.doSomething());
    }
}

在测试类中进行验证:

@SpringBootTest
class HdlsApplicationTests {
    @Autowired
    private Demo d;

    @Test
    public void testDemo() {
        d.doSomething();
    }
}

运行结果如下:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.6.RELEASE)

2020-04-21 00:39:06.014  WARN 37818 --- [           main] o.s.boot.StartupInfoLogger               : InetAddress.getLocalHost().getHostName() took 5003 milliseconds to respond. Please verify your network configuration (macOS machines may need to add entries to /etc/hosts).
2020-04-21 00:39:11.020  INFO 37818 --- [           main] com.example.hdls.HdlsApplicationTests    : Starting HdlsApplicationTests on hdls.local with PID 37818 (started by zhuweiwei in /Users/zhuweiwei/Java/hdls)
2020-04-21 00:39:11.021  INFO 37818 --- [           main] com.example.hdls.HdlsApplicationTests    : No active profile set, falling back to default profiles: default
2020-04-21 00:39:11.299  INFO 37818 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'sideCar' of type [com.example.hdls.SideCar] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-04-21 00:39:11.352  INFO 37818 --- [           main] com.example.hdls.HdlsApplicationTests    : Started HdlsApplicationTests in 15.497 seconds (JVM running for 16.32)

I am a sidecar

BeanFactoryPostProcessor

作用范围在类,在类上打上自定义注解,可以实现自动注入 bean。实现方法如下:

自定义注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomBean {
}

完成 BeanFactoryPostProcessor 的实现

@Component
public class CustomBeanDefinitionRegistryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner((BeanDefinitionRegistry) configurableListableBeanFactory);
        scanner.addIncludeFilter(new AnnotationTypeFilter(CustomBean.class));
        scanner.scan("com.example.hdls");
    }
}

使用自定义注解

@CustomBean
public class Demo {

    public void doSomething() {
        System.out.println("I am a custom bean.");
    }
}

在测试类中进行验证:

@SpringBootTest
class HdlsApplicationTests {

    @Autowired
    private Demo d;

    @Test
    void contextLoads() {
    }

    @Test
    public void testDemo() {
        d.doSomething();
    }
}

运行结果如下:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.6.RELEASE)

2020-04-21 00:45:28.288  WARN 37852 --- [           main] o.s.boot.StartupInfoLogger               : InetAddress.getLocalHost().getHostName() took 5001 milliseconds to respond. Please verify your network configuration (macOS machines may need to add entries to /etc/hosts).
2020-04-21 00:45:33.298  INFO 37852 --- [           main] com.example.hdls.HdlsApplicationTests    : Starting HdlsApplicationTests on hdls.local with PID 37852 (started by zhuweiwei in /Users/zhuweiwei/Java/hdls)
2020-04-21 00:45:33.299  INFO 37852 --- [           main] com.example.hdls.HdlsApplicationTests    : No active profile set, falling back to default profiles: default
2020-04-21 00:45:33.566  INFO 37852 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'sideCar' of type [com.example.hdls.SideCar] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-04-21 00:45:33.619  INFO 37852 --- [           main] com.example.hdls.HdlsApplicationTests    : Started HdlsApplicationTests in 15.497 seconds (JVM running for 16.223)

I am a custom bean.