• Spring Import注解


    今天了解了,Spring @Import的使用

    先贴上Spring官方关于Spring @Import注解的文档链接   https://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-java-using-import

    一.@Import 引入一个普通java对象 适用4.2.0之后版本

    二.@Import与@Configuration使用方式

    三.@Import 与 ImportBeanDefinitionRegistrar 使用4.2.0之前版本

    四.@Import 与 ImportSelector 使用方式

    一。@Import直接引入一个类,将其作为Spring bean,受Spring容器管理;(Spring 4.2.0以及之后版本可以使用)

    在Spring 4.2.x之前的版本,@Import注解无法引入一个没有注解 @Configuration 或者 @Bean 且没有实现ImportBeanDefinitionRegistry 或者 ImportSelector 的类,将其作为Spring Bean管理;

      如果你在使用的版本是4.2.0之前的版本,想要使用@Import导入一个类,方法二、三或许可行;

    特地拿了Spring 4.1.9版本,测试,@Import一个普通的Java类,抛出异常:

    Exception in thread "main" org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: pack1.AppConfig2 was @Import'ed but is not annotated with @Configuration nor does it declare any @Bean methods; it does not implement ImportSelector or extend ImportBeanDefinitionRegistrar. Update the class to meet one of these requirements or do not attempt to @Import it.

    翻译过来就是: AppConfig2被import导入了,但是没有标注@Configuration或者也没有声明任何@Bean方法,也没有实现ImportSelector或者继承ImportBeanDefinitionRegistrar类;

    不做任何改动,修改maven导入的Spring版本号,拿4.2.0测试,就成功Import该java类;  Import导入的类,默认beanId是类全限定名,可以设定@Configuration改变类的beanId;

    关于为什么配置类上加@Configuration作用,查看我的理解      https://www.cnblogs.com/lvbinbin2yujie/p/10279416.html

    @Configuration
    @Import({AppConfig2.class})
    public class AppConfig1 {
    
        public static void main(String[] args) {
            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig1.class);
            String[] names = ac.getBeanDefinitionNames();
            for (String string : names) {
                System.out.println(string+","+ac.getBean(string));
            }
        }
    }
    
    
    public class AppConfig2 {
        
    }

    二。@Import引入配置类@Configuration标注的类; 纯属个人看Spring官方文档的理解,大致能看懂,Spring官方文档链接:https://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-java-using-import

       2.1 先说最容易看懂的一种使用方式,每个@Configuration里的@Bean定义都很明显,就像开发过程中写了好多配置文件,然后在某一个XML里面<import  />其他的bean;

    @Configuration
    public class ConfigA {
    
        @Bean
        public A a() {
            return new A();
        }
    }
    
    public class A {
    
    }
    @Import({ConfigA.class})
    @Configuration
    public class ConfigB {
    
        @Bean
        public B b() {
            return new B();
        }
        
        public static void main(String[] args) {
            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ConfigB.class);
            String[] names = ac.getBeanDefinitionNames();
            for (String string : names) {
                System.out.println(string+" , "+ac.getBean(string));
            }
        }
    }

    测试结果如下:  印证了 1. @Import引入的类默认beanId为类全路径名  ;  2. @Configuration标注的类是动态代理之后的类 ;

    configB , pack1.ConfigB$$EnhancerBySpringCGLIB$$1acf4932@4313f5bc
    pack1.ConfigA , pack1.ConfigA$$EnhancerBySpringCGLIB$$e26ca611@7f010382
    a , pack1.A@1e802ef9
    b , pack1.B@2b6faea6

      2.2 但是我们平常见得开发中,比如service配置文件在一个XML里,dao的配置文件在一个XML中,就会跨文件引用,但是我们都会写 ref引用那个对象,因为Spring会帮助我们找到需要的对象;

    举个例子模拟这种跨文件的引用,Spring官方文档里面也有这样一个例子;

    // dao类
    public class AccountDao {
    
        private DataSource dataSource;
        
        public AccountDao(DataSource dataSource) {
            this.dataSource=dataSource;
        }
        
        public void saveMoney() {
            System.out.println("利用dataSource模拟存钱操作");
        }
    
    }
    // service类
    public class AccountService {
    
        private AccountDao accountDao;
        
        public AccountService(AccountDao accountDao) {
            this.accountDao=accountDao;
        }
        
        public void saveMoney() {
            accountDao.saveMoney();
        }
    }

    下面将使用注解,来代替原来的XML配置方式,定义了两个配置类DaoConfig  ServiceConfig 

    @Configuration
    public class DaoConfig {
    
        @Autowired
        private DataSource dataSource;
        
        @Bean
        public AccountDao accountDao() {
            return new AccountDao(dataSource);
        }
    }
    @Configuration
    public class ServiceConfig {
    
        @Autowired
        private AccountDao accountDao;
        
        @Bean
        public AccountService accountService() {
            return new AccountService(accountDao);
        }
        
    
    @Configuration
    @Import({ServiceConfig.class,DaoConfig.class})
    public class SystemConfig {
        
        @Bean
        public DataSource dataSource() {
            return new DruidDataSource();
        }
        
        public static void main(String[] args) {
            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SystemConfig.class);
            AccountService service = ac.getBean(AccountService.class);
            service.saveMoney();   
        }
    }

    查看测试结果:

    利用dataSource模拟存钱操作

    简单来讲下我的分析:从bean的load讲一下, 第一个加载的bean是SystemConfig,先解析Import注解,和SysemConfig一样递归加载ServiceConfig以及DaoConfig这两个类,递归因为没有别的注解了,所以就是解析@Bean,也就是纳入了AccountService AccountDao 最后是 DataSource 这三个Bean ,别忘了 加上SystemConfig、ServiceConfig 、DaoConfig这样六个bean对象;然后属性@Autowired注入,如果属性注入的时候这个bean还没有就回去实例化这个Bean,这样一来就完成了。

    可能看完这个方法,还是复杂啊,假如少Import一个配置类,那肯定会报错了。

      2.3 此外,Spring另外一个方法可以实现上面功能,比较奇特的方式;

    @Configuration
    public class DaoConfig {
    
        @Autowired
        private DataSource dataSource;
        
        @Bean
        public AccountDao accountDao() {
            return new AccountDao(dataSource);
        }
    }
    @Configuration
    public class ServiceConfig2 {
    
        @Autowired
        private DaoConfig daoConfig;
        
        @Bean
        public AccountService accountService() { 
            AccountDao dao1 = daoConfig.accountDao();
            System.out.println("ServiceConfig中的dao:"+dao1);
            return new AccountService(dao1);
        }
    }
    @Configuration
    @Import({ServiceConfig2.class,DaoConfig.class})
    public class SystemConfig2 {
    
        @Autowired
        private ServiceConfig2 serviceConfig;
        
        @Bean
        public DataSource dataSource() {
            return new DruidDataSource();
        }
        
        public static void main(String[] args) {
            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SystemConfig2.class);
            AccountService service = ac.getBean(AccountService.class);
            service.saveMoney();
        }
    }

    测试结果: 由于使用main方法测试,如果在业务方法中可以直接 serviceConfig.accountService().saveMoney();

    ServiceConfig中的dao:pack2.AccountDao@3daa422a
    利用dataSource模拟存钱操作

    说明: 1. @Configuration注解的类本身也是个bean对象,这点不能忘;   2.@Configuration标注的类调用 同一个@Bean方法 会一直得到同一个bean对象 ;

        3. Spring官方文档中有一句:要使用@Configuration注解需要使用 <context:component-scan /> 标签

    三。方法三,实现ImportBeanDefinitionRegistrar接口, 这也是 Spring 4.2.0之前版本 如果想要引入一个普通bean,没法直接Import,实现接口并且重写方法里注册这个bean对象;

    public class ABeanDefinitionRegistry implements ImportBeanDefinitionRegistrar{
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                BeanDefinitionRegistry registry) {
         //简单说下使用,只要将beanDefinition注册到registry里,就算把bean对象引入了;只是简单写了个例子引入A类 BeanDefinitionBuilder bd
    = BeanDefinitionBuilder.rootBeanDefinition(A.class); registry.registerBeanDefinition("a", bd.getBeanDefinition()); } }
    @Import({ABeanDefinitionRegistry.class})

    这个方法是怎么调用的呢?ConfigurationClassParser类的processImports方法,是解析类上@Import注解的;ConfigurationClassBeanDefinitionReader的loadBeanDefinitionsFromRegistrars方法,遍历引入的实现ImportBeanDefinitionRegistrar的类,然后调用其registerBeanDefinitions方法; 

    四。方法四,实现ImportSelector接口,这种方式相比之前方式更加容易理解,只需要返回bean的类全限定名即可;

    public class AImportSelector implements ImportSelector{
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[] {A.class.getName()};
        }
    }

    这样调用即可;

    @Import({AImportSelector.class})

     三、四两种方式都有个参数为AnnotationMetadata是类上的注解信息的,可以这样实现很多功能,由于不是很了解;

  • 相关阅读:
    【Vue学习】vue中 关于$emit的用法
    【Vue学习】vue中watch高级用法deep和immediate
    【Go学习】YAML格式的语法
    LINQ学习笔记(十一)
    LINQ学习笔记(十)
    LINQ学习笔记(八)
    LINQ学习笔记(十二)
    LINQ学习笔记(十三)
    LINQ学习笔记(六)
    LINQ学习笔记(七)
  • 原文地址:https://www.cnblogs.com/lvbinbin2yujie/p/10289528.html
Copyright © 2020-2023  润新知