Spring-注解上下文和注解

一,注解上下文

Spring3.0之后,提供了使用AnnotationConfigApplicationContext来实现基于Java配置类加载Spring应用上下文的功能。这种方式可以避免使用application.xml进行配置,相比XML配置这种方式也更加便捷。

这种ApplicationContext实现不仅能够接受@Configuration类作为输入,而且还可以接受@Component注解类和带有JSR-330元数据注解的类。

public class TestAnnotation {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);
        DataSource dataSource = (DataSource) ctx.getBean("dataSource");
        try {
            System.out.println(dataSource.getConnection() == null);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

AnnotationConfigApplicationContext不仅限于与@Configuration注解一起使用。任何包含@Component的注解类或带JSR-330注解的类都可以作为输入提供给构造函数。

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class, MyServiceImpl.class, MyDaoImpl.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.todo();
}

AnnotationBeanDefinition

该接口继承自BeanDefinition接口

public interface AnnotatedBeanDefinition extends BeanDefinition {

	// 获取注解元数据
	AnnotationMetadata getMetadata();

	// 获取此Bean定义的工厂方法的元数据
    // 如果在配置类中使用了@Bean注解,被@Bean标记的方法就会被解析为FactoryMethodMetadata
	MethodMetadata getFactoryMethodMetadata();
}

