Spring 注解之注册组件

1.spring组件与注册的概念

理解这两个概念的前提是得了解spring的控制反转依赖注入IOC容器这三个概念。
- spring的组件其实就是ioc容器管理的每个bean对象
- 组件注册其实就是告诉spring这个bean 是否要给ioc容器托管。

2.spring中常用的五中组件注册方法

2.1 使用@Bean注解

这种方式是见得最多的一种方式,该注解作用在方法注解定义上。但这限于我们自定义的bean组件
这里写图片描述
如下:

public class MyConfing {
    /*
     * 给ioc容器中注入一个Blue的组件,
     * 如果不设置value的话默认是采用方法名 blue为组件名
     */
    @Bean(initMethod = "init",destroyMethod = "destroy")
    public Blue blue(){
        return new Blue();
    }
}

2.2 使用@ComponentScan注解

这个注解是一个组件扫描注解,通过扫描某些包下的组件批量的注册。该注解有几个比较重要的属性

  • basePackages/value 表示扫描的路径是一个String[]
  • includeFilters 包含过滤器Filter[] ,指定扫描符合条件的组件
  • excludeFilters 排除过滤器Filter[] ,不扫描不符合条件的组件
  • useDefaultFilters 默认的扫描策略默认为true,如果想要用自定义的策略 该值要设为false
    其中Filter[] 的Filter是ComponentScan内部的注解
@interface Filter {

        /**
         * The type of filter to use.
         * <p>Default is {@link FilterType#ANNOTATION}.
         * @see #classes
         * @see #pattern
         */
        FilterType type() default FilterType.ANNOTATION;

        /**
         * Alias for {@link #classes}.
         * @see #classes
         */
        @AliasFor("classes")
        Class<?>[] value() default {};

        /**
         * The class or classes to use as the filter.
         * <p>The following table explains how the classes will be interpreted
         * based on the configured value of the {@link #type} attribute.
         * <table border="1">
         * <tr><th>{@code FilterType}</th><th>Class Interpreted As</th></tr>
         * <tr><td>{@link FilterType#ANNOTATION ANNOTATION}</td>
         * <td>the annotation itself</td></tr>
         * <tr><td>{@link FilterType#ASSIGNABLE_TYPE ASSIGNABLE_TYPE}</td>
         * <td>the type that detected components should be assignable to</td></tr>
         * <tr><td>{@link FilterType#CUSTOM CUSTOM}</td>
         * <td>an implementation of {@link TypeFilter}</td></tr>
         * </table>
         * <p>When multiple classes are specified, <em>OR</em> logic is applied
         * &mdash; for example, "include types annotated with {@code @Foo} OR {@code @Bar}".
         * <p>Custom {@link TypeFilter TypeFilters} may optionally implement any of the
         * following {@link org.springframework.beans.factory.Aware Aware} interfaces, and
         * their respective methods will be called prior to {@link TypeFilter#match match}:
         * <ul>
         * <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
         * <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
         * <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
         * <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}
         * </ul>
         * <p>Specifying zero classes is permitted but will have no effect on component
         * scanning.
         * @since 4.2
         * @see #value
         * @see #type
         */
        @AliasFor("value")
        Class<?>[] classes() default {};

        /**
         * The pattern (or patterns) to use for the filter, as an alternative
         * to specifying a Class {@link #value}.
         * <p>If {@link #type} is set to {@link FilterType#ASPECTJ ASPECTJ},
         * this is an AspectJ type pattern expression. If {@link #type} is
         * set to {@link FilterType#REGEX REGEX}, this is a regex pattern
         * for the fully-qualified class names to match.
         * @see #type
         * @see #classes
         */
        String[] pattern() default {};

    }

从源码中可以看出FilterType是一个enum,其值可以为以下几种,如要是Custom 的话 value里面的Class 要是TypeFilter的实现类。

public enum FilterType {

    /**
     * Filter candidates marked with a given annotation.
     * @see org.springframework.core.type.filter.AnnotationTypeFilter
     */
    ANNOTATION,

    /**
     * Filter candidates assignable to a given type.
     * @see org.springframework.core.type.filter.AssignableTypeFilter
     */
    ASSIGNABLE_TYPE,

    /**
     * Filter candidates matching a given AspectJ type pattern expression.
     * @see org.springframework.core.type.filter.AspectJTypeFilter
     */
    ASPECTJ,

    /**
     * Filter candidates matching a given regex pattern.
     * @see org.springframework.core.type.filter.RegexPatternTypeFilter
     */
    REGEX,

    /** Filter candidates using a given custom
     * {@link org.springframework.core.type.filter.TypeFilter} implementation.
     */
    CUSTOM

}

我自定义一个扫描组件过滤器如下

@Component
public class MyFilter implements TypeFilter {
    /**
     *
     * @param metadataReader 当前被扫描的类的相关信息
     * @param metadataReaderFactory  工厂里其他类的信息
     * @return
     * @throws IOException
     */
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        //获取当前类的类型信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();

