01 spring 常用注解 原理 逻辑 代码演示

这是自己观看视频的笔记

一、组件注册

1.1-spring注解驱动开发

image-20200511205219602

1.2-组件注册 @Configuration

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.12.RELEASE</version>
</dependency>
  1. 如果创建beans.xml没有如下内容,则为没有添加spring支持

image-20200511215300998

  1. 则开启

image-20200511215403953

image-20200511215507250

1.3-组件注册 Configuration、Bean、ComponentScan(s)、TypeFilter

  1. 创建一个Person类

image-20200511215948782

  1. 配置beans.xml

  2. 给一个id方便从容器中获取

  3. 可以通过property作为一个属性的赋值
    在这里插入图片描述

这是以前的一个配置文件

  1. 开始使用,写一个测试类

    1. 通过ClassPathXmlApplicationContext,表示类路径下的一个xml配置文件。会返回IOC容器
    2. 可通过getBean加上“id”进行获取。或是类型

    image-20200511220652858

  2. 以前配置文件的方式被替换为了配置类

    1. 建立一个config.MainConfig

    image-20200511221104414

    2.回到MainTest,通过AnnotationConfigApplicationContext注解式的config,它传入的就是这个配置类。相当于是穿配置类的位置。

image-20200511221947155

	3.	通过getBeanDefinitionNames可获得Bean容器中组件的所有名称

image-20200511222718056

  1. 也可通过getBeanNamesForType

image-20200511222718056

  1. 也可通过getBeanNamesForType

image-20200511222836873

  1. 通过上面的这个方法,我也可改变组键名称。要么改方法名,要么采用下面这种方式

image-20200511223329264

  1. 在实际开发中,包的扫描写得比较多

    1. image-20200511223832900
    2. 这是xml的写法(以前的方式)
    <!--包扫描、只要标注了@Controller@Service@Repository@Component,都会被自动扫描加入容器中-->
    <context:component-scan base-package="top.p3wj"></context:component-scan>
    

image-20200512125701035

​ 3. 写在配置类中

image-20200512125756335

​ 4. 效果演示

image-20200512130735176

​ 发现其中mainConfig也是一个组件,是因为@Configuration也是一个@Component

image-20200512131106829

​ 5. excludeFilters,过滤不扫描的内容。

image-20200512131533196

image-20200512152116248

image-20200512152146253

它是一个Filter()数组

image-20200512152438561

//excludeFilters = Filter[] 指定扫描的时候按照规则排除哪些规则
//includeFilters = Filter[] 指定扫描的时候只需要包含哪些组件
//useDefaultFilters 默认为true,加载所有组件

image-20200512152805361

​ 6.@ComponentScan

image-20200512152959793

​ 在8几以上中才可以

image-20200512153417456

如果不是,就使用@ComponentScan,指定扫描策略

image-20200512153632824

image-20200512153718572

FilterType.ASSIGNABLE_TYPE 按照给定的类型

FilterType.ASPECTJ 使用ASPECTJ表达式(不太常用)

FilterType.REGEX 使用正则表达式

image-20200512154654583

实现TypeFilter

image-20200512155505125

image-20200512155730049

top.p3wj中的每一个类都会进入进行匹配

1.4-组件注册 @Scope

image-20200512160837963

image-20200512160917381

默认单实例

image-20200512174415394

* ConfigurableBeanFactory#SCOPE_PROTOTYPE  prototype   多实例
* ConfigurableBeanFactory#SCOPE_SINGLETON  singleton   单实例(默认值)
* org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST  request 同一次请求创建一个实例
* org.springframework.web.context.WebApplicationContext#SCOPE_SESSION  session 同一个session创建一个实例

改为多实例后

image-20200512174717502

这其实就相当于在xml文件中,Bean里加上scope

image-20200512174811173

ioc容器启动会创建对象,放到ioc容器中,以后每次获取就是直接从容器(map.get())中拿

1.下面演示单实例

image-20200512175129601

把test02中下面的注释掉

image-20200512175221757

2.多实例情况

image-20200512180504798

就不打印“给容器中添加Person…”了

image-20200512180656870

ioc容器启动并不会去调用方法创建对象放在容器中。每次获取的时候才会调用方法创建对象。

1.5-组件注册@Lazy-bean懒加载

  • 单实例bean,默认在容器启动的时候创建对象
  • 懒加载:容器启动不创建对象,第一次使用(获取)Bean创建对象,并初始化

image-20200512181332557

image-20200512181358877

即在第一次获得的时候才加载

若取消懒加载

image-20200512181536119

1.6-组件注册 @Conditional 按照条件给容器注入Bean

@Conditional ,按照一定的条件进行判断,满足条件给容器中注册Bean

