一、ImportSelector注入组件

功能:可以根据字符串数组(数组元素为类的全类名)来批量的注入指定的bean。

基本使用

项目的包路径如下:
在这里插入图片描述
SpringBoot默认会扫描启动类所在包及其子包下的组件,并将其注入容器中,不会扫描上图中的config、importSelector、register、service包下的组件,可以在启动类上添加@ComponScan(“com”)注解来定义扫描com包下的所有组件,也可以使用@Import(MyImportSelector.class)注解单个或批量导入组件。

  1. 自定一个类实现ImportSelector接口,实现注入bean的方法

    public class MyImportSelector implements ImportSelector {
    
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            //读取resource文件下配置文件中的内容
            ResourceBundle rb = ResourceBundle.getBundle("import");
            String items = rb.getString("className");
            //以,分割返回字符数组
            String[] classNames = items.split(",");
            return classNames;
        }
    
        //将bean中包含Admin的组件过滤,不注入容器中
        @Override
        public Predicate<String> getExclusionFilter() {
            return s -> s.contains("Admin");
        }
    }
    
  2. 配置文件内容

    #批量注入容器中的bean的类路径
    className=com.service.AdminService,\
     com.service.GroupService,\
      com.service.UserService
    
  3. 将自定义的实现类注入容器中

    //先容器中注入自定义实现ImportSelector接口的实现类
    @Import(MyImportSelector.class)
    @SpringBootApplication
    public class SpringbotSourceApplication {
    
        public static void main(String[] args) {
            ConfigurableApplicationContext context = SpringApplication.run(SpringbotSourceApplication.class, args);
        }
    }
    
  4. 以debug方法测试容器中是否注入了properties文件中定义的bean
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    由debug结果可以得到,导入容器的MyImportSelector将配置文件中符合条件的UserService、GrouService注入了容器中,将AdminService过滤了,所以可以使用ImportSelect批量导入一些配置类。

二、ImportBeanDefinitionRegistrar注入组件

功能:可以向容器中动态的注入Bean对象和Bean的代理对象,如mybatis启动器就是使用了它实现了Mapper接口的代理对象注入容器中的。

2.1 根据类路径装载单个对象

基本包路径
在这里插入图片描述
目标:使用ImportBeanDefinitionRegistrar接口实现类,将register包下的Cat注入容器中。

  1. 创建SimpleRegister类实现ImportBeanDefinitionRegistrar接口,创建BeanDefinition类并配置其基本属性,将其挂载到BeanDefinitionRegistry中实现组件的注入,方法参数具体的属性和方法可以用户debug方法查看

    public class SimpleRegister implements ImportBeanDefinitionRegistrar {
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            //创建BeanDefinition接口的实现类,这里使用其子类对象
            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
            //根据bean的完全限定名创建对象
            beanDefinition.setBeanClassName("com.register.Cat");
            //根据bean的class创建对象
    //        beanDefinition.setBeanClass(Cat.class);
            //将beanDefinition的注册信息注入到beanFactory中,最后该bean会被注入容器中
            registry.registerBeanDefinition("cat",beanDefinition);
        }
    }
    
  2. 将定义的配置类注入容器中

    @SpringBootApplication
    @Import(SimpleRegister.class)
    public class SpringbotSourceApplication {
    
        public static void main(String[] args) {
            ConfigurableApplicationContext context = SpringApplication.run(SpringbotSourceApplication.class, args);
        }
    }
    
  3. debug测试查看,Cat类注入成功
    在这里插入图片描述

2.2 扫描指定包下的自定义注解

基本包路径
在这里插入图片描述

功能:自定义配置包的扫描路径,将该包下的添加了自定义注解的对象添加到容器中

目标:将register包下添加了@ComponentRegister注解的对象注入容器中

  1. 自定义注解

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ComponentRegister {
    }
    
  2. 创建SimpleComponentRegister类实现ImportBeanDefinitionRegistrar接口,使用ClassPathBeanDefinitionScanner对象,配置包扫描的自定义规则,扫描规则具体可以参考ComponentScanAnnotationParser类的parse()方法

    public class SimpleComponentRegister implements ImportBeanDefinitionRegistrar {
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            //包路径扫描,将符合的bean注入容器中,参数2为是否使用默认的过滤器
            //默认过滤器扫描的@Component、@Controller、@Service、@Repository注解
            ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, false);
            //添加自定的扫描过滤器,返回true就添加该组件,否则不添加
            scanner.addIncludeFilter(new TypeFilter() {
                @Override
                public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
                    //判断扫描包下的类上是否标注了自定义的注解
                    return metadataReader.getAnnotationMetadata().hasAnnotation("com.importSelector.ComponentRegister");
                }
            });
            //配置自定扫描的路径,之后进行扫描,如果就指定包路径没有指定过滤器的话默认将该包下的所有对象注入容器中
            scanner.scan("com.register");
        }
    }
    
  3. 在Cat类上标注自定义的注解

    @ComponentRegister
    public class Cat {
    }
    
  4. 将配置类注入容器中

    @SpringBootApplication
    @Import(SimpleComponentRegister.class)
    public class SpringbotSourceApplication {
    
        public static void main(String[] args) {
            ConfigurableApplicationContext context = SpringApplication.run(SpringbotSourceApplication.class, args);
        }
    }
    
  5. debug测试Cat对象注入成功
    在这里插入图片描述

