视频-图灵-3期-001

210 阅读15分钟

文章标题:视频-图灵-3期-001
编辑地点:北京 和义东里
完成时间:2021-07-11 17:12

视频讲师:司马
视频名称:IOC容器设计理念与核心注解的使用
视频日期:2019-06-05 20:13


00.讲师自我介绍

讲师:司马
曾就职于亚信科技、美团等多家互联网公司。
历任资深高级工程师、研发经理等。
主导过电信国家级项目重构,对高并发、微服务、中间件颇有研究。
IT界快车手
QQ:1920803591


01.Spring课程安排

Spring课程安排
第一节课讲应用
第二节课讲bean的扫描和启动
第三节课讲bean的生命周期
第四节课讲ioc容器如何解决bean的循环依赖(重中之重、核心)
第五节课aop源码解析
第六节课事物源码解析

02.Spring框架功能整体介绍

001-001.png

Spring Core Container

  1. Core 主要包含 Spring 框架基本的核心工具类, Spring 的其他组件都要用到这个包 里的类, Core模块是其他组件的基 本核心。

  2. Beans (BeanFacotry的作用) 它包含访问配直文件、创建和管理 bean 以及进行 Inversion of Control I Dependency Injection ( IoC/DI )操作相关的所有类

  3. Context(处理BeanFactory,一下还是ApplicationContext的作用)

    模构建于 Core 和 Beans 模块基础之上,提供了一种类似JNDI 注册器的框 架式的对象访问方 法。 Context 模块继承了 Beans 的特性,为 Spring 核 心提供了大量 扩展,添加了对 国际化(例如资源绑定)、事件传播、资源加载和对 Context 的透明创 建的支持。 Context 模块同时 也支持 J2EE 的一些特性, ApplicationContext 接口是 Context 模块的关键

    本质区别:(使用BeanFacotry的bean是延时加载的,ApplicationContext是非延时加载的)

  4. Expression Language

    模块提供了强大的表达式语言,用于在运行时查询和操纵对象。 它是 JSP 2.1 规范中定义的 unifed expression language 的扩展。 该语言支持设直/获取属 性的值,属性的分配,方法的调用,访问数 组上下文( accessiong the context of arrays )、 容器和索引器、逻辑和算术运算符、命名变量以 及从Spring的 IoC 容器中根据名称检 索对象。 它也支持 list 投影、选择和一般的 list 聚合

Spring Data Access/Integration

  1. JDBC

    模块提供了一个 JDBC 抽象层,它可以消除冗长的 JDBC 编码和解析数据库厂 商特有的错误代码。 这个模块包含了 Spring 对 JDBC 数据访问进行封装的所有类

  2. ORM 模块为流行的对象-关系映射 API

    如 JPA、 JDO、 Hibernate、 iBatis 等,提供了 一个交互层。 利用 ORM 封装包,可以混合使用所 有 Spring 提供的特性进行 O/R 映射, 如前边提到的简单声 明性事务管理。

  3. OXM 模块提供了一个对 ObjecνXML 映射实现的抽象层

    Object/XML 映射实现包括 JAXB、 Castor、 XMLBeans、 JiBX 和 XStrearn

  4. JMS ( Java Messaging Service )

    模块主要包含了 一些制造和消 费消息的特性

  5. Transaction

    支持编程和声明性的事务管理,这些事务类必须实现特定的接口,并 且对所有的 POJO 都适用

Spring Web

Web 模块:提供了基础的面向 Web 的集成特性c 例如,多文件上传、使用 servlet listeners 初始化 IoC 容器以及一个面向 Web 的应用上下文。 它还包含 Spring 远程支持中 Web 的相关部分

Spring Aop

  1. Aspects 模块提供了对 AspectJ 的集成支持
  2. Instrumentation 模块提供了 class instrumentation 支持和 classloader 实现,使得可以在特 定的应用服务器上使用

Test

Test 模块支持使用 JUnit 和 TestNG 对 Spring 组件进行测试


03.Spring 容器继承图

001-002.png


