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

Stella981
• 阅读 595

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

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对象) }

底层实现原理:

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

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 { 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/

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Easter79 Easter79
3年前
swap空间的增减方法
(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
Java获得今日零时零分零秒的时间(Date型)
publicDatezeroTime()throwsParseException{    DatetimenewDate();    SimpleDateFormatsimpnewSimpleDateFormat("yyyyMMdd00:00:00");    SimpleDateFormatsimp2newS
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这