美文网首页
将自定义Bean加入IOC容器

将自定义Bean加入IOC容器

作者: ElevenKing | 来源:发表于2019-08-14 15:19 被阅读0次
在特定的场景,我们需要将没有通过spring配置文件或注解的类加入到IOC容器中。这就需要我们了解spring如何将bean加载到IOC容器中,并对之功能进行扩展。
  • 自定义类 的识别方式。 自定义注解?自定义包路径?
  • ClassPathBeanDefinitionScanner 扫描器扩展
  • ScannerConfigurer 交给客户端定义的配置

\color{red}{MyServiceScannerConfigurer }

public class MyServiceScannerConfigurer implements BeanDefinitionRegistryPostProcessor,
        InitializingBean, ApplicationContextAware {

    private static final Logger LOGGER = LoggerFactory.getLogger(RemoteServiceScannerConfigurer.class);
    /** 要扫描的包 */
    private String basePackage;

    /** 排除自身项目包 */
    private String excludePackage;
    
    /** applicationContext上下文 通过实现ApplicationContextAware 获取 */
    private ApplicationContext applicationContext;
    
     /** beanName生成器 */
    private BeanNameGenerator nameGenerator;

    /** set方法指定要被扫描的包名 */
    public void setBasePackage(String basePackage) {
        this.basePackage = basePackage;
    }

    public BeanNameGenerator getNameGenerator() {
        return nameGenerator;
    }

    public void setNameGenerator(BeanNameGenerator nameGenerator) {
        this.nameGenerator = nameGenerator;
    }
  
    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(this.basePackage, "Property 'basePackage' is required");
    }
  
    
    /** 实现BeanDefinitionRegistryPostProcessor接口 此方法中实例化 ClassPathRemoteServiceScanner 扫描器  */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        LOGGER.info("BeanFactoryPostProcessor postProcessBeanDefinitionRegistry start...");
        //实例化扫描器       
        MyClassPathBeanDefinitionScanner scanner = new MyClassPathBeanDefinitionScanner(registry, excludePackage, applicationContext.getEnvironment());
        //设置要扫描的注解
        scanner.setAnnotationClass(RemoteService.class);
        //传入applicationContext
        scanner.setResourceLoader(this.applicationContext);
        //传入beaanName生成器
        scanner.setBeanNameGenerator(this.nameGenerator);
        //过滤 不需要扫描的类
        scanner.registerFilters();

        scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
    }

      @Override
      public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // left intentionally blank
      }

      @Override
      public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
      }

      public void setExcludePackage(String excludePackage) {
        this.excludePackage = excludePackage;
      }

}

\color{red}{MyClassPathBeanDefinitionScanner }

public class MyClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {

    private static final String TEST_PROFILE = "test";
    private static final Logger LOG = LoggerFactory.getLogger(ClassPathRemoteServiceScanner.class);

    private Class<? extends Annotation> annotationClass;

    private Class<?> markerInterface;

    /**
     * 排除自身项目包.
     */
    private String excludePackage;

    private RemoteServiceProxyFactoryBean<Object> exportServiceProxyFactoryBean = new RemoteServiceProxyFactoryBean<Object>();

    public ClassPathRemoteServiceScanner(BeanDefinitionRegistry registry, String excludePackage, Environment environment) {
        super(registry, true, environment);
        this.excludePackage = excludePackage;
    }

    public ClassPathRemoteServiceScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
        super(registry, useDefaultFilters);
    }

    public ClassPathRemoteServiceScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment) {
        super(registry, useDefaultFilters, environment);
    }

    public void setAnnotationClass(Class<? extends Annotation> annotationClass) {
        this.annotationClass = annotationClass;
    }

    public void setMarkerInterface(Class<?> markerInterface) {
        this.markerInterface = markerInterface;
    }

    /**
     * Configures parent scanner to search for the right interfaces. It can search
     * for all interfaces or just for those that extends a markerInterface or/and
     * those annotated with the annotationClass
     */
    public void registerFilters() {
        boolean acceptAllInterfaces = true;

        // if specified, use the given annotation and / or marker interface
        if (this.annotationClass != null) {
            addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
            acceptAllInterfaces = false;
        }

        // override AssignableTypeFilter to ignore matches on the actual marker interface
        if (this.markerInterface != null) {
            addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
                @Override
                protected boolean matchClassName(String className) {
                    return false;
                }
            });
            acceptAllInterfaces = false;
        }

        if (acceptAllInterfaces) {
            // default include filter that accepts all classes
            addIncludeFilter(new TypeFilter() {
                @Override
                public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
                    return true;
                }
            });
        }

        // exclude package-info.java
        addExcludeFilter(new TypeFilter() {
            @Override
            public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
                String className = metadataReader.getClassMetadata().getClassName();
                if (excludePackage != null && className.startsWith(excludePackage)) {
                    return true;
                }

                return className.endsWith("package-info");
            }
        });
    }

    private void registerServiceMetadata(Class<?> clazz) {
        for (Method method : clazz.getDeclaredMethods()) {
            RemoteMethod annotation = method.getAnnotation(RemoteMethod.class);
            if (annotation != null) {
                String serviceName = annotation.serviceName();
                //String serviceDesc = annotation.serviceDesc();
                String methodName = method.getName();
                RemoteType remoteType = annotation.remoteType();
                String fullMethodName = clazz.getName() + "." + methodName;

                if (!serviceName.isEmpty()) {
                    ServiceMetadata metadata = new ServiceMetadata();
                    metadata.setServiceName(serviceName);
                    metadata.setServiceMethod(method);
                    metadata.setRemoteType(remoteType);
                    metadata.setInterfaceName(fullMethodName);
                    metadata.setServiceInterface(clazz);
                    ServiceManager.getInstance().put(fullMethodName, metadata);
                }
            }
        }
    }

    @Override
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

        if (beanDefinitions.isEmpty()) {
            LOG.warn("No ExportService interface was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
        } else {
            processBeanDefinitions(beanDefinitions);
        }

        return beanDefinitions;
    }

    private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
        GenericBeanDefinition definition;
        for (BeanDefinitionHolder holder : beanDefinitions) {
            definition = (GenericBeanDefinition) holder.getBeanDefinition();

            LOG.info("Creating ExportServiceProxyFactoryBean with name '" + holder.getBeanName()
                    + "' and '" + definition.getBeanClassName() + "' mapperInterface");

            try {
                Class<?> clazz = definition.resolveBeanClass(Thread.currentThread().getContextClassLoader());
                RemoteService annotation = clazz.getAnnotation(RemoteService.class);
                if (annotation != null) {
                    // the export service interface is the original class of the bean
                    // but, the actual class of the bean is ExportServiceProxyFactoryBean
                    definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
                    definition.setBeanClass(this.exportServiceProxyFactoryBean.getClass());
                    definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
                    definition.getPropertyValues().add(TEST_PROFILE, getEnvironment().acceptsProfiles(TEST_PROFILE));
                    registerServiceMetadata(clazz);
                }
            } catch (ClassNotFoundException ignore) {
                //ignore
                LOG.error("", ignore);
            }
        }
    }


    @Override
    protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
        if (super.checkCandidate(beanName, beanDefinition)) {
            return true;
        } else {
            LOG.warn("Skipping MapperFactoryBean with name '" + beanName
                    + "' and '" + beanDefinition.getBeanClassName() + "' mapperInterface"
                    + ". Bean already defined with the same name!");
            return false;
        }
    }

    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
    }

相关文章

  • 将自定义Bean加入IOC容器

    在特定的场景,我们需要将没有通过spring配置文件或注解的类加入到IOC容器中。这就需要我们了解spring如何...

  • 阐述Spring框架中Bean的生命周期?

    ① Spring IoC容器找到关于Bean的定义并实例化该Bean。② Spring IoC容器对Bean进行依...

  • 3.Spring DI

    当Spring Ioc容器完成了bean定义的定位,载入和解析注册,Ioc容器就可以管理bean定义的相关数据了,...

  • 实例化Bean

    Spring IoC容器需要根据Bean定义里的配置元数据使用反射机制来创建Bean。在Spring IoC容器中...

  • [Spring]Bean与BeanDefinition

    IOC容器 IOC容器主要的职责有: Spring IOC 可以从配置文件或者注解中根据每个Bean的定义,将这些...

  • Spring Bean 生命周期

    Spring IoC 容器根据配置文件的定义顺序来实例化 bean Spring 根据 bean 的定义填充所有的...

  • ApplicationContext与BeanFactory

    俩者都是IOC的容器,只不过BeanFactory是作为spring IOC的基础容器,定义了最基础的获取bean...

  • spring-core

    bean: 应用里被Spring IoC容器管理的对象叫做bean.Spring IoC容器负责bean的初始化,...

  • spring bean理解

    一、Spring bean定义由Spring IoC容器所管理的对象称为bean。bean被实例化,组装,并通过S...

  • SpringIOC原理源码理解(2)

    IOC容器的依赖注入 依赖注入发生的时间当Spring IoC容器完成了Bean定义资源的定位、载入和解析注册以后...

网友评论

      本文标题:将自定义Bean加入IOC容器

      本文链接:https://www.haomeiwen.com/subject/bgwskctx.html