三、FactoryBean的基本使用

3.1 使用FactoryBean注入普通对象

功能:对于一些复杂Bean对象的创建可以使用FactoryBean来创建,如动态代理对象的创建,mybatis中mapper接口的代理对象就是使用FactoryBean对象创建的。

基本包路径

在这里插入图片描述

目标:将factoryBean包下的Dog对象注入容器中

  1. 创建FactoryBean接口的实现类对象SimpleFactoryBean

    public class SimpleFactoryBean implements FactoryBean<Dog> {
    
        //返回要注入容器的对象
        @Override
        public Dog getObject() throws Exception {
            Dog dog = new Dog("旺财", 3, "公");
            return dog;
        }
    
        //返回注入容器的类型
        @Override
        public Class<?> getObjectType() {
            return Dog.class;
        }
    }
    
  2. 将SimpleFactoryBean对象注入容器

    @SpringBootApplication
    public class SpringbotSourceApplication {
    
        public static void main(String[] args) {
            ConfigurableApplicationContext context = SpringApplication.run(SpringbotSourceApplication.class, args);
        }
    
        @Bean
        public SimpleFactoryBean simpleFactoryBean(){
            return new SimpleFactoryBean();
        }
    }
    
  3. debug测试,Dog对象注入成功
    在这里插入图片描述

3.2 使用FactoryBean注入代理对象

功能:这里使用FactoryBean注入接口的代理对象,使用JDK动态代理的方式生成接口的代理对象,并将其注入到容器中

包结构
在这里插入图片描述

