• 随笔记录-看nacos源码


    @Import注解

    @Import注解可以导入一些配置类,也就是创建一些指定对象。

    使用@Import导入普通类

    在这里插入图片描述

    项目结构中,import-consumer和import-provider都是同层级的module,import-consumer的pom文件中有引用import-provider的依赖;

    import-provider中创建接口的实现类:ImportFirstWayProviderConfig

    @Slf4j
    public class ImportFirstWayProviderConfig implements ImportAnnotationAble{
    
        @PostConstruct
        public void init(){
             log.info("这是@Import注解第一种用法,直接导入普通类");
        }
    
        @Override
        public String introduce() {
            return "这是@Import注解第一种用法,直接导入普通类";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    import-consumer中创建注解 @TestImportAnnotation

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Import(value = {ImportFirstWayProviderConfig.class})
    public @interface TestImportAnnotation {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    import-consumer中任意一个@service中使用刚才创建的@TestImportAnnotation注解:

    @Service
    @TestImportAnnotation
    public class ThreeImportService {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    然后启动 import-consumer module:

    在这里插入图片描述

    可以发现启动的是import-consumer,但是成功将import-provider中ImportFirstWayProviderConfig类给创建了。

    实现ImportSelector接口

    在import-provider种,添加一个类: ImportSecondWayProviderConfig

    @Slf4j
    public class ImportSecondWayProviderConfig implements ImportAnnotationAble{
    
    
        @PostConstruct
        public void init(){
            log.info("这是@Import注解【第二种】用法,在消费module里实现ImportSelector接口");
        }
    
        @Override
        public String introduce() {
            return "这是@Import注解【第二种】用法,在消费module里实现ImportSelector接口";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在import-consumer种,实现ImportSelector接口:ImportSecondWayImpl

    public class ImportSecondWayImpl implements ImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[]{"com.example.importProvider.config.importWay.ImportSecondWayProviderConfig"};
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    其中返回的字符串就是 ImportSecondWayProviderConfig 的全路径;在@TestImportAnnotation注解中添加:

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Import(value = {ImportFirstWayProviderConfig.class, ImportSecondWayImpl.class})
    public @interface TestImportAnnotation {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    启动import-consumer项目:

    在这里插入图片描述

    可以看到也是成功加载了。

    实现ImportBeanDefinitionRegistrar接口

    在import-provider中添加 ImportThirdWayProvider 类:

    @Slf4j
    public class ImportThirdWayProvider implements ImportAnnotationAble{
    
        @PostConstruct
        public void init(){
            log.info("这是@Import注解《第三种》用法,在消费module里实现ImportBeanDefinitionRegistrar接口");
        }
    
        @Override
        public String introduce() {
            return "这是@Import注解《第三种》用法,在消费module里实现ImportBeanDefinitionRegistrar接口";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在import-consumer中实现ImportBeanDefinitionRegistrar接口:

    public class ImportThirdWayImpl implements ImportBeanDefinitionRegistrar {
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            RootBeanDefinition define = new RootBeanDefinition(ImportThirdWayProvider.class);
            registry.registerBeanDefinition("importThirdWay",define);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在RootBeanDefinition的带参构造函数中,参数就是需要的ImportThirdWayProvider 对象,在@TestImportAnnotation注解中添加:

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Import(value = {ImportFirstWayProviderConfig.class, ImportSecondWayImpl.class, ImportThirdWayImpl.class})
    public @interface TestImportAnnotation {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    启动项目:

    在这里插入图片描述

    @ConditionalOnProperty注解

    新建一个注解 @ConditionToday:

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @ConditionalOnProperty(value = "today.isWorkDay", matchIfMissing = true)
    public @interface ConditionToday {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    新建一个注解修饰的类:

    @Slf4j
    @Configuration(proxyBeanMethods = false)
    @ConditionToday
    public class ConditionWorkBean {
    
        @PostConstruct
        public void init(){
            log.info("这是 ConditionWorkBean 的 init方法");
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在application.yml中添加配置:

    today:
      isWorkDay: true
    
    • 1
    • 2

    在spring.factories文件中增加这个类:

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
      com.example.importConsuemer.bean.ConditionWorkBean
    
    • 1
    • 2

    成功启动项目:

    在这里插入图片描述

    修改配置为false,再次启动项目,会发现并没有打印 日志,说明没有创建。

    @Bean注解修饰方法

    之前一直使用@Bean注解方法修饰无参的方法,在看NacosServiceRegistryAutoConfiguration源码的时候,发现修饰的是有参构造方法,好奇参数的值是怎么创建的。

    创建两个普通对象 BeanAnnotationUse ,BeanAnnotationUse :

    public class BeanHasCreated {
        private String name;
    
        public String getName() {
            return name;
        }
    }
    
    @Slf4j
    public class BeanAnnotationUse {
        private String name;
    
        public String getName() {
            return name;
        }
    
        public BeanAnnotationUse(String name) {
            this.name = name;
        }
    
        public BeanAnnotationUse() {
        }
    
        public String test(){
            log.info("这是使用@Bean注解创建的bean的测试方法,输出的name={}",getName());
            return getName();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    再新增一个中间类,TestBeanAnnotation,并将这个类配置再spring.factories中。

    @Slf4j
    public class TestBeanAnnotation {
    
        @PostConstruct
        public void init(){
            log.info("这是测试 @Bean注解的初始化方法");
        }
    
        @Bean
        public BeanAnnotationUse beanAnnotationUse2(BeanHasCreated bhc){
            return new BeanAnnotationUse("@Bean修饰有参数的方法");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    编译有报错,并且启动项目也会报错,提示的很清楚:找不到BeanHasCreated这个对象,

    在这里插入图片描述

    @Configuration
    public class BeanHasCreated {
        private String name;
    
        public String getName() {
            return name;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在BeanHasCreated类上增加@Configuration注解,这个时候编译没报错,启动项目也正常,那就说明@Bean修饰的方法参数必须是已经在spring中注入过的bean。

    ApplicationListener接口和ApplicationEventPublisher接口

    ApplicationEventPublisher接口时spring提供的一个用来发布事件的接口,ApplicationListener接口时spring提供的一个用来订阅事件发布的接口。

    ApplicationListener接口demo

    新建一个订阅事件类:

    @Configuration
    @Slf4j
    public class TestWebServerEventListener implements ApplicationListener {
        @Override
        public void onApplicationEvent(WebServerInitializedEvent event) {
            log.info("接收的容器启动事件的");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    然后直接启动项目,会发现打印结果符合预期。

    在这里插入图片描述

    使用@EventListener注解

    在这里插入图片描述

    自定义事件和自定义订阅

    新建自定义事件对象:

    public class TestSelfEvent extends ApplicationEvent {
    
        private String eventName;
    
        public TestSelfEvent(Object source) {
            super(source);
            this.eventName = "随便自定义的事件";
        }
    
        public TestSelfEvent(Object source, Clock clock) {
            super(source, clock);
        }
    
        @Override
        public String toString() {
            return "TestSelfEvent{" +
                    "eventName='" + eventName + '\'' +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    新建发布类:

    @Slf4j
    @Component
    public class TestSelfPublisher implements ApplicationEventPublisher, ApplicationContextAware {
    
        private ApplicationContext context;
    
        @Override
        public void publishEvent(Object event) {
            log.info("准备发布一个事件:{}",event.toString());
            context.publishEvent(event);
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            context = applicationContext;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    新建订阅对象:

    @Slf4j
    @Configuration
    public class SelfEventListener implements ApplicationListener {
    
        @Override
        public void onApplicationEvent(TestSelfEvent event) {
            log.info("接收到事件,内容是,event.getSource() = {}",event.getSource());
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    再新建一个测试用的类:

    @Configuration
    @AutoConfigureAfter({TestSelfPublisher.class,SelfEventListener.class})
    @Slf4j
    public class TestSelfEventImport {
    
    
        @Autowired
        private  TestSelfPublisher tsp;
    
        @PostConstruct
        public void init(){
            TestSelfEvent tsf = new TestSelfEvent("这是一个自定义的随便的任意的一个事件");
            tsp.publishEvent(tsf);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    启动项目,打印如下:

    在这里插入图片描述

  • 相关阅读:
    jitsi音视频会议集成到自己的网页中
    为什么NIO比BIO效率高
    Pytorch torch.split()的简单用法
    git-新增业务代码分支
    报错处理:Java休眠时发生错误
    【Java进阶篇】第六章 IO流
    首发 自媒体博客最新版Spimes主题 X6.0开心免授权
    Flutter转换png图片为jpg图片
    成员内部类、局部内部类、匿名内部类
    【如何配置应用Github开源项目(以具体实例为案例,针对入门选手)】
  • 原文地址:https://blog.csdn.net/ZRL1996/article/details/127857888