04.控制反转和依赖注入

  1. 什么是控制反转? 我觉得有必要先了解软件设计的一个重要思想:依赖倒置原则(Dependency Inversion Principle )

什么是依赖倒置原则? 假设我们设计一辆汽车:先设计轮子,然后根据轮子大小设计底盘,接着根据底 盘设计车身,最后根据车身设计好整个汽车。这里就出现了一个“依赖”关系:汽车依赖车身,车身依赖 底盘,底盘依赖轮子

001-003.png

上图看上去没有什么毛病?但是 万一轮胎尺寸改了,那么地盘需要改,地盘改了,车身也改了,让后整个汽车构造都改了.然后汽车公司倒闭了...

董事长依赖总经理争取,总经理依赖部门经理挣钱,部门经理依赖员工争取,那么员工离职了怎么办???

反过来...假如汽车公司决定修改轮胎的 我们就只需要改动轮子的设计,而不需要动底盘,车身,汽车 的设计了。

001-004.png

IOC容器的最最最最核心思想:

ioc的思想最核心的地方在于,资源不由使用资源的双方管理,而由不使用资源的第三方管理,这可以带来很多好处。 第一,资源集中管理,实现资源的可配置和易管理。 第二,降低了使用资源双方的依赖程度,也就是我们说的耦合度

001-005.png


Spring IOC 容器底层注解使用


05.xml配置文件的形式 VS 配置类的形式

基于xml的形式定义Bean的信息

创建Person类:

package luckyyyyyyluke.tuling.course001.entity;

public class Person {
}

创建XmlBeanFacotry.xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="person" class="luckyyyyyyluke.tuling.course001.entity.Persion"></bean>

</beans>

创建MainClass类:

package luckyyyyyyluke.tuling.course001;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import luckyyyyyyluke.tuling.course001.entity.Person;

public class MainClass {

    public static void main(String[] args) {

        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("XmlBeanFacotry.xml");
        final Person bean = applicationContext.getBean(Person.class);

        System.out.println(bean);
    }

}

运行MainClass结果:

luckyyyyyyluke.tuling.course001.entity.Person@1e178745

基于读取配置类的形式定义Bean信息

复用上面Person类

创建ConfigurationClassConfig配置类:

package luckyyyyyyluke.tuling.course001.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import luckyyyyyyluke.tuling.course001.entity.Person;

@Configuration
public class ConfigurationClassConfig {

    @Bean
    public Person person(){
        return new Person();
    }
}

修改MainClass测试类为:

package luckyyyyyyluke.tuling.course001;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import luckyyyyyyluke.tuling.course001.config.ConfigurationClassConfig;
import luckyyyyyyluke.tuling.course001.entity.Person;

public class MainClass {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigurationClassConfig.class);
        Person bean = applicationContext.getBean(Person.class);

        System.out.println(bean);
    }
}

运行MainClass结果:

luckyyyyyyluke.tuling.course001.entity.Person@350aac89

06.在配置类上写@ComponentScan注解来进行包扫描

创建C1_Controller:

package luckyyyyyyluke.tuling.course001.component_scan.controller;

import org.springframework.stereotype.Controller;

@Controller
public class C1_Controller {
}

创建S1_Service:

package luckyyyyyyluke.tuling.course001.component_scan.service;

import org.springframework.stereotype.Service;

@Service
public class S1_Service {
}

创建D1_Dao:

package luckyyyyyyluke.tuling.course001.component_scan.dao;

import org.springframework.stereotype.Repository;

@Repository
public class D1_Dao {
}

创建ComponentScanConfig配置类:

package luckyyyyyyluke.tuling.course001.config;

import org.springframework.context.annotation.ComponentScan;

@ComponentScan(basePackages ={"luckyyyyyyluke.tuling.course001.component_scan"} )
public class ComponentScanConfig {
}

修改MainClass测试类为:

package luckyyyyyyluke.tuling.course001;

import luckyyyyyyluke.tuling.course001.componentscan.config.MainConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import luckyyyyyyluke.tuling.course001.config.ComponentScanConfig;

import java.util.Arrays;