目标:使用反射、动态代理和FactoryBean对象生成SimpleUserMapper接口的代理对象并将代理对象注入容器中,并调接口中定义的方法。

  1. 定义接口对象

    public interface SimpleUserMapper {
        void select();
    }
    
  2. 定义SimpleFactoryBean对象,在重写方法中使用JDK动态代理来创建mapper的代理对象,并返回

    public class MapperFactoryBean implements FactoryBean {
    
        private String className;
    
        //使用构造方法传入要反射的对象
        public MapperFactoryBean(String className) {
            this.className = className;
        }
    
        @Override
        public Object getObject() throws Exception {
            //反射获取对象
            Class<?> interfaceClass = Class.forName(className);
            //创建传入的对象的代理对象,将该代理对象返回
            Object proxyInstance = Proxy.newProxyInstance(MapperFactoryBean.class.getClassLoader(), new Class[]{interfaceClass}, new InvocationHandler() {
                @Override
                public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                    //判断select方法是否执行
                    if ("select".equals(method.getName())){
                        System.out.println(className + "." + method.getName() + "() 方法执行");
                    }
                    return null;
                }
            });
            return proxyInstance;
        }
    
        @Override
        public Class<?> getObjectType() {
            try {
                //返回该对象的类型
                Class<?> aClass = Class.forName(className);
                return aClass;
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    
  3. 将MapperFactoryBean注入容器

    @SpringBootApplication
    public class SpringbotSourceApplication {
    
        public static void main(String[] args) {
            ConfigurableApplicationContext context = SpringApplication.run(SpringbotSourceApplication.class, args);
        }
    
        @Bean
        public MapperFactoryBean mapperFactoryBean(){
            //注入要创建代理对象的类路径
            return new MapperFactoryBean("com.factoryBean.SimpleUserMapper");
        }
    }
    
  4. debug测试
    在这里插入图片描述

四、ImportBeanDefinitionRegistrar结合FactoryBean使用注入接口的代理对象

功能:使用自定义的注解,在mapper接口上面添加该注解,容器中就会自动注入该接口的代理对象

包结构

在这里插入图片描述

目标:将factoryBean中的OrderMapper和UserMapper接口通过添加自定义注解,注入其代理对象到容器中

  1. 自定义注解

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyMapper {
    }
    
  2. 定义OrderMapper和UserMapper接口并添加自定义的注解

    @MyMapper
    public interface OrderMapper {
        void select();
    }
    
    //---------------------------------------------------
    
    @MyMapper
    public interface UserMapper {
        void select();
    }
    
  3. 定义FactoryBean的实现类,实现对象的动态代理类的生成

    public class MapperFactoryBean implements FactoryBean {
    
        //传入类型的类路径
        private String className;
    
        public MapperFactoryBean(String className) {
            this.className = className;
        }
    
        @Override
        public Object getObject() throws Exception {
            Class<?> interfaceClass = Class.forName(className);
            //创建传入的对象的代理对象,将该代理对象返回
            Object proxyInstance = Proxy.newProxyInstance(MapperFactoryBean.class.getClassLoader(), new Class[]{interfaceClass}, new InvocationHandler() {
                @Override
                public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                    if ("select".equals(method.getName())){
                        System.out.println(className + "." + method.getName() + "() 方法执行");
                    }
                    return null;
                }
            });
            return proxyInstance;
        }
    
        @Override
        public Class<?> getObjectType() {
            try {
                //返回该对象的类型
                Class<?> aClass = Class.forName(className);
                return aClass;
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    
  4. 创建ImportBeanDefinitionRegistrar的实现类发现,方法中使用ClassPathBeanDefinitionScanner创建BeanDefinition时,并不会创建接口的BeanDefinition,以致接口对象注入不到容器中,debug方法ImportBeanDefinitionRegistrar的isCandidateComponent(AnnotatedBeanDefinition beanDefinition)方法返回false,使得接口类注入不了容器,解决方法定义ImportBeanDefinitionRegistrar的实现类重写该方法,方法中在使用该实现类来实现BeanDefinition的创建

    public class MyBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
        public MyBeanDefinitionScanner(BeanDefinitionRegistry registry) {
            super(registry);
        }
    
        public MyBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
            super(registry, useDefaultFilters);
        }
    
        @Override
        protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
            //重写该方法,判断创建的BeanDefinition为接口是也可以注入到容器中
            AnnotationMetadata metadata = beanDefinition.getMetadata();
            return metadata.isInterface();
        }
    }
    
  5. 定义ImportBeanDefinitionRegistrar的实现类自定义创建BeanDefinition对象,运行发现控制台报错,接口对象注入容器错误,debug发现findCandidateComponents()方法可以返回过滤后的所有BeanDefinition集合,于是使用该方法获得所有可用的集合,遍历集合,然后使用BeanDefinitionBuilder创建MapperFactoryBean的BeanDefinition,并为其传入构造参数为当前遍历的BeanDefinition的类路径,为遍历的接口生成代理对象,之后将创建的BeanDefinition加入到registry中,将生成的代理对象注入容器中

    public class MyMapperRegister implements ImportBeanDefinitionRegistrar {
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            //ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry,false);
            // isCandidateComponent(AnnotatedBeanDefinition beanDefinition)
            // (metadata.isIndependent() && (metadata.isConcrete() ||
            // (metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
            /**
             * 创建类路径扫描器,debug发现默认的ClassPathBeanDefinitionScanner 在创建beanDefinition时,并不会将接口,private的类加载,致使,无法注入接口类组件
             * 解决方法,定义该类的的子类重写判断的方法
             */
            MyBeanDefinitionScanner scanner = new MyBeanDefinitionScanner(registry,false);
    
            //添加过滤器仅注入带自定义注解的类
            scanner.addIncludeFilter(new TypeFilter() {
                @Override
                public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
                    return metadataReader.getAnnotationMetadata().hasAnnotation("com.factoryBean.MyMapper");
                }
            });
            //扫描指定包下的可配置的BeanDefinitions
            Set<BeanDefinition> beanDefinitions = scanner.findCandidateComponents("com.factoryBean");
            //遍历后将userMapper接口的BeanDefinition类,使用自定义的FactoryBean类来转换生成该接口的代理对象,之后再注入到registry中,最后将该接口的代理对象注入容器中
            for (BeanDefinition definition : beanDefinitions) {
                AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class)
                        .addConstructorArgValue(definition.getBeanClassName()).getBeanDefinition();
                registry.registerBeanDefinition(definition.getBeanClassName(),beanDefinition);
            }
        }
    }
    
  6. 将该类注入容器中

    @SpringBootApplication
    @Import(MyMapperRegister.class)
    public class SpringbotSourceApplication {
    
        public static void main(String[] args) {
            ConfigurableApplicationContext context = SpringApplication.run(SpringbotSourceApplication.class, args);
        }
    }
    
  7. debug测试,UserMapp和OrderMapper代理对象注入容器成功
    在这里插入图片描述
    在这里插入图片描述

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