先前准备:

image-20200512182257589

image-20200512182319410

要求:

* 如果是MacOs,给容器注册 jobs
* 如果是linux,给容器注册linus

image-20200512183810347

通过applicationContext拿到一个运行的环境

image-20200512183326595

image-20200512183431866

要传入一个Condition数组。@Conditional({})

image-20200512183517813

配置两个实现了Condition的类

image-20200512191747085

image-20200512191753422

设置一下参数image-20200512192027729

image-20200512192110595

//boolean pserson = registry.containsBeanDefinition("pserson");//也可判断容器中是否包含一个Bean。也可给容器中注册Bean

可以做非常多的判断条件

image-20200512202421896

也可放在类上,含义即满足当前条件,这个类中配置的 所有bean注册才能生效

注意

若有多个,则为按照顺序判断(猜测)已经设置-Dos.name=Linux

image-20200512202810229

1.7-组件注册 @Import快速导入

/**
 * 给容器中注册组件:
 * 1) 包扫描+组件标注注解 (@Controller/@Service/@Repository/@Component)[自己写的]
 * 2) @Bean[导入的第三方包里面的组件],但是它比较麻烦(需要return等)
 * 3) @Import[快速给容器导入一个组件]
 */

1.新建一个color类image-20200512201028328

2.使用@Importimage-20200512201044794

3.测试

image-20200512202928632

//导入组件,id默认是组件的全类名,@Import(要导入到容器中到组件),容器中就会自动注册这个组件,id默认是全类名

image-20200512203021101

可以导入多个,现写一个Red类

image-20200512203140380

1.7.1-组件注册 @ImportSelector
2)  ImportSelector:返回需要导入的组件的全数组
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);

}

前提条件:

image-20200513205236065

image-20200513205309622

打上断点进行调试

image-20200513205336350

结果:

image-20200513205350130

如果返回null:---->return null;会报空指针,因为在拿类名的时候

image-20200513205605821

所以不要返回null,可以返回一个空数组

return new String[]{};

image-20200513210013639

可获取到所以注解信息及类相关的

因为是return的,所以也被导入了

image-20200513211141399

1.7.2-组件注册 @ImportBeanDefinationRegister
ImportBeanDefinitionRegistrar 手动注册Bean

command+alt+b查看实现类

image-20200513213125884

注意,import方式注入的名称为全类名

image-20200513235948165

image-20200514000256305

1.8-组件注册 @FactoryBean

image-20200514000945452

通过此方法把对象放到容器中

image-20200514103628414

image-20200514103648956

结果:

image-20200514103750174

//工厂获取的是调用getObject创建的对象
@Override
public boolean isSingleton() {
    return false;
}

isSingleton是false情况下是多实例,每一次获取都调用getObject

image-20200514104048192

image-20200514104418286

Reason:

image-20200514104829151

使用Spring提供的FactoryBean(工厂Bean)
*              1)  默认获取到到是工厂bean调用getObject创建的对象
*              2)  要获取工厂Bean本身,我们需要给id前面加一个&
*                  &colorFactoryBean

二、生命周期

2.1-生命周期 @Bean指定初始化和销毁方法

在以前,可以指定初始化和销毁方法

image-20200514105507290

image-20200514114624653

创建Car

public class MainConfigOfLifeCycle {
    @Bean
    public Car car(){
        return new Car();
    }
}

image-20200514114824116

以上针对单实例对象

image-20200514115444620

通过调用close()关闭

image-20200514115552183

2.当改为多实例Bean当时候

image-20200514120208416

​ 2.1 当获取的时候才会初始化

image-20200514120255213

​ 2.2 容器关闭后不会进行销毁

image-20200514120338201

* bean的生命周期:
*      bean创建---初始化---销毁的过程
* 容器管理bean的生命周期:
* 我们可以自定义初始化和销毁方法;容器在  bean进行到当前生命周期的时候调用我们自定义的初始化和销毁方式
*构造(对象创建)
*      单实例:在容器启动的时候创建对象
*      多实例:在每次获取的时候创建对象
*初始化:
*      对象创建完成,并赋值好,调用初始化方法。。。
*销毁:
*      单实例:容器关闭的时候
*      多实例:容器不会管理这个bean,容器不会调用销毁方法;需要手动调用
* 1)、指定初始化和销毁方法:
*          指定init-method和destroy-method方法

2.2-生命周期 InitializingBean和DisposableBean

image-20200514122845407

image-20200514122926120

在这里提出一个问题,@Bean不搭配@Configuration使用跟搭配有什么区别(还未解决)

image-20200514132432275

image-20200514132500329

image-20200514132603373

