文档章节

Spring5.0源码深度解析之Spring核心注解

须臾之余
 须臾之余
发布于 07/28 16:59
字数 1743
阅读 20
收藏 0

Spring核心注解原理

这篇文章主要针对Spring核心注解进行分析

一:@Condition注解

Condition 是在spring4.0 增加的条件注解,通过这个可以功能可以实现选择性的注入Bean操作,接下来先学习下Condition是如何使用的,然后分析spring源码了解其中的实现原理。

实现案例:

在Spring容器加载中,如果当前环境是WIN7操作系统就装配win7实体类、其他系统就不装配。

@Configuration
public class MyConfig {
    /**
     * Import作用主要将外部的jar包注入到springioc容器中 @Import(Win7Entity.class) 等于与@Bean
     * Import注册的bean对象 id为当前类全路径
     */
    @Bean
    public Win7Entity win7Entity() {
        return new Win7Entity();
    }
public class V1Spring {
    public static void main(String[] args) {

        // 1.基于注解方式实现启动
        ApplicationContext annotationApplicationContext =
                new AnnotationConfigApplicationContext(MyConfig.class);
        Win7Entity win7Entity = (Win7Entity) annotationApplicationContext.getBean("win7Entity");
        System.out.println(win7Entity);

输出结果

com.mayikt.v1.entity.Win7Entity@5606c0b

MyCondition

public class MyCondition implements Condition {
    /**
     * @param context  获取到当前的上下文
     * @param metadata 获取当前注解的细心
     * @return
     */
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 1.获取当前的环境
        Environment environment = context.getEnvironment();
        // win7 linux win8 win10 苹果系统MAC
        String osName = environment.getProperty("os.name");
        if (osName.equals("Windows 10")) {
            // 可以注册该对象
            return true;
        }
        // 不能注册该对象
        return false;
    }
}
@Configuration
public class MyConfig {
    /**
     * Import作用主要将外部的jar包注入到springioc容器中 @Import(Win7Entity.class) 等于与@Bean
     * Import注册的bean对象 id为当前类全路径
     */
    @Bean
    @Conditional(MyCondition.class)
    public Win7Entity win7Entity() {
        return new Win7Entity();
    }

输出:

com.mayikt.v1.entity.Win7Entity@64d7f7e0

二:@Import注解

1.为什么要使用@Import注解?Import注解的主要作用的将外部的jar包注入到springioc容器中

2.@Bean注解应用场景是什么?注册加载外部jar包

@Configuration
@Import(Win7Entity.class)
public class MyConfig {
    /**
     * Import作用主要将外部的jar包注入到springioc容器中 @Import(Win7Entity.class) 等同于@Bean
     * Import注册的bean对象 id为当前类全路径
     */
   /* @Bean
    @Conditional(MyCondition.class)
    public Win7Entity win7Entity() {
        return new Win7Entity();
    }*/
public class V1Spring {
    public static void main(String[] args) {

        // 1.基于注解方式实现启动
        ApplicationContext annotationApplicationContext =
                new AnnotationConfigApplicationContext(MyConfig.class);
        Win7Entity win7Entity = (Win7Entity) annotationApplicationContext.getBean("com.mayikt.v1.entity.Win7Entity");//这里当前类Win7Entity的全路径
        System.out.println(win7Entity);

结果:

com.mayikt.v1.entity.Win7Entity@f4168b8

public class V1Spring {
    public static void main(String[] args) {

        // 1.基于注解方式实现启动
        ApplicationContext annotationApplicationContext =
                new AnnotationConfigApplicationContext(MyConfig.class);
        Win7Entity win7Entity = (Win7Entity) annotationApplicationContext.getBean("com.mayikt.v1.entity.Win7Entity");
        System.out.println(win7Entity);
        String[] beanDefinitionNames = annotationApplicationContext.getBeanDefinitionNames();
        for (int i = 0; i < beanDefinitionNames.length; i++) {
            System.out.println(beanDefinitionNames[i]);
        }

结果

com.mayikt.v1.entity.Win7Entity@f4168b8
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
myConfig
com.mayikt.v1.entity.Win7Entity

总结下:@Bean注解与@Import注解的区别?

@Bean注解注册的bean的id是以方法名称来作为beanid ,

@Import注解以当前类的完整路径地址注册 ,相比来说@Import注入类更加简单

应用场景都是引入外部jar包

三@EnableXXX注解的实现原理

配合@Configuration使用,包括 @EnableAsync, @EnableScheduling, @EnableTransactionManagement, @EnableAspectJAutoProxy, @EnableWebMvc。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({PayEntity.class,MyPayEntity.class}) //引入@Import注解
public @interface EnablePayEntity {
    // 只要启动的时候 加入该EnablePayEntity  就会将PayEntity实体类注入到spruingioc容器
    // Enable注解的话 底层 实际上在调用@Import(PayEntity.class)
}
@Configuration
@Import(Win7Entity.class)
@EnablePayEntity    //配置类加上这个注解
public class MyConfig {
    /**
     * Import作用主要将外部的jar包注入到springioc容器中 @Import(Win7Entity.class) 等于与@Bean
     * Import注册的bean对象 id为当前类全路径
     */

程序输出结果

com.mayikt.v1.entity.Win7Entity@130f889
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
myConfig
com.mayikt.v1.entity.PayEntity
com.mayikt.v1.entity.MyPayEntity
com.mayikt.v1.entity.Win7Entity

@EnableXXX注解的实现原理:底层实际上还是调用@Import注解

四:ImportSelector类

public class MyImportSelector implements ImportSelector {
    /**
     *  注解信息
     * @param importingClassMetadata
     * @return
     */
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //注入多个对象,类的完整路径
        return new String[]{"com.mayikt.v1.entity.MemberEntity","com.mayikt.v1.entity.MsEntity"};
    }
}
@Configuration
@Import({Win7Entity.class,MyImportSelector.class})
@EnablePayEntity
public class MyConfig {
    /**
     * Import作用主要将外部的jar包注入到springioc容器中 @Import(Win7Entity.class) 等于与@Bean
     * Import注册的bean对象 id为当前类全路径
     */

输出结果

com.mayikt.v1.entity.Win7Entity@16267862
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
myConfig
com.mayikt.v1.entity.PayEntity
com.mayikt.v1.entity.MyPayEntity
com.mayikt.v1.entity.Win7Entity
com.mayikt.v1.entity.MemberEntity
com.mayikt.v1.entity.MsEntity

五:ImportBeanDefinitionRegistrar

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     * AnnotationMetadata 注解的信息
     * @param importingClassMetadata
     * @param registry
     */
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // spring容器中 bean 的信息 Bean Definition描述   手动注册到ioc容器中
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(SmsEntity.class);
        registry.registerBeanDefinition("smsEntity", rootBeanDefinition);
        // 底层源码:springioc 底层通过beanDefinitionMap存放 线程是安全的
    }
    //FactoryBean (往IOC容器存储对象 注入对象) BeanFactory(从ioc工厂获取bean对象)
}

底层实现原理:

this.beanDefinitionMap.put(beanName, beanDefinition);
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap(256)

底层通过一个ConcurrentHashMap保存起来的。

@Configuration
@Import({Win7Entity.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
@EnablePayEntity
public class MyConfig {
    /**
     * Import作用主要将外部的jar包注入到springioc容器中 @Import(Win7Entity.class) 等于与@Bean
     * Import注册的bean对象 id为当前类全路径
     */

输出结果

myConfig
com.mayikt.v1.entity.PayEntity
com.mayikt.v1.entity.MyPayEntity
com.mayikt.v1.entity.Win7Entity
com.mayikt.v1.entity.MemberEntity
com.mayikt.v1.entity.MsEntity
smsEntity

六:FactoryBean

public class MyFactoryBean implements FactoryBean<MyEntity> {
    public MyEntity getObject() throws Exception {
        return new MyEntity();
    }
    public Class<?> getObjectType() {
        return MyEntity.class;
    }
    //往IOC容器中注入对象
    public boolean isSingleton() {
        // 默认的情况下就是为true true表示为单例 false 表示为多例
        return false;
    }
}
@Configuration
@Import({Win7Entity.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class,MyFactoryBean.class})
@EnablePayEntity
public class MyConfig {
    /**
     * Import作用主要将外部的jar包注入到springioc容器中 @Import(Win7Entity.class) 等于与@Bean
     * Import注册的bean对象 id为当前类全路径
     */

输出结果

myConfig
com.mayikt.v1.entity.PayEntity
com.mayikt.v1.entity.MyPayEntity
com.mayikt.v1.entity.Win7Entity
com.mayikt.v1.entity.MemberEntity
com.mayikt.v1.entity.MsEntity
com.mayikt.v2.config.MyFactoryBean
smsEntity

疑问:BeanFactory和FactoryBean区别?

BeanFactory是个Factory,也就是IOC容器或对象工厂,FactoryBean是个Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。

但对FactoryBean而言,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似

疑问:@Service与@Compent注解区别?

底层还是调用@Compont注解,主要是为了分类,更好划分场景,注意要加上扫包范围

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Mayikt {
}

Spring注释@Qualifie

在学习@Autowired的时候我们已经接触到了@Qualifier,这节就来详细学习一下自定义@Qualifier

例如定义一个交通工具类:Vehicle以及它的子类Bus和Sedan。

如果用@Autowired来找Vehicle的话,会有两个匹配的选项Bus和Sedan。为了限定选项,可以像下面这样。

@Autowired
@Qualifier("car")
private Vehicle vehicle;

如果要频繁使用@Qualifier("car")并且想让它变得更有意义,我们可以自定义一个@Qualifier。

@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Car{

}
 @Autowired
  @Car
  private Vehicle vehicle;

最后在Sedan类加上注释。

@Car
public class Sedan implements Vehicle{
}

疑问:@Primary与@Qualifier区别?

在一个接口下有两个实现类使用@Autowired获取的时候,有什么问题?

@Autowired默认情况下使用类型查找,会存在问题。SpringBoot多数据源 设置默认或者优先级。

解决方案:

@Resource按照名称查找;

@Qualifier指定实现类

@Primary指定实现类的优先级第一,默认获取

 

本文参考

蚂蚁课堂:http://www.mayikt.com/

 

© 著作权归作者所有

须臾之余
粉丝 125
博文 68
码字总数 178724
作品 0
吉安
程序员
私信 提问
Spring5对比Spring3.2源码之容器的基本实现

最近看了《Spring源码深度解析》,该书是基于Spring3.2版本的,其中关于第二章容器的基本实现部分,目前spring5的实现方式已有较大改变。 Spring3.2的实现: 容器的基础XmlBeanFactory已经被...

Ilike_Java
2018/10/17
145
0
Spring Boot 优雅的配置拦截器方式

其实spring boot拦截器的配置方式和springMVC差不多,只有一些小的改变需要注意下就ok了。下面主要介绍两种常用的拦截器: 一、基于URL实现的拦截器: 关键代码:path.matches(Const.NOINTE...

边鹏_尛爺鑫
2018/11/15
21.4K
9
spring boot框架学习2-spring boot核心(1)

本节主要: 1:解析spring boot入口和@SpringBootApplication源码详解 SpringBootApplication包含: @SpringBootConfiguration @ComponentScan @EnableAutoConfiguration 本文是《凯哥陪你学......

中凯_凯哥java
2017/10/27
423
0
spring boot框架学习3-spring boot核心(2)

本节主要: 1:解析spring boot入口和@SpringBootApplication源码详解 SpringBootApplication包含: @SpringBootConfiguration @ComponentScan @EnableAutoConfiguration 本文是《凯哥陪你学......

凯哥java
2017/10/26
112
0
仿照源码,手写一个自定义 Spring MVC 框架

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 https://blog.csdn.net/GitChat/article/details/97947618 前言 上节课我们学习了 Spring M...

GitChat技术杂谈
07/31
0
0

没有更多内容

加载失败,请刷新页面

加载更多

3_数组

3_数组

行者终成事
今天
7
0
经典系统设计面试题解析:如何设计TinyURL(二)

原文链接:https://www.educative.io/courses/grokking-the-system-design-interview/m2ygV4E81AR 编者注:本文以一道经典的系统设计面试题:《如何设计TinyURL》的参考答案和解析为例,帮助...

APEMESH
今天
7
0
使用logstash同步MySQL数据到ES

概述   在生成业务常有将MySQL数据同步到ES的需求,如果需要很高的定制化,往往需要开发同步程序用于处理数据。但没有特殊业务需求,官方提供的logstash就很有优势了。   在使用logstas...

zxiaofan666
今天
10
0
X-MSG-IM-分布式信令跟踪能力

经过一周多的鏖战, X-MSG-IM的分布式信令跟踪能力已基本具备, 特点是: 实时. 只有要RX/TX就会实时产生信令跟踪事件, 先入kafka, 再入influxdb待查. 同时提供实时sub/pub接口. 完备. 可以完整...

dev5
今天
7
0
OpenJDK之CyclicBarrier

OpenJDK8,本人看的是openJDK。以前就看过,只是经常忘记,所以记录下 图1 CyclicBarrier是Doug Lea在JDK1.5中引入的,作用就不详细描述了,主要有如下俩个方法使用: await()方法,如果当前线...

克虏伯
今天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部