public class MainClass {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ComponentScanConfig.class);
        
        // 获取所有Bean定义的名称
        String[] beanDefinationNames = applicationContext.getBeanDefinitionNames();
        // 遍历打印Bean定义的名称
        Arrays.stream(beanDefinationNames).forEach(System.out::println);
    }
}

运行MainClass结果:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory

注:上面的类是容器内默认固定的,后面再打印出来时省略号表示

componentScanConfig
c1_Controller
d1_Dao
s1_Service

排除用法 excludeFilters(排除@Controller注解和S1_Service类)

修改ComponentScanConfig配置类:

package luckyyyyyyluke.tuling.course001.config;

import luckyyyyyyluke.tuling.course001.component_scan.service.S1Service;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;

@ComponentScan(
        basePackages = {"luckyyyyyyluke.tuling.course001.component_scan"},
        excludeFilters = {
                @ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class}),
                @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {S1Service.class}
                )
        }
)
public class ComponentScanConfig {
}

运行MainClass结果:

......
componentScanConfig
d1_Dao

包含用法 includeFilters ,注意,若使用包含的用法,需要把useDefaultFilters属性设置为false(true表示扫描全部的)

修改ComponentScanConfig配置类为:

package luckyyyyyyluke.tuling.course001.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;

@ComponentScan(
        basePackages = {"luckyyyyyyluke.tuling.course001.component_scan"},
        includeFilters = {
                @ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class, Service.class})
        },
        useDefaultFilters = false
)
public class ComponentScanConfig {
}

运行MainClass结果:

......
componentScanConfig
c1_Controller
s1_Service

@ComponentScan.Filter type的类型

类型作用
注解形式的FilterType.ANNOTATION@Controller @Service @Repository @Compent
指定类型的 FilterType.ASSIGNABLE_TYPE@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {S1_Service.class})
aspectj类型的 FilterType.ASPECTJ(不常用)
正则表达式的 FilterType.REGEX(不常用)
自定义的 FilterType.CUSTOM

FilterType.CUSTOM 自定义类型如何使用?

自定义CustomFilterType类:

package luckyyyyyyluke.tuling.course001.component_scan.custom;

import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;

public class CustomFilterType implements TypeFilter {

    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) {

        //获取当前类的注解源信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        //获取当前类的class的源信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        //获取当前类的资源信息
        Resource resource = metadataReader.getResource();

        if (classMetadata.getClassName().contains("dao")) {
            return true;
        }

        return false;
    }
}

修改ComponentScanConfig配置类为:

package luckyyyyyyluke.tuling.course001.config;

import luckyyyyyyluke.tuling.course001.componentscan.custom.CustomFilterType;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;

@ComponentScan(basePackages = {"luckyyyyyyluke.tuling.course001.component_scan"},
        includeFilters = {
                @ComponentScan.Filter(type = FilterType.CUSTOM, value = CustomFilterType.class)
        },
        useDefaultFilters = false
)
public class ComponentScanConfig {
}

运行MainClass结果:

......
componentScanConfig
d1_Dao

07.配置Bean的作用域对象

在不指定@Scope的情况下,所有的bean都是单实例的bean,而且是饿汉加载(容器启动实例就创建好了)

@Bean
public Person person(){
    return new Person();
}

指定@Scope为 prototype 表示为多实例的,而且还是懒汉模式加载(IOC容器启动的时候,并不会创建对象,而是 在第一次使用的时候才会创建)

@Bean
@Scope("prototype")
public Person person(){
    return new Person();
}

@Scope指定的作用域方法取值

singleton 单实例的(默认)
prototype 多实例的
request 同一次请求
session 同一个会话级别