通过包扫描的方式进行注册,同时通过实现接口进行初始化和销毁

 2)、通过让Bean实现InitializingBean(定义初始化逻辑)

2.3-生命周期 @PostConstruct和@PreDestroy

image-20200514132924546

image-20200514140351680

image-20200514140414660

这是java规范的注解,目前java8能用

image-20200514141103750

* 3)、可以使用JSR250:
*          @PostConstruct:在bean创建完成并且属性赋值完成:来执行初始化方法
*          @PreDestroy:在容器销毁bean之前通知我们进行清理工作

2.4-生命周期 BeanPostProcessor(后置处理器)

image-20200514141319899

image-20200514142626963

先创建对象-》〉》〉初始化

image-20200514142738648

* 4)、BeanPostProcessor【interface,bean的后置处理器:
*          在bean初始化前后进行一些处理工作:
*          postProcessBeforeInitialization:在初始化之前工作
*          postProcessAfterInitialization:在初始化之后工作
2.4.1-生命周期 BeanPostProcessor原理

打断点debug一下

image-20200514143005184

1.查看调用方法栈,往上依次看

image-20200514143549752

image-20200514143607149

image-20200514143632531

image-20200514144004605

image-20200514144033011

image-20200514144215598

image-20200514144334348

image-20200514144411012

image-20200514144446397

image-20200514144618656

10.来看怎么创建的

image-20200514144654708

image-20200514144722127

12.创建好后准备初始化

image-20200514144918297

image-20200514145300044

13.原理体现的地方

image-20200514145545659

点进去看一下,里面的内容

image-20200514145718974

applyBeanPostProcessorsAfterInitialization 就不看了,类似的
*          遍历得到容器中所有的BeanPostProcessor:挨个执行beforeInitialization
*          一单返回null,跳出for循环,不会执行后面单BeanPostProcessor
*          populateBean(beanName, mbd, instanceWrapper); 给bean进行属性赋值的
*
*       initializeBean:
*      {
*          wrappedBean = this.applyBeanPostProcessorsBeforeInitialization(bean, beanName);
*          this.invokeInitMethods(beanName, wrappedBean, mbd);
*          wrappedBean = this.applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
*      }
2.4.2-生命周期 spring底层对BeanPostProcessor的使用

我们来看一下ApplicationContextAwareProcessor,实现的BeanPostProcessor

image-20200514154132580

image-20200514154831407

其实看看之前写的MyBeanPostProcessor,也是实现了它的方法

image-20200514160401922

ApplicationContextAwareProcessor是封装好了这些实现

再来看下ApplicationContextAware

image-20200514154526246

image-20200514160625684

如果是就调用下面的 invokeAwareInterfaces(bean);

点进去查看

image-20200514160937349

来debug看一下

准备如下:
image-20200514161116770

Debug:

image-20200514171109848

并且会把ioc容器传进来,怎么传进来呢?接下来根据方法栈来看下之前调用的

1.在这里调用 postProcessBeforeInitialization

image-20200514171332661

image-20200514171415726

image-20200514171447377

再来看看 BeanValidationPostProcessor

image-20200514171639061

该PostProcessor是用来做数据校验的,在web用的比较多

再来看看 InitDestroyAnnotationBeanPostProcessor

image-20200514173814349

为什么Dog中标注了这样的注解它就知道在哪执行呢?

image-20200514173955545

我们打一个断点来看一下

image-20200514174035231

**出了个问题,init不执行(以后解决)**发现的问题为:
我还重写了个BeanPostProcessor的postProcessBeforeInitialization方法,@PostConstruct也是用

image-20200514203711376

这里重写了:

image-20200514203748694

再来看看 AutowiredAnnotationBeanPostProcessor

@Autowired也是通过这个来注值的

* spring底层对BeanPostProcessor的使用
*          bean赋值、注入其他组件,@Autowired,生命周期注解功能,@Async,等等
*          都是用BeanPostProcessor来完成的

三、属性赋值

3.1-属性赋值 @Value

image-20200515133831844

image-20200515133819235

并没有赋值,在以前的beans.xml文件中:

image-20200515133857543

是通过这样的方式

我们有一个对应的@Value

如果要在beans.xml中使用${}取properties中的值就要配上这个名称空间

image-20200515134521784

并采用以下方式:

ApplicationContext applicationContext2 = new ClassPathXmlApplicationContext("beans.xml");

但是启动会报错:

image-20200515141220002

这是因为少了context相关的解析文件。

image-20200515141321466

解决如下,在 xsi:schemaLocation 中添加:

image-20200515141440673

image-20200515141625033

image-20200515141729519

image-20200515141715575

3.2-属性赋值 @PropertySource加载外部配置文件

@PropertySource() 	属性的来源

image-20200515135417654