        //如果当前类的类名含Red就不扫描
        if (classMetadata.getClassName().contains("Red")) {
            return true;
        }

        return false;
    }
}

在代码中使用@ComponentScan。该注解的定义上有@Repeatable(ComponentScans.class)表示如果想多个扫描器的话可以在一个类上重复使用@ComponentScan或者是 @ComponenetScans({@ComponentScan})

@Configuration
@Import({Red.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
@ComponentScans({@ComponentScan(value = {"com.javxuan.boot"},
includeFilters = {@ComponentScan.Filter(type=FilterType.ANNOTATION,value = {Controller.class})},
excludeFilters = {@ComponentScan.Filter(type =FilterType.CUSTOM,value = {MyFilter.class})},
useDefaultFilters = false)})
public class MyConfing {

2.3 使用@Import注解

@Import主要是用来引用第三方组件,当然也可以导入自定义的组件。这个注解分别可以导入以下注解

  • 引入普通的组件
package com.javxuan.boot.entity;

/**
 * @Description
 * @Author xiaoqx <[email protected]>
 * @Version V1.0.0
 * @Since 1.0
 * @Date 2018/5/20
 */
public class Red {
}

使用@Import注解

@Configuration
@Import({Red.class)//引入普通组件
public class MyConfing {
}


  • 引入ImportSelect的实现类

Interface to be implemented by types that determine which @{@link Configuration} class(es) should be imported based on a given selection criteria, usually one or more
annotation attributes.
ImportSelect这个接口的实现类一般是用来引入配置文件的。

public interface ImportSelector {

    /**
     * Select and return the names of which class(es) should be imported based on
     * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
     */
    String[] selectImports(AnnotationMetadata importingClassMetadata);

}

自定义实现类

public class MyImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"com.javxuan.boot.entity.Yellow"};
    }
}

使用@Import来引入

@Configuration
@Import({MyImportSelector.class})
public class MyConfing {
}
  • 引入ImportBeandeinitionRegistrart
/*
 * Copyright 2002-2013 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.context.annotation;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.core.type.AnnotationMetadata;

/**
 * Interface to be implemented by types that register additional bean definitions when
 * processing @{@link Configuration} classes. Useful when operating at the bean definition
 * level (as opposed to {@code @Bean} method/instance level) is desired or necessary.
 *
 * <p>Along with {@code @Configuration} and {@link ImportSelector}, classes of this type
 * may be provided to the @{@link Import} annotation (or may also be returned from an
 * {@code ImportSelector}).
 *
 * <p>An {@link ImportBeanDefinitionRegistrar} may implement any of the following
 * {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective
 * methods will be called prior to {@link #registerBeanDefinitions}:
 * <ul>
 * <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
 * <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
 * <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
 * <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}
 * </ul>
 *
 * <p>See implementations and associated unit tests for usage examples.
 *
 * @author Chris Beams
 * @since 3.1
 * @see Import
 * @see ImportSelector
 * @see Configuration
 */
public interface ImportBeanDefinitionRegistrar {

    /**
     * Register bean definitions as necessary based on the given annotation metadata of
     * the importing {@code @Configuration} class.
     * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
     * registered here, due to lifecycle constraints related to {@code @Configuration}
     * class processing.
     * @param importingClassMetadata annotation metadata of the importing class
     * @param registry current bean definition registry
     */
    public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);

}

自定义ImportBeanDefinitionRegistrar

@Component
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Yellow.class);
        beanDefinitionRegistry.registerBeanDefinition("yellow",rootBeanDefinition);
    }
}

使用注解引入

@Configuration
@Import({MyImportBeanDefinitionRegistrar.class})
public class MyConfing {
}

2.4 使用@Conditional注解

这个是条件注解,只有满足一定的条件才注入组件。这个条件要实现Condition接口,这是一个功能注解接口,然后重写match方法,符合条件就注入。

/**
 * A single {@code condition} that must be {@linkplain #matches matched} in order
 * for a component to be registered.
 *
 * <p>Conditions are checked immediately before the bean-definition is due to be
 * registered and are free to veto registration based on any criteria that can
 * be determined at that point.
 *
 * <p>Conditions must follow the same restrictions as {@link BeanFactoryPostProcessor}
 * and take care to never interact with bean instances. For more fine-grained control
 * of conditions that interact with {@code @Configuration} beans consider the
 * {@link ConfigurationCondition} interface.
 *
 * @author Phillip Webb
 * @since 4.0
 * @see ConfigurationCondition
 * @see Conditional
 * @see ConditionContext
 */
@FunctionalInterface
public interface Condition {

    /**
     * Determine if the condition matches.
     * @param context the condition context
     * @param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
     * or {@link org.springframework.core.type.MethodMetadata method} being checked
     * @return {@code true} if the condition matches and the component can be registered,
     * or {@code false} to veto the annotated component's registration
     */
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);

}

自定义我的Condition

package com.javxuan.boot.registry;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotatedTypeMetadata;

/**
 * @Description
 * @Author xiaoqx <[email protected]>
 * @Version V1.0.0
 * @Since 1.0
 * @Date 2018/5/21
 */