Bean的懒加载@Lazy(主要针对单实例的bean 容器启动的时候,不创建对象,在第一次使用的时候才会创建该对象

@Bean
@Lazy
public Person person(){
    return new Person();
}

08.@Conditional进行条件判断等

场景:有二个组件C1_Compoment 和C2_Compoment ,C2_Compoment组件是依赖于C1_Compoment的组件

创建C1_Compoment组件:

package luckyyyyyyluke.tuling.course001.component_scan.component;

public class C1_Component {
}

创建C2_Compoment组件:

package luckyyyyyyluke.tuling.course001.component_scan.component;

public class C2_Component {
}

创建CustomCondition:

package luckyyyyyyluke.tuling.course001.component_scan.custom;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class CustomCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {

        //判断容器中是否有c1_Component的组件
        if(context.getBeanFactory().containsBean("c1_Component")) {
            return true;
        }

        return false;
    }
}

修改ConfigurationClassConfig配置类为:

package luckyyyyyyluke.tuling.course001.config;

import luckyyyyyyluke.tuling.course001.component_scan.component.C1_Component;
import luckyyyyyyluke.tuling.course001.component_scan.component.C2_Component;
import luckyyyyyyluke.tuling.course001.component_scan.custom.CustomCondition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;

public class ConfigurationClassConfig {

    @Bean
    public C1_Component c1_Component() {
        return new C1_Component();
    }

    @Bean
    @Conditional(value = CustomCondition.class)
    public C2_Component c2_Component() {
        return new C2_Component();
    }
}

修改MainClass测试类为:

package luckyyyyyyluke.tuling.course001;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import luckyyyyyyluke.tuling.course001.config.ConfigurationClassConfig;

import java.util.Arrays;

public class MainClass {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigurationClassConfig.class);
        
        // 获取所有Bean定义的名称
        String[] beanDefinationNames = applicationContext.getBeanDefinitionNames();
        // 遍历打印Bean定义的名称
        Arrays.stream(beanDefinationNames).forEach(System.out::println);
    }
}

运行MainClass结果:

......
mainConfig
c1_Component
c2_Component

注释掉MainConfig中C1_Component组件的 @Bean,运行MainClass结果:

......
configurationClassConfig

09.往IOC 容器中添加组件的方式

  1. 通过@CompentScan +@Controller @Service @Respository @compent 适用场景: 针对我们自己写的组件可以通过该方式来进行加载到容器中。

  2. 通过@Bean的方式来导入组件(适用于导入第三方组件的类)

  3. 通过@Import来导入组件 (导入组件的id为全类名路径)

  4. 通过实现FacotryBean接口来实现注册组件


10.@Import导入组件

修改ConfigurationClassConfig配置类为:

package luckyyyyyyluke.tuling.course001.config;

import luckyyyyyyluke.tuling.course001.component_scan.component.C1_Component;
import luckyyyyyyluke.tuling.course001.component_scan.component.C2_Component;
import org.springframework.context.annotation.Import;

@Import({C1_Component.class, C2_Component.class})
public class ConfigurationClassConfig {
}

运行MainClass结果:

......
configurationClassConfig
luckyyyyyyluke.tuling.course001.component_scan.component.C1_Component
luckyyyyyyluke.tuling.course001.component_scan.component.C2_Component

通过@Import 的ImportSeletor类实现组件的导入 (导入组件的id为全类名路径)

创建CustomImportSelector类:

package luckyyyyyyluke.tuling.course001.component_scan.custom;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

public class CustomImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"luckyyyyyyluke.tuling.course001.componentscan.component.C2_Component"};
    }
}

修改ConfigurationClassConfig配置类为:

package luckyyyyyyluke.tuling.course001.componentscan.config;

import luckyyyyyyluke.tuling.course001.componentscan.component.C1_Component;
import org.springframework.context.annotation.Import;

@Import({C1_Component.class, CustomImportSelector.class})
public class ConfigurationClassConfig {
}

运行MainClass结果:

......
configurationClassConfig
luckyyyyyyluke.tuling.course001.component_scan.component.C1_Component
luckyyyyyyluke.tuling.course001.component_scan.component.C2_Component

通过@Import的 ImportBeanDefinitionRegister导入组件 (可以指定bean的名称)

创建CustomImportBeanDefinitionRegister类:

package luckyyyyyyluke.tuling.course001.component_scan.custom;

import luckyyyyyyluke.tuling.course001.componentscan.component.C2_Component;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