image-20200515141917082

image-20200515141937236

image-20200515142005301

因为是运行时候的变量,所以还可以用applicationContext.getEnvironment

image-20200515142306675

也可用PropertySources,是一个可重复标注的注解

image-20200515142409537

四、自动装配

4.1-自动装配 @Autowired & @Qualifier & @Primary

image-20200515153256213

image-20200515153418632

image-20200515153439198

image-20200515153521079

此外注意,在此版本中:

image-20200515152416877

准备:

image-20200515152514854

两个Dao,通过labe的设置看注入的是哪一个。相同类型,一个叫bookDao,一个叫bookDao2

image-20200515152623823

你可以看见第二个报错了,因为按照BookDao.class去找的

@Autowired 如果找到相同类型组件,就需要按照属性名去寻找

如:BookDao bookDao;就是按照bookDao去寻找

我现在按照名字去找,可以发现这两个BookDao是不一样的,即@Repository与@Bean返回 注入的两者不一样

image-20200515154741721

虽然在BookService通过@Autowired默认方法吗作为id注入,但是我们可以通过@Qualifier去改变

image-20200515155107498

另外:

将BookDao的@Repository注释掉

image-20200515155517873

image-20200515155727062

此时相当于容器中没有任何一个BookDao

运行时会报错

image-20200515155947706

看一下@Autowired,我们要达到没有该Bean就不注入的效果

image-20200515155558544

image-20200515160044106

此时service就正常了

我们发现如果容器同一个类型要用多个就要写多次@Qualifier,那么可以选用@Primary,即让spring进行自动装配的时候,默认使用首选的bean

image-20200515160942701

image-20200515161053012

image-20200515161343000

总结

image-20200515162356891

4.2-自动装配 JSR250-@Resource、JSR330-@Inject

image-20200517172739380

@Resource是默认按照属性的名称

image-20200517173723812

@Inject需要导入maven依赖

image-20200517174006293

image-20200517181450653

image-20200517181854598

4.3-自动装配 方法、构造器位置的自动装配 & Aware注入Spring底层组件 & 原理

@Autowired:

image-20200517182013544

1、标注在方法位置

image-20200517201930625

给Car也加上@Component,通过配置类@ComponentScan扫描进去

image-20200517202000244

image-20200517202043578

默认加载ioc容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作

image-20200517202624049

加到参数上,效果也一样

image-20200517203018066

image-20200517203541214

如果只有一个有参构造器,@Autowired可以不用写

image-20200517205922549

以@Bean的方式注入

准备

image-20200517211806836

image-20200517211929701

image-20200517212034370

自定义

image-20200517222851303

传进来这个applicationContext我们就能用,类似这样的有很多

总接口是Aware

image-20200517222941756

找几个Aware来看一下

image-20200517225059305

解析字符串的值

image-20200517225212076

这些Aware都是由相应的XXXAwareProcessor来处理的

我们来看一下怎么将applicationContext注入进来的

打一个断点:

image-20200517225638654

image-20200517225904728

跟之前是类似的

总结一下

image-20200518205313640

4.4-自动装配 @Profile 根据环境注册Bean

引入c3p0和mysql-connector

image-20200518205759283

配置dbconfig.properties

image-20200518210459825

并加载@PropertySource(“classpath:/dbconfig.properties”)

来自spring的黑科技

image-20200518210826772

另一种方式,Aware接口

image-20200518211504970

image-20200518211734753

那么来看看@Profile

image-20200518212000740

* @description Profile:
*                  Spring为我们提供的可以根据当前环境,动态地激活和切换一些列组件的功能:
* 开发环境、测试环境、生产环境:
* 数据源:(/A)(/B)(/C)
* @Profile 指定组件在哪个环境下才能被注册到容器中。不指定任何环境下都能注册这个组件
* 1)、加了环境表示的bean,只有这个环境被激活的时候才能被注册到容器中,默认是default环境

image-20200518212915954

默认是"default",可以看见只有标了"default"才会被加入到容器中

那么怎么切换环境呢?

最简单的方法,使用命令行参数

image-20200518213755544

image-20200518213944958

2.代码的方式

针对于AnnotationConfigApplicationContext

image-20200518214440915

配置类一注册进来,容器就启动刷新了,环境还没有设置好

image-20200518215001987

image-20200518215437570

如果写在类上,就代表整个类里面的内容是否会被加载

image-20200518215740276

* @Profile 指定组件在哪个环境下才能被注册到容器中。不指定任何环境下都能注册这个组件
* 1)、加了环境表示的bean,只有这个环境被激活的时候才能被注册到容器中,默认是default环境
* 2) 、写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
* 3)、没有标注环境标识的bean,任何环境下都会加载
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