前言
Hello,大家好,我是java小面。不知道大家有没有自己实现过一个注解来替换Spring原有注解的经历,有人说Spring不是有注解给你用吗?干嘛还要特地的去实现一个来替换呢?
我们这次去请教ChatGPT如何写这么一段代码
只是可惜,它似乎不太能理解我的需求,还是自己来吧!
关于这个需求,小面当然不是闲得慌,我们做业务功能开发的时候,往往只懂得它该怎么用,却不知道它为什么可以这么用,它具体又是怎样的一个运行机制?
而当我们了解了它的一个机制后,不仅保持住了对技术专研的热情,还收获了成就感,让我们在技术的这条路上越走越远。
如何自定义依赖注入注解?
- 基于AutowiredAnnotationBeanPostProcessor实现
- 自定义实现
- InjectedElement
- InjectionMetadata
- InstantiationAwareBeanPostProcessor
- MergedBeanDefinitionPostProcessor
- 生命周期处理
- 元数据
这次我们拿最常见的一个注解进行开发。
举个例子
一、copy注解
这是一个大家都常用的注解
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Autowired { /** * Declares whether the annotated dependency is required. * <p>Defaults to {@code true}. */ boolean required() default true; }
@Autowired最常用于依赖注入,但是大家都知道,注解是无法拓展的,如果大家想要拓展,唯一的方法就是copy一份一模一样的取代掉它的使用。
比如我重新定义一个Autowired注解且把它用于依赖注入中。
我们给他命名 MyAutowired
/** * 自定义注解 */ @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Autowired public @interface MyAutowired { /** * Declares whether the annotated dependency is required. * <p>Defaults to {@code true}. */ boolean required() default true; }
其他的和@Autowired一模一样,区别在于public上面把 Autowired注解标注上了。
因为Autowired本身拥有ElementType.ANNOTATION_TYPE就说明它允许被使用在注解上面
测试Demo
package org.example.definition; import org.example.pojo.MyAutowired; import org.example.pojo.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author Java面试教程 * @date 2022-12-18 21:14 */ @Configuration public class Demo { @Bean public User User(){ return new User(1,"Java面试教程"); } @Autowired private User user; @MyAutowired private User myUser; public static void main(String[] args) { //创建BeanFactory容器 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); //注册当前类,主要目的是获取@Bean applicationContext.register(Demo.class); //启动应用上下文 applicationContext.refresh(); Demo bean = applicationContext.getBean(Demo.class); System.out.println("user对象:"+bean.user); System.out.println("myUser对象:"+bean.myUser); applicationContext.close(); } }
结果
Connected to the target VM, address: '127.0.0.1:65300', transport: 'socket' user对象:User{id=1, name='Java面试教程'} myUser对象:User{id=1, name='Java面试教程'} Disconnected from the target VM, address: '127.0.0.1:65300', transport: 'socket'
说明拥有ElementType.ANNOTATION_TYPE的元标注可以通过这种方式来拓展开发。
二、自定义注解
接下来我们讲一下,用AutowiredAnnotationBeanPostProcessor怎么去实现自定义依赖注入注解
首先还是定义一个注解,这个注解不包含其他注解
/** * 自定义依赖注入注解 */ @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CustomAutowired { }
然后重新实现 AutowiredAnnotationBeanPostProcessor 的bean对象
@Bean(name = AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME) public static AutowiredAnnotationBeanPostProcessor beanPostProcessor(){ AutowiredAnnotationBeanPostProcessor beanPostProcessor = new AutowiredAnnotationBeanPostProcessor(); Set<Class<? extends Annotation > > types = new LinkedHashSet<>(Arrays.asList(Autowired.class, MyAutowired.class, CustomAutowired.class)); beanPostProcessor.setAutowiredAnnotationTypes(types); return beanPostProcessor; }
AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME 是Autowired默认的Bean名称,所以我们重新,且把自己定义的CustomAutowired注入进去,那么我们之后使用@CustomAutowired的时候它就可以达到和Autowired一样的效果了。
Set<Class<? extends Annotation > > types = new LinkedHashSet<>(Arrays.asList(Autowired.class, MyAutowired.class, CustomAutowired.class)); beanPostProcessor.setAutowiredAnnotationTypes(types);
如果types里只有CustomAutowired.class,没有Autowired.class, MyAutowired.class,那么原本使用@Autowired和@MyAutowired的对象,就只会拿到null,因为你重新覆盖的值里面没有他们。那么他们就不被归类到依赖注入的用法里面了。
感兴趣的可以自己试试运行,随便找一个类替换掉User就能执行了。
测试Demo
import static org.springframework.context.annotation.AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME; /** * @author Java面试教程 * @date 2022-12-26 21:14 */ @Configuration public class Demo { @Bean public User User(){ return new User(1,"Java面试教程"); } @Autowired private User user; @CustomAutowired private User customAutowiredUser; @MyAutowired private User myUser; @Bean(name = AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME) public static AutowiredAnnotationBeanPostProcessor beanPostProcessor(){ AutowiredAnnotationBeanPostProcessor beanPostProcessor = new AutowiredAnnotationBeanPostProcessor(); Set<Class<? extends Annotation > > types = new LinkedHashSet<>(Arrays.asList(Autowired.class, MyAutowired.class, CustomAutowired.class)); beanPostProcessor.setAutowiredAnnotationTypes(types); return beanPostProcessor; } public static void main(String[] args) { //创建BeanFactory容器 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); //注册当前类,主要目的是获取@Bean applicationContext.register(Demo.class); //启动应用上下文 applicationContext.refresh(); Demo bean = applicationContext.getBean(Demo.class); System.out.println("user对象:"+bean.user); System.out.println("myUser对象:"+bean.myUser); System.out.println("自定义对象:"+bean.customAutowiredUser); applicationContext.close(); } }
结果
Connected to the target VM, address: '127.0.0.1:52324', transport: 'socket' user对象:User{id=1, name='Java面试教程'} myUser对象:User{id=1, name='Java面试教程'} 自定义对象:User{id=1, name='Java面试教程'} Disconnected from the target VM, address: '127.0.0.1:52324', transport: 'socket'
结束语
我们刚刚通过了 拓展@Autowired注解 以及 复用AutowiredAnnotationBeanPostProcessor这个API 两种方式来实现替代@Autowired注解,虽然内容简单,但是却通过了AutowiredAnnotationBeanPostProcessor向大家揭晓了为什么Autowired可以达到依赖注入的原因。