AnnotatedBeanDefinition有三个实现类:

  1. AnnotatedGenericBeanDefinition

    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        ctx.register(JavaConfig.class);
    }
    

    如上代码块中JavaConfig,和使用@Import注解导入的类,都会被解析为这种类型。

  2. ScannedGenericBeanDefinition

    通过注解扫描类,如@Service@Compnent等配置的方式,都会被解析为ScannedGenericBeanDefinition。

  3. ConfigurationClassBeanDefinition

    通过使用@Bean注解配置的方式,都会被解析为ConfigurationClassBeanDefinition。

    注解上下文源码解析

    org.springframework.context.annotation.AnnotationConfigApplicationContext

    // 注册带注解的Bean(编程方式注册,仅适用于显式注册的类)
    private final AnnotatedBeanDefinitionReader reader;
    
    // 扫描方式注册带注解的Bean(Bean定义扫描器,检测指定路径中的Bean候选者)
    private final ClassPathBeanDefinitionScanner scanner;
    
    // AnnotationConfigApplicationContext默认构造函数
    public AnnotationConfigApplicationContext() {
        this.reader = new AnnotatedBeanDefinitionReader(this);
        this.scanner = new ClassPathBeanDefinitionScanner(this);
    }
    
    // 使用BeanFactory创建一个新的AnnotationConfigApplicationContext
    public AnnotationConfigApplicationContext(DefaultListableBeanFactory beanFactory) {
        super(beanFactory);
        this.reader = new AnnotatedBeanDefinitionReader(this);
        this.scanner = new ClassPathBeanDefinitionScanner(this);
    }
    
    // 使用给定的带注解的类创建AnnotationConfigApplicationContext,并刷新上下文
    public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
        this();
        register(annotatedClasses);
        // 刷新上下文
        refresh();
    }
    
    // 创建AnnotationConfigApplicationContext,扫描指定包路径中的Bean定义
    public AnnotationConfigApplicationContext(String... basePackages) {
        this();
        scan(basePackages);
        // 刷新上下文
        refresh();
    }
    

    org.springframework.context.annotation.AnnotatedBeanDefinitionReader

    public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
        // BeanDefinition定义,增加了注解元数据的支持
        AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
        // 检测是否需要跳过@Conditional注解
        if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
            return;
        }
    
        // 解析对应的Scope
        ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
        // 设置abd的Scope
        abd.setScope(scopeMetadata.getScopeName());
        // 使用BeanNameGenerator生成对应的beanName
        String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
        // 对常用注解进行处理
        AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
        // 除了Bean类级别的限定符外,还需要处理特定限定符注解
        if (qualifiers != null) {
            for (Class<? extends Annotation> qualifier : qualifiers) {
                if (Primary.class == qualifier) {
                    abd.setPrimary(true);
                }
                else if (Lazy.class == qualifier) {
                    abd.setLazyInit(true);
                }
                else {
                    abd.addQualifier(new AutowireCandidateQualifier(qualifier));
                }
            }
        }
    
        // 定义BeanDefinition和beanName的映射关系类
        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
        // 为组件设置Scope代理(如果有的话)
        definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
        // 注册BeanDefinition到容器并且注册别名
        BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
    }
    

    二,常用注解

    容器配置

    Spring配置中的两个主要注解是@Configuration和@Bean。

    @Bean

    该注解主要用于表示由Spring IoC容器管理对象的实例化,配置和初始化。对于熟悉Spring XML配置的人来说,@ Bean注解与Spring XML配置中的<bean/>元素具有相同作用。

    配套注解

    @Lazy

    表明一个Bean 是否延迟加载。

    @Primary

    多个候选者都有资格自动装配依赖项时,应当优先考虑的Bean。

    @DependsOn

    指当前Bean所依赖的Bean,可以控制Bean的加载顺序。

    @Role

    Spring使用Role来标识Bean的分类。目前有三种Role:ROLE_APPLICATION、ROLE_INFRASTRUCTURE、ROLE_SUPPORT。

    @Description

    BeanDefinition的文本说明

    @Configuration

    该注解表示该类的主要目的是作为Bean定义的来源(即:表明这就是一个配置类)。此外,@Configuration类允许通过简单地调用同一类中的其他@Bean注解来定义Bean间的依赖关系。

    @Configuration
    public class JavaConfig {
        @Bean
        public static TestBean testBean(){
            System.out.println("注解.......");
            return new A();
        }
    }

    上面的JavaConfig类等同于在Spring XML配置文件中如下定义:

    <beans>
        <bean id="testBean" class="xxx.xxx.TestBean"/>
    </beans>

    @ComponentScan

    根据指定的配置自动扫描组件,通常与@Configuration注解一起使用。该注解与Spring XML配置文件中的<context:component-scan>元素作用相同。

    注解参数

    默认值

    说明

    basePackageClasses

    {}

    basePackages()的安全替代方案,用于扫描带注解组件的包名。包名中每一个类都会被扫描

    basePackages

    {}

    扫描带注解的组件的包名

    excludeFilters

    Filter[]

    扫描时不需要包含哪些组件

    includeFilters

    Filter[]

    扫描时需要包含哪些组件

    lazyInit

    false

    被扫描到的Bean是否应注册为延迟初始化

    nameGenerator

    BeanNameGenerator

    BeanNameGenerator接口用于对Spring容器扫描到的组件进行命名

    resourcePattern

    **/*.class

    组件扫描的匹配模式

    scopedProxy

    ScopedProxyMode.DEFAULT

    是否为扫描到的组件生成代理

    scopeResolver

    AnnotationScopeMetadataResolver

    ScopeMetadataResolver用于处理扫描到的组件的Scope

    useDefaultFilters

    true

    是否需要使用Spring默认的扫描规则,即自动检测带@Component @ Repository@ Service或@Controller的注解类

    value

    {}

    basePackages()的别名

    过滤类型

    过滤类型

    使用示例

    说明

    annotation

    org.example.SomeAnnotation

    默认方式。在目标组件的类型级别上存在的注释。

    assignable

    org.example.SomeClass

    组件实现/继承(extends/implements)的类

    aspectj

    org.example..*Service+

    组件要匹配的AspectJ类型表达式

    regex

    org\.example\.Default.*

    与正则表达式匹配的目标组件

    custom

    org.example.MyTypeFilter

    自定义实现了org.springframework.core.type .TypeFilter 的过滤类。

    参数使用示例

    @ComponentScan(basePackageClasses = Person.class)
    
    // 扫描xxx.xxx包下的组件
    @ComponentScan(value = "xxx.xxx")
    
    // 扫描包含有@Component注解的组件
    @ComponentScan(value = "xxx.xxx", includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Component.class))
    
    // 扫描ai.yunxi包,包含与“.*Stub.*Repository”表达式匹配的组件,排除包含@Repository注解的组件
    @ComponentScan(basePackages = "xxx.xxx",
            includeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
            excludeFilters = @ComponentScan.Filter(Repository.class))
    
    // 扫描包含extends或implements Test的组件
    @ComponentScan(value = "xxx.xxx", includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = Test.class), useDefaultFilters = false)
    
    // 扫描时排除有@Component注解的组件
    @ComponentScan(value = "xxx.xxx", excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Component.class))
    
    // 自定义过滤接口
    @ComponentScan(value = "xxx.xxx", includeFilters = @ComponentScan.Filter(type = FilterType.CUSTOM, value = MyComponentFilter.class), useDefaultFilters = true)
    

    @Conditional

    该注解用来表示仅当指定条件匹配时,Bean才能够注册。如果@Configuration注解用@Conditional进行标记,则与该类关联的所有@Bean,@ Import和@ComponentScan注解将受条件限制。总的来说,就是根据特定条件来控制Bean的创建行为,这样我们可以利用这个特性进行一些自动的配置。

    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Conditional {
        /**
         * All {@link Condition Conditions} that must {@linkplain Condition#matches match}
         * in order for the component to be registered.
         */
        Class<? extends Condition>[] value();
    }

    使用示例

    Bean类

    public interface Vehicle {
        public void run();
    }
    
    public class Car implements Vehicle {
        @Override
        public void run() {
            System.out.println("car run");
        }
    }
    
    public class Motor implements Vehicle {
        @Override
        public void run() {
            System.out.println("motor run");
        }
    }

    Condition接口实现类

    public class CarCondition implements Condition {
        @Override
        public boolean matches(ConditionContext ctx, AnnotatedTypeMetadata metadata) {
            return ctx.getEnvironment().containsProperty("car");
        }
    }
    
    public class MotorCondition implements Condition {
        @Override
        public boolean matches(ConditionContext ctx, AnnotatedTypeMetadata metadata) {
            return ctx.getEnvironment().containsProperty("motor");
        }
    }
    

    测试类

    // Spring配置类
    @Configuration
    public class JavaConfig {
    
        @Bean
        @Conditional(MotorCondition.class)
        public Vehicle motor() {
            return new Motor();
        }
    
        @Bean
        @Conditional(CarCondition.class)
        public Vehicle car() {
            return new Car();
        }
    }
    
    // 主要测试类
    public class TestCondition {
        public static void main(String[] args) {
            System.setProperty("motor", "");
            AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);
            Vehicle vehicle = ctx.getBean(Vehicle.class);
            vehicle.run();
        }
    }
    

    扩展注解

    注解

    说明

    @ConditionalOnBean

    当容器里有指定Bean的条件下实例化

    @ConditionalOnClass

    当类路径下有指定的类的条件下实例化

    @ConditionalOnExpression

    基于SpEL表达式作为判断条件

    @ConditionalOnJava

    基于JVM版本作为判断条件

    @ConditionalOnJndi

    在JNDI存在的条件下查找指定的位置

    @ConditionalOnMissingBean

    当容器里没有指定Bean的情况下实例化

    @ConditionalOnMissingClass

    当类路径下没有指定的类的情况下实例化

    @ConditionalOnNotWebApplication

    当前项目不是web项目的条件下实例化

    @ConditionalOnResource

    类路径有指定资源的条件下实例化

    @ConditionalOnSingleCandidate

    当指定Bean在容器中只有一个,或者虽然有多个但是指定首选的Bean

    @ConditionalOnWebApplication

    当前项目是Web项目的情况下的条件下

    @ConditionalOnProperty

    当属性有指定的值时进行实例化

    @Import

    与Spring XML文件中使用<import/>元素来进行模块化配置一样,@Import注解允许从另一个配置类加载@Bean定义。

    @Configuration
    public class ConfigA {
        @Bean
        public A a() {
            return new A();
        }
    }
    
    @Configuration
    @Import(ConfigA.class)
    public class ConfigB {
        @Bean
        public B b() {
            return new B();
        }
    }
    

    @ImportResource

    在Spring应用程序中@Configuration注解是配置容器的主要机制,但仍然可能需要使用一些XML,该注解就是用来加载xml配置的。

    @Configuration
    @ImportResource("classpath:/xxx/xxx/jdbc-config.xml")
    public class AppConfig {
    
        @Value("${jdbc.url}")
        private String url;
    
        @Value("${jdbc.username}")
        private String username;
    
        @Value("${jdbc.password}")
        private String password;
    
        @Bean
        public DataSource dataSource() {
            return new DriverManagerDataSource(url, username, password);
        }
    }
    

    @ImportResource配置等同于Spring XML配置中

    <context:property-placeholder location="classpath:/xxx/xxx/jdbc.properties"/>

    @Autowired

    自动装配注解。

    环境注解

    @Profile

    该注解可以根据当前环境,动态的激活和切换一系列组件的功能。通过设置Profile,只有被激活的Profile才会将其中所对应的Bean注册到Spring容器中。

    @PropertySource

    组件管理

    @Component

    @Service

    @Repository

    @Controller

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值