public class CustomImportBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        //创建一个bean定义对象
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(C2_Component.class);
        //把bean定义对象导入到容器中
        registry.registerBeanDefinition("c2_Component", rootBeanDefinition);
    }
}

修改ConfigurationClassConfig配置类为:

package luckyyyyyyluke.tuling.course001.config;

import luckyyyyyyluke.tuling.course001.component_scan.custom.C1_Component;
import org.springframework.context.annotation.Import;

@Import({C1_Component.class, CustomImportBeanDefinitionRegister.class})
public class ConfigurationClassConfig {
}

运行MainClass结果:

......
configurationClassConfig
luckyyyyyyluke.tuling.course001.component_scan.component.C1_Component
c2_Component

11.FacotryBean接口来实现注册组件

创建PersonFactoryBean类:

package luckyyyyyyluke.tuling.course001.facotry_bean;

import luckyyyyyyluke.tuling.course001.entity.Person;
import org.springframework.beans.factory.FactoryBean;

public class PersonFactoryBean implements FactoryBean<Person> {

    // 返回bean的对象
    @Override
    public Person getObject() throws Exception {
        return new Person();
    }

    // 返回bean的类型
    @Override
    public Class<?> getObjectType() {
        return Person.class;
    }

    // 是否为单例
    @Override
    public boolean isSingleton() {
        return true;
    }
}

修改ConfigurationClassConfig为:

package luckyyyyyyluke.tuling.course001.config;

import luckyyyyyyluke.tuling.course001.facotry_bean.PersonFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ConfigurationClassConfig {

    @Bean
    public PersonFactoryBean personFactoryBean() {
        return new PersonFactoryBean();
    }
}

运行MainClass结果:

......
configurationClassConfig
personFactoryBean

Person类实例呢? 此时需要修改MainClass类为:

package luckyyyyyyluke.tuling.course001;

import luckyyyyyyluke.tuling.course001.config.ConfigurationClassConfig;
import luckyyyyyyluke.tuling.course001.entity.Person;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.util.Arrays;

public class MainClass {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigurationClassConfig.class);

        Arrays.stream(applicationContext.getBeanDefinitionNames()).forEach(System.out::println);

        Person bean = applicationContext.getBean(Person.class);
        System.out.println("Person: " + bean);
    }
}

运行MainClass结果:

......
configurationClassConfig
personFactoryBean
Person: luckyyyyyyluke.tuling.course001.entity.Person@4c60d6e9

12.Bean的初始化方法和销毁方法

什么是bean的生命周期?

bean的创建 --> 初始化 --> 销毁方法 由容器管理Bean的生命周期,我们可以通过自己指定bean的初始化方法和bean的销毁方法

通过@Bean指定bean的生命周期的初始化方法和销毁方法

修改Person类为:

package luckyyyyyyluke.tuling.course001;

public class Person {

    public Person() {
        System.out.println("Person的构造方法执行");
    }

    public void init() {
        System.out.println("Person的init方法执行");
    }

    private void destroy() {
        System.out.println("Person的destroy方法执行");
    }
}

创建LifeCycleConfig配置类:

package luckyyyyyyluke.tuling.course001.config;

import luckyyyyyyluke.tuling.course001.Person;
import org.springframework.context.annotation.Bean;

public class LifeCycleConfig {

    //指定了bean的生命周期的初始化方法和销毁方法
    @Bean(initMethod = "init",destroyMethod = "destroy")
    public Person person() {
        return new Person();
    }
}

修改MainClass类为:

package luckyyyyyyluke.tuling.course001;

import luckyyyyyyluke.tuling.course001.config.LifeCycleConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainClass {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(LifeCycleConfig.class);

        applicationContext.close();
    }
}

运行MainClass类结果为:

Person的构造方法执行
Person的init方法执行
Person的destroy方法执行

针对单实例bean的话,容器启动的时候,bean的对象就创建了,而且容器销毁的时候,也会调用Bean的销毁方法 针对多实例bean的话,容器启动的时候,bean是不会被创建的而是在获取bean的时候被创建,而且bean的销毁不受IOC容器的管理.