public class MyCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        //获得项目相关环境
        Environment environment = conditionContext.getEnvironment();
        //获取bean的定义注册中心
        BeanDefinitionRegistry registry = conditionContext.getRegistry();
        //获取resource
        ResourceLoader resourceLoader = conditionContext.getResourceLoader();

        //如果容器中没有Boot bean 就注册否则不注册
        if(registry.containsBeanDefinition("boot")){
            return true;
        }
        return false;
    }
}

在方法或者类上使用@Conditional注解,如果是在类上标注说明这个注解是全局的,在方法上就是局部的

@Conditional({MyCondition.class}) //可以在类与方法上进行注解
@Bean
public Boot boot(){
        return new Boot();
}

2.5 使用FactroyBean

使用FactoryBean接口的实现类来给容器中注入组件,这一方法通常是springboot整合第三方组件用的,如Mybatis。
该接口里面的getObject()方法可以返回该工厂生产的Bean

T 代表该工厂要生产的bean
public interface FactoryBean<T> {
@Nullable
T getObject() throws Exception;

@Nullable
Class<?> getObjectType();

//设置该bean 是单例还是多实例
default boolean isSingleton() {
    return true;
}
}

自定义一个WhiteFactoryBean

public class WhiteFactoryBean implements FactoryBean<White> {
    @Override
    public White getObject() throws Exception {
        return new White();
    }

    @Override
    public Class<?> getObjectType() {
        return White.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

在配置类中使用FactoryBean来注册

  /**
     * 该方法一眼看过去是在ioc容器中注入WhiteFactoryBean 的bean
     * 其实真正注册的是White 的bean,我可以通过 getBean("bean的id")
     * 在有些情况下我们就是有获取WhiteFactoryBean的实例 那么我们通过getBean("&bean的id")
     * 
     * @return
     */
  @Bean
  public WhiteFactoryBean whiteFactoryBean(){
        return new WhiteFactoryBean();
  }

如下我们用测试类分别获取

@Test
    public void  test(){
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(MyConfing.class);
        //获取White的bean
        Object whiteFactoryBean = annotationConfigApplicationContext.getBean("whiteFactoryBean");
        System.out.println(whiteFactoryBean.getClass());
        //获取WhiteFactoryBean的bean
        Object bean = annotationConfigApplicationContext.getBean("&whiteFactoryBean");
        System.out.println(bean.getClass());

    }

结果
这里写图片描述

整合mybatis

 @Bean
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        //利用sqlSessionFacotyBean来的SqlSessionFactory
        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();

        factory.setDataSource(dataSource);
        factory.setVfs(SpringBootVFS.class);
        if(StringUtils.hasText(this.properties.getConfigLocation())) {
            factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
        }

        factory.setConfiguration(this.properties.getConfiguration());
        if(!ObjectUtils.isEmpty(this.interceptors)) {
            factory.setPlugins(this.interceptors);
        }

        if(this.databaseIdProvider != null) {
            factory.setDatabaseIdProvider(this.databaseIdProvider);
        }

        if(StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
            factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
        }

        if(StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
            factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
        }

        if(!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
            factory.setMapperLocations(this.properties.resolveMapperLocations());
        }

        return factory.getObject();
    }

3.测试一下5中注册注解的效果

package com.javxuan.boot.configuration;

import com.javxuan.boot.entity.Blue;
import com.javxuan.boot.entity.Boot;
import com.javxuan.boot.entity.Red;
import com.javxuan.boot.registry.*;
import org.springframework.context.annotation.*;
import org.springframework.stereotype.Controller;

/**
 * @Description
 * @Author xiaoqx <[email protected]>
 * @Version V1.0.0
 * @Since 1.0
 * @Date 2018/5/20
 */
@Configuration //代表该类是一个配置类
@Import({Red.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
@ComponentScans({@ComponentScan(value = {"com.javxuan.boot"},
includeFilters = {@ComponentScan.Filter(type=FilterType.ANNOTATION,value = {Controller.class})},
excludeFilters = {@ComponentScan.Filter(type =FilterType.CUSTOM,value = {MyFilter.class})},
useDefaultFilters = false)})
public class MyConfing {

    @Bean(initMethod = "init",destroyMethod = "destroy")
    public Blue blue(){
        return new Blue();
    }

    @Conditional({MyCondition.class}) //可以在类与方法上进行注解
    @Bean
    public Boot boot(){
        return new Boot();
    }

    /**
     * 该方法一眼看过去是在ioc容器中注入WhiteFactoryBean 的bean
     * 其实真正注册的是White 的bean,我可以通过 getBean("bean的id")
     * 在有些情况下我们就是有获取WhiteFactoryBean的实例 那么我们通过getBean("&bean的id")
     *
     * @return
     */
    @Bean
    public WhiteFactoryBean whiteFactoryBean(){
        return new WhiteFactoryBean();
    }

}

猜你喜欢

转载自blog.csdn.net/u014297148/article/details/80399471