通过 InitializingBean和DisposableBean 的二个接口实现bean的初始化以及销毁方法

修改Person类为:

package luckyyyyyyluke.tuling.course001;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

public class Person implements InitializingBean, DisposableBean {

    public Person() {
        System.out.println("Person的构造方法执行");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("Person的afterPropertiesSet方法执行");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("Person的destroy方法执行");
    }
}

修改Person类为:

package luckyyyyyyluke.tuling.course001.config;

import luckyyyyyyluke.tuling.course001.Person;
import org.springframework.context.annotation.Bean;

public class LifeCycleConfig {

    @Bean
    public Person person() {
        return new Person();
    }
}

运行MainClass类结果为:

Person的构造方法执行
Person的afterPropertiesSet方法执行
Person的destroy方法执行

通过JSR250规范 提供的注解@PostConstruct 和@ProDestory标注的方法

修改Person类为:

package luckyyyyyyluke.tuling.course001;

import org.springframework.stereotype.Component;

public class Person {

    public Person() {
        System.out.println("Person的构造方法执行");
    }

    @PostConstruct
    public void postConstruct() {
        System.out.println("Person的postConstruct方法执行");
    }

    @PreDestroy
    private void preDestroy() {
        System.out.println("Person的preDestroy方法执行");
    }
}

修改ComponentScanConfig为:

package luckyyyyyyluke.tuling.course001.config;

import luckyyyyyyluke.tuling.course001.Person;
import org.springframework.context.annotation.Import;

@Import({Person.class})
public class ComponentScanConfig {
}

修改MainClass类为:

package luckyyyyyyluke.tuling.course001;

import luckyyyyyyluke.tuling.course001.config.ComponentScanConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainClass {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ComponentScanConfig.class);

        applicationContext.close();
    }
}

运行MainClass结果为:

Person的构造方法执行
Person的postConstruct方法执行
Person的preDestroy方法执行

通过Spring的BeanPostProcessor的 bean的后置处理器会拦截所有bean创建过程

postProcessBeforeInitialization 在init方法之前调用 postProcessAfterInitialization 在init方法之后调用

创建CustomBeanPostProcessor类:

package luckyyyyyyluke.tuling.course001.life_cycle.custom;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class CustomBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

        System.out.println("postProcessBeforeInitialization: " + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        System.out.println("postProcessAfterInitialization: " + beanName);
        return bean;
    }
}

修改ConfigurationClassConfig为:

package luckyyyyyyluke.tuling.course001.config;

import luckyyyyyyluke.tuling.course001.entity.Person;
import luckyyyyyyluke.tuling.course001.life_cycle.custom.CustomBeanPostProcessor;
import org.springframework.context.annotation.Bean;

public class ConfigurationClassConfig {

    @Bean
    public CustomBeanPostProcessor customBeanPostProcessor() {
        return new CustomBeanPostProcessor();
    }

    @Bean
    public Person person() {
        return new Person();
    }
}

修改MainClass类为:

package luckyyyyyyluke.tuling.course001;

import luckyyyyyyluke.tuling.course001.config.ConfigurationClassConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.util.Arrays;

public class MainClass {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigurationClassConfig.class);

        Arrays.stream(applicationContext.getBeanDefinitionNames()).forEach(System.out::println);
    }
}

运行MainClass类结果为:

postProcessBeforeInitialization: org.springframework.context.event.internalEventListenerProcessor
postProcessAfterInitialization: org.springframework.context.event.internalEventListenerProcessor
postProcessBeforeInitialization: org.springframework.context.event.internalEventListenerFactory
postProcessAfterInitialization: org.springframework.context.event.internalEventListenerFactory
postProcessBeforeInitialization: person
postProcessAfterInitialization: person
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
configurationClassConfig
customBeanPostProcessor
person

BeanPostProcessor的执行时机:

populateBean(beanName, mbd, instanceWrapper)
initializeBean{
    applyBeanPostProcessorsBeforeInitialization()
    invokeInitMethods{
        isInitializingBean.afterPropertiesSet
        自定义的init方法
    }
    applyBeanPostProcessorsAfterInitialization()方法
}

13.通过@Value +@PropertySource来给组件赋值

修改Person类为:

package luckyyyyyyluke.tuling.course001.entity;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.beans.factory.annotation.Value;

@Getter
@Setter
@ToString
public class Person {

    //通过普通的方式
    @Value("司马")
    private String firstName;

    //spel方式来赋值
    @Value("#{28-8}")
    private Integer age;

    // 通过读取外部配置文件的值
    @Value("${person.hobby}")
    private String hobby;
}

创建person.properties文件:

person.hobby=read

修改ConfigurationClassConfig为:

package luckyyyyyyluke.tuling.course001.config;

import luckyyyyyyluke.tuling.course001.entity.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;

@PropertySource(value = {"classpath:person.properties"}) //指定外部文件的位置
public class ConfigurationClassConfig {

    @Bean
    public Person person() {
        return new Person();
    }
}

修改MainClass类为:

package luckyyyyyyluke.tuling.course001;

import luckyyyyyyluke.tuling.course001.config.ConfigurationClassConfig;
import luckyyyyyyluke.tuling.course001.entity.Person;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainClass {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigurationClassConfig.class);

        Person bean = applicationContext.getBean(Person.class);

        System.out.println(bean);
    }
}

运行MainClass结果为:

Person(firstName=司马, age=20, hobby=read)

14.自动装配

@AutoWired的使用

修改S1_Service为:

package luckyyyyyyluke.tuling.course001.component_scan.service;

import luckyyyyyyluke.tuling.course001.component_scan.dao.D1_Dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class S1_Service {

    @Autowired
    private D1_Dao d1_dao;
}

结论:

  1. 自动装配首先时按照类型进行装配,若在IOC容器中发现了多个相同类型的组件,那么就按照 属性名称来进行装配

    @Autowired
        private D1_Dao d1_dao;
    

    比如,我容器中有二个Dao类型的组件 一个叫D1_Dao 一个叫D2_Dao。那么我们通过@AutoWired 来修饰的属性名称时d1_Dao,那么拿就加载容器的d1_Dao组件,若属性名称为 d2_Dao 那么他就加载的时d2_Dao组件

  2. 假设我们需要指定特定的组件来进行装配,我们可以通过使用@Qualifier("tulingDao")来指定装配的组件 或者在配置类上的@Bean加上@Primary注解

    package luckyyyyyyluke.tuling.course001.component_scan.service;
    
    import luckyyyyyyluke.tuling.course001.component_scan.dao.D1_Dao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Service;
    
    @Service
    public class S1_Service {
    
        @Autowired
        @Qualifier("d2_dao")
        private D1_Dao d1_dao;
    }
    
  3. 假设我们容器中即没有tulingDao 和tulingDao2,那么在装配的时候就会抛出异常No qualifying bean of type 'com.tuling.testautowired.TulingDao' available,若我们想不抛异常 ,我们需要指定 required为false的时候可以了

    @Autowired(required = false)
    @Qualifier("d2_dao")
    private D1_Dao d1_dao;
    
  4. @Resource(JSR250规范)

    功能和@AutoWired的功能差不多一样,但是不支持@Primary 和@Qualifier的支持

  5. @InJect(JSR330规范)

    需要导入jar包依赖 功能和支持@Primary功能 ,但是没有Require=false的功能

    <dependency>
        <groupId>javax.inject</groupId>
        <artifactId>javax.inject</artifactId>
        <version>1</version>
    </dependency>
    
  6. 使用autowired 可以标注在方法上

    标注在set方法上

    package luckyyyyyyluke.tuling.course001.component_scan.service;
    
    import luckyyyyyyluke.tuling.course001.component_scan.dao.D1_Dao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    public class S1_Service {
    
        private D1_Dao d1_dao;
    
        @Autowired
        public void setD1_dao(D1_Dao d1_dao) {
            this.d1_dao = d1_dao;
        }
    }
    

    标注在构造方法上

    package luckyyyyyyluke.tuling.course001.component_scan.service;
    
    import luckyyyyyyluke.tuling.course001.component_scan.dao.D1_Dao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    public class S1_Service {
    
        @Autowired
        public S1_Service(D1_Dao d1_dao) {
            this.d1_dao = d1_dao;
        }
    
        private D1_Dao d1_dao;
    }
    

    标注在配置类上的入参中(可以不写)

    package luckyyyyyyluke.tuling.course001.config;
    
    import luckyyyyyyluke.tuling.course001.component_scan.dao.D1_Dao;
    import luckyyyyyyluke.tuling.course001.component_scan.service.S1_Service;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    
    public class ConfigurationClassConfig {
    
        @Bean
        public S1_Service s1_Service(@Autowired D1_Dao d1_dao) {
            return new S1_Service(d1_dao);
        }
    }
    

15.如果自己的组件需要使用spring ioc的底层组件的时候,比如 ApplicationContext等,可以通过实现XXXAware接口来实现

修改C1_Component为:

package luckyyyyyyluke.tuling.course001.component_scan.component;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

@Getter
@Setter
@ToString
public class C1_Component implements ApplicationContextAware, BeanNameAware {

    private String name;
    private ApplicationContext applicationContext;

    @Override
    public void setBeanName(String name) {
        this.name = name;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

修改ComponentScanConfig为:

package luckyyyyyyluke.tuling.course001.config;

import luckyyyyyyluke.tuling.course001.component_scan.component.C1_Component;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import({C1_Component.class})
public class ComponentScanConfig {
}

修改MainClass为:

package luckyyyyyyluke.tuling.course001;

import luckyyyyyyluke.tuling.course001.component_scan.component.C1_Component;
import luckyyyyyyluke.tuling.course001.config.ComponentScanConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainClass {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ComponentScanConfig.class);

        C1_Component bean = applicationContext.getBean(C1_Component.class);

        System.out.println(bean);
    }
}

运行MainClass为:

C1_Component(name=luckyyyyyyluke.tuling.course001.component_scan.component.C1_Component, applicationContext=org.springframework.context.annotation.AnnotationConfigApplicationContext@148080bb: startup date [Sun Jul 11 16:26:07 CST 2021]; root of context hierarchy)

16.通过@Profile注解 来根据环境来激活标识不同的Bean

@Profile标识在类上,那么只有当前环境匹配,整个配置类才会生效 @Profile标识在Bean上 ,那么只有当前环境的Bean才会被激活 没有标志为@Profile的bean 不管在什么环境都可以被激活

修改ConfigurationClassConfig为:

package luckyyyyyyluke.tuling.course001.config;

import luckyyyyyyluke.tuling.course001.entity.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;

public class ConfigurationClassConfig {

    @Bean
    @Profile(value = "test") // 标识为测试环境才会被装配
    public Person testPerson() {
        return new Person();
    }

    @Bean
    @Profile(value = "dev") // 标识开发环境才会被激活
    public Person devPerson() {
        return new Person();
    }

    @Bean
    @Profile(value = "prod") // 标识生产环境才会被激活
    public Person prodPerson() {
        return new Person();
    }
}

激活切换环境的方法

  1. 通过运行时jvm参数来切换 -Dspring.profiles.active=test|dev|prod

  2. 通过代码的方式来激活

    package luckyyyyyyluke.tuling.course001;
    
    import luckyyyyyyluke.tuling.course001.config.ConfigurationClassConfig;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    import java.util.Arrays;
    
    public class MainClass {
    
        public static void main(String[] args) {
    
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
    
            applicationContext.getEnvironment().setActiveProfiles("test", "dev");
    
            applicationContext.register(ConfigurationClassConfig.class);
            applicationContext.refresh();
    
            Arrays.stream(applicationContext.getBeanDefinitionNames()).forEach(System.out::println);
        }
    }
    

    运行MainClass结果:

    ......
    configurationClassConfig
    testPerson
    devPerson
    

结束

作者微信:1554325818
如有问题欢迎沟通