吃透 Spring 系列—IOC部分

目录

◆ 传统Javaweb开发的困惑

-传统Javaweb开发代码分析-用户模块

-传统Javaweb开发困惑及解决方案

◆ IoC、DI和AOP思想提出 

- IoC 控制反转思想的提出

- DI 依赖注入思想的提出

- AOP 面向切面思想的提出

- 框架概念的出现

- 思想、框架和编码关系

◆ Spring框架的诞生

- Spring 框架概述

- Spring 框架的历史

- Spring Framework技术栈图示

- BeanFactory 快速入门

- ApplicationContext快速入门

- BeanFactory与ApplicationContext的关系

- BeanFactory的继承体系

- ApplicationContext的继承体系

◆ 基于xml的Spring应用

- SpringBean 的配置详解

- Spring 的get方法

- Spring 配置非自定义Bean

- Bean 实例化的基本流程

- Spring的后处理器

- Spring Bean的生命周期

- Spring IoC 整体流程总结

- Spring xml方式整合第三方框架

◆ 基于注解的Spring应用

- Bean基本注解开发

- Bean依赖注入注解开发

- 非自定义Bean注解开发

- Bean配置类的注解开发

- Spring 配置其他注解

- Spring注解的解析原理

- Spring注解方式整合第三方框架


传统Javaweb开发的困惑

-传统Javaweb开发代码分析-用户模块

 

 

-传统Javaweb开发困惑及解决方案

困惑一层与层之间紧密耦合在了一起,接口与具体实现紧密耦合在了一起

解决思路:程序代码中不要手动new对象,第三方根据要求为程序提供需要的Bean对象

 

惑二:通用的事务功能耦合在业务代码中,通用的日志功能耦合在业务代码中 

解决思路:程序代码中不要手动new对象,第三方根据要求为程序提供需要的Bean对象的代理对象,代理对象内部动态结合业务和通用功能 

IoC、DI和AOP思想提出 

- IoC 控制反转思想的提出

谁去充当第三方角色呢?

工厂设计模式BeanFactory来充当第三方的角色,来产生Bean实例BeanFactory怎么知道产生哪些Bean实例呢?

可以使用配置文件配置Bean的基本信息,BeanFactory根据配置文件来生产Bean实例

例如,用IoC思想去反转UserServiceImpl的创建权,由原来程序中创建反转给通过BeanFactory去创建

- DI 依赖注入思想的提出

上面使用BeanFactory的方式已经实现的"控制反转",将Bean的创建权交给了BeanFactory,如果我们想将UserDao的创建权也反转给BeanFactory,与此同时UserService内部还需要用到UserDao实例对象,那应该怎
样操作呢?

1)在程序中,通过BeanFactory获得UserService

2)在程序中,通过BeanFactory获得UserDao

3)在程序中,将UserDao设置给UserService

该方式是否存在一些问题? 

 

UserService存在于BeanFactory中,UserDao也存在于BeanFactory中,可以在BeanFactory内部进行结合

将UserDao在BeanFactory内部设置给UserService的过程叫做“注入”,而UserService需要依赖UserDao的注入才能正常工作,这个过程叫做“依赖注入

面试题:IoC 和 DI 的关系?

首先,先回答IoC和DI的是什么:

IoC: Inversion of Control,控制反转,将Bean的创建权由原来程序反转给第三方

DI:Dependency Injection,依赖注入,某个Bean的完整创建依赖于其他Bean(或普通参数)的注入
其次,在回答IoC和DI的关系:

第一种观点:IoC强调的是Bean创建权的反转,而DI强调的是Bean的依赖关系,认为不是一回事

第二种观点:IoC强调的是Bean创建权的反转,而DI强调的是通过注入的方式反转Bean的创建权,认为DI是IoC的其中一种实现方式

- AOP 面向切面思想的提出

IoC和DI思想主要是解决前面我们的困惑一,困惑二还没有解决

困惑二的解决方案是,借助于IoC思想,将Bean的创建权反转给BeanFactory,而BeanFactory生产的Bean是目标Bean的代理对象,这样就可以在代理对象中对目标对象方法进行相应的增强

AOP,Aspect Oriented Programming,面向切面编程,是对面向对象编程OOP的升华。OOP是纵向对一个事物的抽象,一个对象包括静态的属性信息,包括动态的方法信息等。而AOP是横向的对不同事物的抽象,属性与属性、方法与方法、对象与对象都可以组成一个切面,而用这种思维去设计编程的方式叫做面向切面编程

三种思想总结

1)IoC控制反转,是将程序创建Bean的权利反转给第三方;

2)DI依赖注入,某个完整Bean需要依赖于其他Bean(或属性)的注入;

3)AOP面向切面编程,用横向抽取方法(属性、对象等)思想,组装成一个功能性切面。

- 框架概念的出现

上面提出了一些思想来解决遇到的问题,而这些思想的实现就需要通过编码去落地,往往我们把具备一定业务领域解决方案的"工具"称为框架

框架的基本特点:

⚫  框架(Framework),是基于基础技术之上,从众多业务中抽取出的通用解决方案;

⚫  框架是一个半成品,使用框架规定的语法开发可以提高开发效率,可以用简单的代码就能完           成复杂的基础业务

⚫  框架内部使用大量的设计模式、算法、底层代码操作技术,如反射、内省、xml解析、注解             解析等;

⚫  框架一般都具备扩展性;

⚫  有了框架,我们可以将精力尽可能的投入在纯业务开发上而不用去费心技术实现以及一些辅助业务。

Java中常用的框架:

不同语言,不同领域都有属于自己的框架,使用框架开发是作为程序员的最基础的底线。Java语言中的框架,可以分为基础框架和服务框架:

- 基础框架:完成基本业务操作的框架,如MyBatis、Spring、SpringMVC、Struts2、Hibernate等

- 服务框架:特定领域的框架,一般还可以对外提供服务框架,如MQ、ES、Nacos等

- 思想、框架和编码关系

- 架构师(高级程序员):把思想落地变为实现的人,例如上面的设计和BeanFactory的编写,即框架的设计和实现者。

- 程序员:使用框架完成业务的人,其中UserServiceImpl、beans.xml、测试类都是我们编写的。

◆ Spring框架的诞生

- Spring 框架概述

spring是一个开源的轻量级Java开发应用框架,可以简化企业级应用开发。Spring解决了开发者在JavaEE开发中遇到的许多常见的问题,提供了功能强大IOC、AOP及Web MVC等功能。是当前企业中Java开发几乎不能缺少的框架之一。Spring的生态及其完善,不管是Spring哪个领域的解决方案都是依附于在SpringFramework基础框架的。

Spring的官网www.spring.io

- Spring 框架的历史

  ●   Jsp 默默扛下所有;

  ●    MVC+三层架构分工明确,但开发成本及其高;

  ●    EJB 重量级框架出现,走出一个困境,有进入另一个困境;

  ●    Spring 春天来到,随之,SSH风生水起、称霸武林;

  ●    Spring 稳住江湖大哥位置,SSM开始上位;

  ●    Spring 本着“拿来主义”的思维快速发展,生态不断健全;

  ●    SpringBoot 又一里程碑崛起,把“约定大于配置“思想玩儿的炉火纯青;

  ●    SpringCloud 打包了微服务众多解决方案,应对互联网项目更加easy!

- Spring Framework技术栈图示

- BeanFactory 快速入门

根据下图,分析一下Spring的BeanFactory的开发步骤

 

1)导入Spring的jar包或Maven坐标;

<!--Spring核心-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.7</version>
</dependency>

2)定义UserService接口及其UserServiceImpl实现类;

public interface UserService {}

public class UserServiceImpl implements UserService {}

3)创建beans.xml配置文件,将UserServiceImpl的信息配置到该xml中;

<bean id="userService" class="com.green.service.impl.UserServiceImpl"></bean>

4)编写测试代码,创建BeanFactory,加载配置文件,获取UserService实例对象。

//创建BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//创建读取器
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
//加载配置文件
reader.loadBeanDefinitions("beans.xml");
//获取Bean实例对象
UserDao userService = (UserService) beanFactory.getBean("userService");

上面使用BeanFactory完成了IoC思想的实现,下面去实现以下DI依赖注入:

1)定义UserDao接口及其UserDaoImpl实现类;

public interface UserDao {}

public class UserDaoImpl implements UserDao {}

2)修改UserServiceImpl代码,添加一个setUserDao(UserDao userDao)用于接收注入的对象;

public class UserServiceImpl implements UserService {
        public void setUserDao(UserDao userDao) {
              System.out.println(userDao);
        }
}

3)修改beans.xml配置文件,在UserDaoImpl的<bean>中嵌入<property>配置注入;

<bean id="userService" class="com.green.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>

4)修改测试代码,获得UserService时,setUserService方法执行了注入操作。

//创建BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//创建读取器
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
//加载配置文件
reader.loadBeanDefinitions("beans.xml");
//获取Bean实例对象
UserDao userService = (UserService) beanFactory.getBean("userService");

- ApplicationContext快速入门

ApplicationContext 称为Spring容器,内部封装了BeanFactory,比BeanFactory功能更丰富更强大,使用ApplicationContext 进行开发时,xml配置文件的名称习惯写成applicationContext.xml

- BeanFactory与ApplicationContext的关系

1)BeanFactory是Spring的早期接口,称为Spring的Bean工厂,ApplicationContext是后期更高级接口,称之为Spring 容器

2)ApplicationContext在BeanFactory基础上对功能进行了扩展,例如:监听功能、国际化功能等。BeanFactory的API更偏向底层,ApplicationContext的API大多数是对这些底层API的封装;

3)Bean创建的主要逻辑和功能都被封装在BeanFactory中,ApplicationContext不仅继承了BeanFactory,而且ApplicationContext内部还维护着BeanFactory的引用,所以ApplicationContext与BeanFactory既有继承关系,又有融合关系。

4)Bean的初始化时机不同,原始BeanFactory是在首次调用getBean时才进行Bean的创建,而ApplicationContext则是配置文件加载,容器一创建就将Bean都实例化并初始化好。

ApplicationContext除了继承了BeanFactory外,还继承了ApplicationEventPublisher(事件发布器)、ResouresPatternResolver(资源解析器)、MessageSource(消息资源)等。但是ApplicationContext的核心功能还是BeanFactory。

applicationContext内部维护着beanFactory的引用,在学习过程中会查看beanFactory内部维护的属性,断点查看如下图示内容的

验证BeanFactory和ApplicationContext对Bean的初始化时机,在UserDaoImpl的无参构造内打印一句话,验证构造方法的执行时机

断点观察,BeanFactory方式时,当调用getBean方法时才会把需要的Bean实例创建,即延迟加载;而ApplicationContext是加载配置文件,容器创建时就将所有的Bean实例都创建好了,存储到一个单例池中,当调用getBean时直接从单例池中获取Bean实例返回

- BeanFactory的继承体系

BeanFactory是核心接口,项目运行过程中肯定有具体实现参与,这个具体实现就是DefaultListableBeanFactory,而ApplicationContext内部维护的Beanfactory的实现类也是它

- ApplicationContext的继承体系

只在Spring基础环境下,即只导入spring-context坐标时,此时ApplicationContext的继承体系

只在Spring基础环境下,常用的三个ApplicationContext作用如下: 

实现类

功能描述

ClassPathXmlApplicationContext

加载类路径下的xml配置的ApplicationContext

FileSystemXmlApplicationContext

加载磁盘路径下的xml配置的ApplicationContext

AnnotationConfigApplicationContext

加载注解配置类的ApplicationContext

如果Spring基础环境中加入了其他组件解决方案,如web层解决方案,即导入spring-web坐标,此时ApplicationContext的继承体系

在Spring的web环境下,常用的两个ApplicationContext作用如下: 

实现类

功能描述

XmlWebApplicationContext

web环境下,加载类路径下的xml配置的ApplicationContext

AnnotationConfigWebApplicationContext

web环境下,加载磁盘路径下的xml配置的ApplicationContext

PS:web环境下的这两个ApplicationContext,在学习Spring集成web时在进行讲解 

◆ 基于xml的Spring应用

- SpringBean 的配置详解

Spring开发中主要是对Bean的配置,Bean的常用配置一览如下:

Xml配置方式

功能描述

<bean id="" class="">

Beanid全限定名配置

<bean name="">

通过name设置Bean的别名,通过别名也能直接获取到Bean实例

<bean scope="">

Bean的作用范围,BeanFactory作为容器时取值singletonprototype

<bean lazy-init="">

Bean的实例化时机,是否延迟加载。BeanFactory作为容器时无效

<bean init-method="">

Bean实例化后自动执行的初始化方法,method定方法名

<bean destroy-method="">

Bean实例销毁前的方法,method定方法名

<bean autowire="byType">

设置自动注入模式,常用的有按照类型byType,按照名字byName

<bean factory-bean="" factory-method=""/>

指定哪个工厂Bean的哪个方法完成Bean的创建

1)Bean的基础配置

例如:配置UserDaoImpl由Spring容器负责管理

此时存储到Spring容器(singleObjects单例池)中的Bean的beanName是userDao,值是UserDaoImpl对象,可以根据beanName获取Bean实例

如果不配置id,则Spring会把当前Bean实例的全类名作为beanName

2)Bean的别名配置

可以为当前Bean指定多个别名,根据别名也可以获得Bean对象

 

此时多个名称都可以获得UserDaoImpl实例对象 

3)Bean的范围配置

默认情况下,单纯的Spring环境Bean的作用范围有两个:SingletonPrototype

●  singleton:单例,默认值,Spring容器创建的时候,就会进行Bean的实例化,并存储到容器内部的单例池中,每次getBean时都是从单例池中获取相同的Bean实例; 

 ●  prototype:原型,Spring容器初始化时不会创建Bean实例,当调用getBean时才会实例化Bean,每次getBean都会创建一个新的Bean实例。

当scope设置为singleton时,获得两次对象打印结果是一样的 

 

通过断点调试,观察可以发现单例池中存在 userDao 实例

当scope设置为prototype时,获得两次对象打印结果是不一样的结果

通过断点调试,观察可以发现单例池中不存在 userDao 实例,但是 userDao的信息已经被存储到beanDefinitionMap中了 

4)Bean的延迟加载

当lazy-init设置为true时为延迟加载,也就是当Spring容器创建的时候,不会立即创建Bean实例,等待用到时在创建Bean实例并存储到单例池中去,后续在使用该Bean直接从单例池获取即可,本质上该Bean还是单例的

 

5)Bean的初始化和销毁方法配置

Bean在被实例化后,可以执行指定的初始化方法完成一些初始化的操作,Bean在销毁之前也可以执行指定的销毁方法完成一些操作,初始化方法名称和销毁方法名称通过

 

扩展:除此之外,我们还可以通过实现 InitializingBean 接口,完成一些Bean的初始化操作,如下: 

6)Bean的实例化配置

Spring的实例化方式主要如下两种:

●  构造方式实例化:底层通过构造方法对Bean进行实例化

●  工厂方式实例化:底层通过调用自定义的工厂方法对Bean进行实例化

构造方式实例化Bean又分为无参构造方法实例化和有参构造方法实例化,Spring中配置的<bean>几乎都是无参构造该方式,此处不在赘述。下面讲解有参构造方法实例化Bean

 

有参构造在实例化Bean时,需要参数的注入,通过<constructor-arg>标签,嵌入在<bean>标签内部提供构造参数,如下:

工厂方式实例化Bean,又分为如下三种: 

●  静态工厂方法实例化Bean

●  实例工厂方法实例化Bean

●  实现FactoryBean规范延迟实例化Bean

静态工厂方法实例化Bean,其实就是定义一个工厂类,提供一个静态方法用于生产Bean实例,在将该工厂类及其静态方法配置给Spring即可

 

 

PS:<constructor-arg>标签不仅仅是为构造方法传递参数,只要是为了实例化对象而传递的参数都可以通过<constructor-arg>标签完成,例如上面通过静态工厂方法实例化Bean所传递的参数也是要通过<constructor-arg>进行传递的

 测试代码,直接通过ApplicationContext获得userDao即可

 

断点调试,UserDaoImpl实例对象会存在于单例池中 

实例工厂方法,也就是非静态工厂方法产生Bean实例,与静态工厂方式比较,该方式需要先有工厂对象,在用工厂对象去调用非静态方法,所以在进行配置时,要先配置工厂Bean,再配置目标Bean 

 

 

测试代码同上,直接通过ApplicationContext获得userDao即可,不在赘述

通过断点观察单例池singletonObjects,发现单例池中既有工厂Bean实例,也有目标Bean实例,且都是在Spring容器创建时,就完成了Bean的实例化

 

上面不管是静态工厂方式还是非静态工厂方式,都是自定义的工厂方法,Spring提供了FactoryBean的接口规范,FactoryBean接口定义如下: 

定义工厂实现FactoryBean 

配置FactoryBean交由Spring管理即可 

通过Spring容器根据beanName可以正常获得UserDaoImpl 

通过断点观察发现Spring容器创建时,FactoryBean被实例化了,并存储到了单例池singletonObjects中,但是getObject() 方法尚未被执行,UserDaoImpl也没被实例化,当首次用到UserDaoImpl时,才调用getObject() ,此工厂方式产生的Bean实例不会存储到单例池singletonObjects中,会存储到 factoryBeanObjectCache 缓存池中,并且后期每次使用到userDao都从该缓存池中返回的是同一个userDao实例。

 

7)Bean的依赖注入配置

Bean的依赖注入有两种方式:

入方式

置方式

通过Beanset方法注入

<property name="userDao" ref="userDao"/>

<property name="userDao" value="haohao"/>

通过构造Bean的方法进行注入

<constructor-arg name="name" ref="userDao"/>

<constructor-arg name="name" value="haohao"/>

其中,ref 是 reference 的缩写形式,翻译为:涉及,参考的意思,用于引用其他Bean的id。value 用于注入普通属性值。

依赖注入的数据类型有如下三种:

●  普通数据类型,例如:String、int、boolean等,通过value属性指定。

●  引用数据类型,例如:UserDaoImpl、DataSource等,通过ref属性指定。

●  集合数据类型,例如:List、Map、Properties等。

注入 List<T> 集合 – 普通数据

 注入 List<T> 集合 – 引用数据

也可以直接引用容器中存在的Bean 

注入 Set<T> 集合 

注入 Map<K,V> 集合 

注入 Properties 键值对 

扩展:自动装配方式

如果被注入的属性类型是Bean引用的话,那么可以在<bean> 标签中使用 autowire 属性去配置自动注入方式,属性值有两个:

●  byName:通过属性名自动装配,即去匹配 setXxx 与 id="xxx"(name="xxx")是否一致;

    ●  byType:通过Bean的类型从容器中匹配,匹配出多个相同Bean类型时,报错。

8)Spring的其他配置标签

Spring 的 xml 标签大体上分为两类,一种是默认标签,一种是自定义标签

 ●  默认标签:就是不用额外导入其他命名空间约束的标签,例如 <bean> 标签

 ●  自定义标签:就是需要额外引入其他命名空间约束,并通过前缀引用的标签,例如      <context:property-placeholder/> 标签

Spring的默认标签用到的是Spring的默认命名空间

该命名空间约束下的默认标签如下: 

标签

作用

<beans>

一般作为 xml 配置根标签,其他标签都是该标签的子标签

<bean>

Bean的配置标签,上面已经详解了,此处不再阐述

<import>

外部资源导入标签

<alias>

指定Bean的别名标签,使用较少

<beans>标签,除了经常用的做为根标签外,还可以嵌套在根标签内,使用profile属性切换开发环境 

可以使用以下两种方式指定被激活的环境:

●  使用命令行动态参数,虚拟机参数位置加载 -Dspring.profiles.active=test

●  使用代码的方式设置环境变量 System.setProperty("spring.profiles.active","test") 

<import>标签,用于导入其他配置文件,项目变大后,就会导致一个配置文件内容过多,可以将一个配置文件根据业务某块进行拆分,拆分后,最终通过<import>标签导入到一个主配置文件中,项目加载主配置文件就连同<import> 导入的文件一并加载了

 <alias> 标签是为某个Bean添加别名,与在<bean> 标签上使用name属性添加别名的方式一样,我们为UserServiceImpl指定四个别名:aaa、bbb、xxx、yyy

断点调试,在beanFactory中维护着一个名为aliasMap的Map<String,String>集合,存储别名和beanName之间的映射关系

Spring的自定义标签需要引入外部的命名空间,并为外部的命名空间指定前缀,使用 <前缀:标签> 形式的标签,称之为自定义标签,自定义标签的解析流程也是 Spring xml扩展点方式之一

 

- Spring 的get方法

方法定义

返回值和参数

Object getBean (String beanName)

根据beanName从容器中获取Bean实例,要求容器中Bean唯一,返回值为Object,需要强转

T getBean (Class type)

根据Class类型从容器中获取Bean实例,要求容器中Bean类型唯一,返回值为Class类型实例,无需强转

T getBean (String beanNameClass type)

根据beanName从容器中获得Bean实例,返回值为Class型实例,无需强转

- Spring 配置非自定义Bean

以上在 xml 中配置的Bean都是自己定义的,例如:UserDaoImpl,UserServiceImpl。但是,在实际开发中有些功能类并不是我们自己定义的,而是使用的第三方jar包中的,那么,这些Bean要想让Spring进行管理,也需要对其进行配置

 配置非自定义的Bean需要考虑如下两个问题:

● 被配置的Bean的实例化方式是什么?无参构造、有参构造、静态工厂方式还是实例工厂方式;

● 被配置的Bean是否需要注入必要属性。

1)配置 Druid 数据源交由Spring管理

   导入Druid坐标

配置 DruidDataSource

2)配置Connection交由Spring管理 

Connection 的产生是通过DriverManager的静态方法getConnection获取的,所以用静态工厂方式配置 

3)配置日期对象交由Spring管理

产生一个指定日期格式的对象,原始代码按如下:

可以看成是实例工厂方式,使用Spring配置方式产生Date实例

4)配置MyBatis的SqlSessionFactory交由Spring管理

导入MyBatis的相关坐标:

 

MyBatis原始获得SqlSessionFactory的方式:

SqlSessionFactory交由Spring管理配置如下:

- Bean 实例化的基本流程

Spring容器在进行初始化时,会将xml配置的<bean>的信息封装成一个BeanDefinition对象,所有的BeanDefinition存储到一个名为beanDefinitionMap的Map集合中去,Spring框架在对该Map进行遍历,使用反射创建Bean实例对象,创建好的Bean对象存储在一个名为singletonObjects的Map集合中,当调用getBean方法时则最终从该Map集合中取出Bean实例对象返回。

Bean信息定义对象-BeanDefinition

DefaultListableBeanFactory对象内部维护着一个Map用于存储封装好的BeanDefinitionMap

Spring框架会取出beanDefinitionMap中的每个BeanDefinition信息,反射构造方法或调用指定的工厂方法生成Bean实例对象,所以只要将BeanDefinition注册到beanDefinitionMap这个Map中,Spring就会进行对应的Bean实例化操作

Bean实例及单例池singletonObjects, beanDefinitionMap中的BeanDefinition会被转化成对应的Bean实例对象,存储到单例池singletonObjects中去,在DefaultListableBeanFactory的上四级父类DefaultSingletonBeanRegistry中,维护着singletonObjects,源码如下:

● 加载xml配置文件,解析获取配置中的每个<bean>的信息,封装成一个个的BeanDefinition对象;

● 将BeanDefinition存储在一个名为beanDefinitionMap的Map<String,BeanDefinition>中;

● ApplicationContext底层遍历beanDefinitionMap,创建Bean实例对象;

● 创建好的Bean实例对象,被存储到一个名为singletonObjects的Map<String,Object>中;

● 当执行applicationContext.getBean(beanName)时,从singletonObjects去匹配Bean实例返回。 

- Spring的后处理器

Spring的后处理器是Spring对外开发的重要扩展点,允许我们介入到Bean的整个实例化流程中来,以达到动态注册BeanDefinition,动态修改BeanDefinition,以及动态修改Bean的作用。Spring主要有两种后处理器

● BeanFactoryPostProcessor:Bean工厂后处理器,在BeanDefinitionMap填充完毕,Bean实例化之前执行;

● BeanPostProcessor:Bean后处理器,一般在Bean实例化之后,填充到单例池singletonObjects之前执行。

Bean工厂后处理器 – BeanFactoryPostProcessor

BeanFactoryPostProcessor是一个接口规范,实现了该接口的类只要交由Spring容器管理的话,那么Spring就会回调该接口的方法,用于对BeanDefinition注册和修改的功能。

BeanFactoryPostProcessor 定义如下:

编写BeanFactoryPostProcessor

配置BeanFactoryPostProcessor

postProcessBeanFactory 参数本质就是 DefaultListableBeanFactory,拿到BeanFactory的引用,自然就可以对beanDefinitionMap中的BeanDefinition进行操作了 ,例如对UserDaoImpl的BeanDefinition进行修改操作

上面已经对指定的BeanDefinition进行了修改操作,下面对BeanDefiition进行注册操作

Spring 提供了一个BeanFactoryPostProcessor的子接口BeanDefinitionRegistryPostProcessor专门用于注册BeanDefinition操作

案例 使用Spring的BeanFactoryPostProcessor扩展点完成自定义注解扫描 

要求如下:

● 自定义@MyComponent注解,使用在类上;

● 使用资料中提供好的包扫描器工具BaseClassScanUtils 完成指定包的类扫描;

● 自定义BeanFactoryPostProcessor完成注解@MyComponent的解析,解析后最终被Spring管理。 

自定义@MyComponent注解,使用在类上

 

 在类上使用@MyComponent

在类上使用@MyComponent

自定义BeanFactoryPostProcessor完成注解解析 

Bean后处理器 – BeanPostProcessor

Bean被实例化后,到最终缓存到名为singletonObjects单例池之前,中间会经过Bean的初始化过程,例如:属性的填充、初始方法init的执行等,其中有一个对外进行扩展的点BeanPostProcessor,我们称为Bean后处理。跟上面的Bean工厂后处理器相似,它也是一个接口,实现了该接口并被容器管理的BeanPostProcessor,会在流程节点上被Spring自动调用。

Bean后处理器 – BeanPostProcessor 

自定义MyBeanPostProcessor,完成快速入门测试

配置MyBeanPostProcessor

测试控制台打印结果如下:

案例: 对Bean方法进行执行时间日志增强

要求如下:

● Bean的方法执行之前控制台打印当前时间;

● Bean的方法执行之后控制台打印当前时间。

分析:

● 对方法进行增强主要就是代理设计模式和包装设计模式;

● 由于Bean方法不确定,所以使用动态代理在运行期间执行增强操作;

● 在Bean实例创建完毕后,进入到单例池之前,使用Proxy代替真是的目标Bean

编写BeanPostProcessor,增强逻辑编写在 after方法中

- Spring Bean的生命周期

Spring Bean的生命周期是从 Bean 实例化之后,即通过反射创建出对象之后,到Bean成为一个完整对象,最终存储到单例池中,这个过程被称为Spring Bean的生命周期。Spring Bean的生命周期大体上分为三个阶段

● Bean的实例化阶段:Spring框架会取出BeanDefinition的信息进行判断当前Bean的范围是否是singleton的,是否不是延迟加载的,是否不是FactoryBean等,最终将一个普通的singleton的Bean通过反射进行实例化;

● Bean的初始化阶段:Bean创建之后还仅仅是个"半成品",还需要对Bean实例的属性进行填充、执行一些Aware接口方法、执行BeanPostProcessor方法、执行InitializingBean接口的初始化方法、执行自定义初始化init方法等。该阶段是Spring最具技术含量和复杂度的阶段,Aop增强功能,后面要学习的Spring的注解功能等、spring高频面试题Bean的循环引用问题都是在这个阶段体现的;

● Bean的完成阶段:经过初始化阶段,Bean就成为了一个完整的Spring Bean,被存储到单例池singletonObjects中去了,即完成了Spring Bean的整个生命周期。

由于Bean的初始化阶段的步骤比较复杂,所以着重研究Bean的初始化阶段

Spring Bean的初始化过程涉及如下几个过程

● Bean实例的属性填充

● Aware接口属性注入

● BeanPostProcessor的before()方法回调

● InitializingBean接口的初始化方法回调

● 自定义初始化方法init回调

●  BeanPostProcessor的after()方法回调

PS:通过代码验证上述初始化顺序. .

Bean实例属性填充

BeanDefinition 中有对当前Bean实体的注入信息通过属性propertyValues进行了存储,例如UserService的属性信息如下:

 

 Spring在进行属性注入时,会分为如下几种情况:

●  注入普通属性,String、int或存储基本类型的集合时,直接通过set方法的反射设置进去;

●  注入单向对象引用属性时,从容器中getBean获取后通过set方法反射设置进去,如果容器中没有,则先创建被注入对象Bean实例(完成整个生命周期)后,在进行注入操作;

●  注入双向对象引用属性时,就比较复杂了,涉及了循环引用(循环依赖)问题,下面会详细阐述解决方案。

PS:通过代码验证上述第二第三种属性填充. .

多个实体之间相互依赖并形成闭环的情况就叫做"循环依赖",也叫做"循环引用" 

代码验证后,分析出UserService与UserDao实例化与初始化的顺序如下:

Spring提供了三级缓存存储 完整Bean实例 半成品Bean实例 ,用于解决循环引用问题

在DefaultListableBeanFactory的上四级父类DefaultSingletonBeanRegistry中提供如下三个Map:

UserService和UserDao循环依赖的过程结合上述三级缓存描述一下

●  UserService 实例化对象,但尚未初始化,将UserService存储到三级缓存;

●  UserService 属性注入,需要UserDao,从缓存中获取,没有UserDao;

●  UserDao实例化对象,但尚未初始化,将UserDao存储到到三级缓存;

●  UserDao属性注入,需要UserService,从三级缓存获取UserService,UserService从三级缓存移入二级缓存;

●  UserDao执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存;

●  UserService 注入UserDao;

●  UserService执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存。

常用的Aware接口

Aware接口是一种框架辅助属性注入的一种思想,其他框架中也可以看到类似的接口。框架具备高度封装性,我们接触到的一般都是业务代码,一个底层功能API不能轻易的获取到,但是这不意味着永远用不到这些对象,如果用到了,就可以使用框架提供的类似Aware的接口,让框架给我们注入该对象。

Aware接口

回调方法

作用

ServletContextAware

setServletContext(ServletContext context)

Spring框架回调方法注入ServletContext对象,web环境下才生效

BeanFactoryAware

setBeanFactory(BeanFactory factory)

Spring框架回调方法注入beanFactory对象

BeanNameAware

setBeanName(String beanName)

Spring框架回调方法注入当前Bean在容器中beanName

ApplicationContextAware

setApplicationContext(ApplicationContext applicationContext)

Spring框架回调方法注入applicationContext

对象

- Spring IoC 整体流程总结

- Spring xml方式整合第三方框架

xml整合第三方框架有两种整合方案:

● 不需要自定义名空间,不需要使用Spring的配置文件配置第三方框架本身内容,如:MyBatis;

● 需要引入第三方框架命名空间,要使用Spring的配置文件配置第三方框架本身内容,如:Dubbo

Spring整合MyBatis,之前已经在Spring中简单的配置了SqlSessionFactory,但是这不是正规的整合方式,MyBatis提供了mybatis-spring.jar专门用于两大框架的整合。

Spring整合MyBatis的步骤如下:

● 导入MyBatis整合Spring的相关坐标;(请见资料中的pom.xml)

● 编写Mapper和Mapper.xml;

● 配置SqlSessionFactoryBean和MapperScannerConfigurer;

● 编写测试代码

Spring整合MyBatis的原理剖析

整合包里提供了一个SqlSessionFactoryBean和一个扫描Mapper的配置对象,SqlSessionFactoryBean一旦被实例化,就开始扫描Mapper并通过动态代理产生Mapper的实现类存储到Spring容器中。相关的有如下四个类:

● SqlSessionFactoryBean:需要进行配置,用于提供SqlSessionFactory;

● MapperScannerConfigurer:需要进行配置,用于扫描指定mapper注册BeanDefinition;

● MapperFactoryBean:Mapper的FactoryBean,获得指定Mapper时调用getObject方法;

● ClassPathMapperScanner:definition.setAutowireMode(2) 修改了自动注入状态,所以MapperFactoryBean中的setSqlSessionFactory会自动注入进去。

Spring 整合其他组件时就不像MyBatis这么简单了,例如Dubbo框架在于Spring进行整合时,要使用Dubbo提供的命名空间的扩展方式,自定义了一些Dubbo的标签

为了降低此处的学习成本,不在引入Dubbo第三方框架了,以Spring的 context 命名空间去进行讲解,该方式也是命名空间扩展方式。

需求:加载外部properties文件,将键值对存储在Spring容器中

原理剖析解析过程,只能从源头ClassPathXmlApplicationContext入手,经历复杂的源码追踪,找到如下两个点:

1)在创建DefaultNamespaceHandlerResolver时,为处理器映射地址handlerMappingsLocation属性赋值,并加载命名空间处理器到Map<String, Object> handlerMappings 中去

 

第一点完成后,Map集合handlerMappings被填充了很多XxxNamespaceHandler,继续往下追代码

2)在DefaultBeanDefinitionDocumentReader的parseBeanDefinitions方法中,发现如下逻辑:

 如果是默认命名空间,则执行parseDefaultElement方法

如果是自定义命名空间,则执行parseCustomElement方法

在执行resovle方法时,就是从Map<String, Object> handlerMappings中根据命名空间名称获得对应的处理器对象,此处是ContextNamespaceHandler,最终执行NamespaceHandler的parse方法

ContextNamespaceHandler源码如下,间接实现了NamespaceHandler接口,初始化方法init会被自动调用。由于context命名空间下有多个标签,所以每个标签又单独注册了对应的解析器,注册到了其父类NamespaceHandlerSupport的Map<String, BeanDefinitionParser> parsers中去了

通过上述分析,清楚的了解了外部命名空间标签的执行流程,如下:

● 将自定义标签的约束 与 物理约束文件与网络约束名称的约束 以键值对形式存储到一个spring.schemas文件里,该文件存储在类加载路径的 META-INF里,Spring会自动加载到;

● 将自定义命名空间的名称 与 自定义命名空间的处理器映射关系 以键值对形式存在到一个叫spring.handlers文件里,该文件存储在类加载路径的 META-INF里,Spring会自动加载到;

● 准备好NamespaceHandler,如果命名空间只有一个标签,那么直接在parse方法中进行解析即可,一般解析结果就是注册该标签对应的BeanDefinition。如果命名空间里有多个标签,那么可以在init方法中为每个标签都注册一个BeanDefinitionParser,在执行NamespaceHandler的parse方法时在分流给不同的BeanDefinitionParser进行解析(重写doParse方法即可)。

案例:设想自己是一名架构师,进行某一个框架与Spring的集成开发,效果是通过一个指示标签,向Spring容器中自动注入一个BeanPostProcessor

步骤分析:

1. 确定命名空间名称、schema虚拟路径、标签名称;

2. 编写schema约束文件haohao-annotation.xsd

3. 在类加载路径下创建META目录,编写约束映射文件spring.schemas和处理器映射文件spring.handlers

4. 编写命名空间处理器 HaohaoNamespaceHandler,在init方法中注册HaohaoBeanDefinitionParser

5. 编写标签的解析器 HaohaoBeanDefinitionParser,在parse方法中注册HaohaoBeanPostProcessor

6. 编写HaohaoBeanPostProcessor

==========以上五步是框架开发者写的,以下是框架使用者写的===========

1. 在applicationContext.xml配置文件中引入命名空间

2. 在applicationContext.xml配置文件中使用自定义的标签

◆ 基于注解的Spring应用

- Bean基本注解开发

Spring除了xml配置文件进行配置之外,还可以使用注解方式进行配置,注解方式慢慢成为xml配置的替代方案。我们有了xml开发的经验,学习注解开发就方便了许多,注解开发更加快捷方便。

Spring提供的注解有三个版本:

● 2.0时代,Spring开始出现注解

● 2.5时代,Spring的Bean配置可以使用注解完成

● 3.0时代,Spring其他配置也可以使用注解完成,我们进入全注解时代

基本Bean注解,主要是使用注解的方式替代原有xml的 <bean> 标签及其标签属性的配置

使用@Component 注解替代<bean>标签

xml配置

注解

描述

<bean id="" class="">

@Component

被该注解标识的类,会在指定扫描范围内被Spring加载并实例化

可以通过@Component注解的value属性指定当前Bean实例的beanName,也可以省略不写,不写的情况下为当前类名首字母小写

使用注解对需要被Spring实例化的Bean进行标注,但是需要告诉Spring去哪找这些Bean,要配置组件扫描路径 

@Component就单纯一个value属性,那么xml配置 <bean> 时那些属性怎么进行配置呢?Spring 是通过注解方式去配置的之前 <bean> 标签中的那些属性,例如:@Scope

使用@Component 注解替代<bean>标签 

xml配置

注解

描述

<bean scope="">

@Scope

在类上或使用了@Bean标注的方法上,标注Bean的作用范围,取值为singleton或prototype

<bean lazy-init="">

@Lazy

在类上或使用了@Bean标注的方法上,标注Bean是否延迟加载,取值为true和false

<bean init-method="">

@PostConstruct

在方法上使用,标注Bean的实例化后执行的方法

<bean destroy-method="">

@PreDestroy

在方法上使用,标注Bean的销毁前执行方法

使用上述注解完成UserDaoImpl的基本配置 

由于JavaEE开发是分层的,为了每层Bean标识的注解语义化更加明确,@Component又衍生出如下三个注解:

@Component衍生注解

描述

@Repository

Dao层类上使用

@Service

Service类上使用

@Controller

Web层类上使用

- Bean依赖注入注解开发

Bean依赖注入的注解,主要是使用注解的方式替代xml的 <property> 标签完成属性的注入操作

Spring主要提供如下注解,用于在Bean内部进行属性注入的:

属性注入注解

描述

@Value

使用在字段或方法上,用于注入普通数据

@Autowired

使用在字段或方法上,用于根据类型(byType)注入引用数据

@Qualifier

使用在字段或方法上,结合@Autowired,根据名称注入

@Resource

使用在字段或方法上,根据类型或名称进行注入

通过@Value 直接注入普通属性

通过@Value 注入properties文件中的属性

加载properties文件

@Autowired注解,用于根据类型进行注入

当容器中同一类型的Bean实例有多个时,会尝试自动根据名字进行匹配:

@Qualifier配合@Autowired可以完成根据名称注入Bean实例,使用@Qualifier指定名称

@Resource注解既可以根据类型注入,也可以根据名称注入,无参就是根据类型注入,有参数就是根据名称注入 

PS:@Resource注解存在与 javax.annotation 包中,Spring对其进行了解析

- 非自定义Bean注解开发

非自定义Bean不能像自定义Bean一样使用@Component进行管理,非自定义Bean要通过工厂的方式进行实例化,使用@Bean标注方法即可,@Bean的属性为beanName,如不指定为当前工厂方法名称

PS:工厂方法所在类必须要被Spring管理

如果@Bean工厂方法需要参数的话,则有如下几种注入方式:

● 使用@Autowired 根据类型自动进行Bean的匹配,@Autowired可以省略 ;

● 使用@Qualifier 根据名称进行Bean的匹配;

● 使用@Value 根据名称进行普通数据类型匹配。

- Bean配置类的注解开发

@Component等注解替代了<bean>标签,但是像<import>、<context:componentScan> 等非<bean> 标签怎样去使用注解替代呢?

定义一个配置类替代原有的xml配置文件,<bean>标签以外的标签,一般都是在配置类上使用注解完成的

@Configuration注解标识的类为配置类,替代原有xml配置文件,该注解第一个作用是标识该类是一个配置类,第二个作用是具备@Component作用

@ComponentScan 组件扫描配置,替代原有xml文件中的

<context:component-scan  base-package=""/>

base-package的配置方式:

● 指定一个或多个包名:扫描指定包及其子包下使用注解的类

● 不配置包名:扫描当前@componentScan注解配置类所在包及其子包下的类

@PropertySource 注解用于加载外部properties资源配置,替代原有xml中的

<context:property-placeholder location=“”/> 配置

@Import 用于加载其他配置类,替代原有xml中的<import  resource=“classpath:beans.xml”/>配置

- Spring 配置其他注解

扩展:@Primary注解用于标注相同类型的Bean优先被使用权,@Primary 是Spring3.0引入的,与@Component和@Bean一起使用,标注该Bean的优先级更高,则在通过类型获取Bean或通过@Autowired根据类型进行注入时,会选用优先级更高的

扩展:@Profile 注解的作用同于xml配置时学习profile属性,是进行环境切换使用的

注解 @Profile 标注在类或方法上,标注当前产生的Bean从属于哪个环境,只有激活了当前环境,被标注的Bean才能被注册到Spring容器里,不指定环境的Bean,任何环境下都能注册到Spring容器里

可以使用以下两种方式指定被激活的环境:

● 使用命令行动态参数,虚拟机参数位置加载 -Dspring.profiles.active=test

● 使用代码的方式设置环境变量 System.setProperty("spring.profiles.active","test");

- Spring注解的解析原理

使用@Component等注解配置完毕后,要配置组件扫描才能使注解生效

● xml配置组件扫描:

● 配置类配置组件扫描:

使用xml方式配置组件扫描,而component-scan是一个context命名空间下的自定义标签,所以要找到对应的命名空间处理器NamespaceHandler 和 解析器,查看spring-context包下的spring.handlers文件

查看 ContextNamespaceHandler 类

将ComponentScanBeanDefinitionParser进行了注册,对其源码进行跟踪,最终将标注的@Component的类,生成对应的BeanDefiition进行了注册

使用配置类配置组件扫描,使用AnnotationConfigApplicationContext容器在进行创建时,内部调用了如下代码,该工具注册了几个Bean后处理器:

- Spring注解方式整合第三方框架

第三方框架整合,依然使用MyBatis作为整合对象,之前我们已经使用xml方式整合了MyBatis,现在使用注解方式无非就是将xml标签替换为注解,将xml配置文件替换为配置类而已,原有xml方式整合配置如下:

使用@Bean将DataSource和SqlSessionFactoryBean存储到Spring容器中,而MapperScannerConfigurer使用注解@MapperScan进行指明需要扫描的Mapper在哪个包下,使用注解整合MyBatis配置方式如下:

 注解方式,Spring整合MyBatis的原理,关键在于@MapperScan,@MapperScan不是Spring提供的注解,是MyBatis为了整合Spring,在整合包org.mybatis.spring.annotation中提供的注解,源码如下: 

重点关注一下@Import({MapperScannerRegistrar.class}),当@MapperScan被扫描加载时,会解析@Import注解,从而加载指定的类,此处就是加载了MapperScannerRegistrar

MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar接口,Spring会自动调用
registerBeanDefinitions方法,该方法中又注册MapperScannerConfigurer类,而MapperScannerConfigurer类作用是扫描Mapper,向容器中注册Mapper对应的MapperFactoryBean

Spring与MyBatis注解方式整合有个重要的技术点就是@Import,第三方框架与Spring整合xml方式很多是凭借自定义标签完成的,而第三方框架与Spring整合注解方式很多是靠@Import注解完成的。

@Import可以导入如下三种类:

● 普通的配置类

● 实现ImportSelector接口的类

● 实现ImportBeanDefinitionRegistrar接口的类

@Import导入实现了ImportSelector接口的类

ImportSelector接口selectImports方法的参数AnnotationMetadata代表注解的媒体数据,可以获得当前注解修饰的类的元信息,例如:获得组件扫描的包名

@Import导入实现ImportBeanDefinitionRegistrar接口的类,实现了该接口的类的registerBeanDefinitions方法会被自动调用,在该方法内可以注册BeanDefinition

来源:黑马程序员新版Spring零基础入门到精通,一套搞定spring全套视频教程(含实战源码) 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/140250.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

11-13 周一 同济子豪兄CNN卷积神经网络学习记录

11-13 周一 同济子豪兄CNN卷积神经网络学习记录 时间版本修改人描述2023年11月13日14:02:14V0.1宋全恒新建文档2023年11月13日19:05:29V0.2宋全恒完成 大白话讲解卷积神经网络的学习 简介 为了深入理解CNN&#xff0c;进行B站 同济子豪兄深度学习之卷积神经网络的学习. 主要内…

如何在电脑和手机设备上编辑只读 PDF

我们大多数人更喜欢以 PDF 格式共享和查看文件&#xff0c;因为它更专业、更便携。但是&#xff0c;通常情况下您被拒绝访问除查看之外的内容编辑、复制或评论。如果您希望更好地控制您的 PDF 或更灵活地编辑它&#xff0c;请弄清楚为什么您的 PDF 是只读的&#xff0c;然后使用…

NLP在网安领域中的应用(初级)

NLP在网安领域的应用 写在最前面1. 威胁情报分析1.1 社交媒体情报分析&#xff08;后面有详细叙述&#xff09;1.2 暗网监测与威胁漏洞挖掘 2. 恶意软件检测2.1 威胁预测与趋势分析 3. 漏洞管理和响应4. 社交工程攻击识别4.1 情感分析与实时监测4.2 实体识别与攻击者画像构建4.…

网络编程 初探windows编程

目录 一、什么是Winodws编程 二、开发环境搭建以及如何学习 三、VA助手安装 四、第一个Win32程序 五、窗口类句柄/窗口类对象 六、Winodws消息循环机制 七、Windows数据类型 一、什么是Winodws编程 Windows 编程指的是在 Microsoft Windows 操作系统上进行软件开发的过…

Spring IOC - BeanDefinition解析

1. BeanDefinition的属性 BeanDefinition作为接口定义了属性的get、set方法。这些属性基本定义在其直接实现类AbstractBeanDefinition中&#xff0c;各属性的含义如下表所示&#xff1a; 类型 名称 含义 常量 SCOPE_DEFAULT 默认作用域&#xff1a;单例模式 AUT…

《Semantics Disentangling for Generalized Zero-Shot Learning》论文解读

其实除了VAE自监督外&#xff0c;引入语义属性来约束生成特征的质量。感觉解纠缠这个说法有点扯淡。 语义相关模块的作用 其实就是把语义属性也作为标签。除了VAE外&#xff0c;加了语义属性作为标签&#xff0c;这样生成的特征就可以即跟原始视觉特征对齐&#xff0c;又跟语…

大数据治理运营整体解决方案:PPT全文39页,附下载

关键词&#xff1a;大数据&#xff0c;数据治理&#xff0c;数据治理解决方案&#xff0c;数据治理的目的和意义 一、数据治理的定义 数据治理专注于将数据作为企事业单位数据资产进行应用和管理的一套管理机制&#xff0c;能够消除数据的不一致性&#xff0c;建立规范的数据应…

5年测试经验之谈:2年功能测试、3年自动化测试,从入门到25k...

毕业3年了&#xff0c;学的是环境工程专业&#xff0c;毕业后零基础转行做软件测试。 已近从事测试行业8年了&#xff0c;自己也从事过2年的手工测试&#xff0c;从事期间越来越觉得如果一直在手工测试的道路上前进&#xff0c;并不会有很大的发展&#xff0c;所以通过自己的努…

Ansible角色定制实例

目录 角色定制&#xff1a;roles 角色定制实例&#xff1a;利用角色部署wordpress 1.在roles目录下生成对应的目录结构 2.定义配置文件 ①nginx ②php ③mysql ④定义剧本文件 ⑤启动服务 角色定制&#xff1a;roles 对于普通的剧本&#xff08;playbook&#xff09;有…

web前端开发第3次Dreamweave课堂练习/html练习代码《网页设计语言基础练习案例》

目标图片&#xff1a; 文字素材&#xff1a; 网页设计语言基础练习案例 ——几个从语义上和文字相关的标签 * h标签&#xff08;h1~h6&#xff09;&#xff1a;用来定义网页的标题&#xff0c;成对出现。 * p标签&#xff1a;用来设置网页的段落&#xff0c;成对出现。 * b…

Linux下的文件系统

文章目录 一、初始文件系统 二、理解磁盘文件 三、了解磁盘 四、深入理解磁盘文件 4、1 磁盘分区 4、2 分区存储细节 4、3 磁盘文件的操作 4、3、1 查找某个磁盘文件 4、3、2 创建文件 4、3、3 对文件进行写入 4、3、4 删除文件 4、4 再次理解文件权限 &#x1f64b;‍♂️ 作…

Spring 重点知识总结(三)——AOP

前言 基于黑马《SSM框架教程》的学习笔记&#xff0c;附链接&#xff1a;SSM框架教程 1 AOP 核心概念 AOP :&#xff08;Aspect Oriented Programming&#xff09;面向切面编程&#xff0c;是一种编程范式&#xff0c;负责指导开发者组织程序结构 同 OOP &#xff08;Object …

AI图像生成模型LCMs: 四个步骤就能快速生成高质量图像的新方法

在最新的AI模型和研究领域&#xff0c;一种名为Latent Consistency Models&#xff08;LCMs&#xff09;的新技术正迅速推动文本到图像人工智能的发展。与传统的Latent Diffusion Models(LDMs)相比&#xff0c;LCMs在生成详细且富有创意的图像方面同样出色&#xff0c;但仅需1-…

python打包部署脚本

linux可使用expect来实现自动交互&#xff0c;windows想要写出同样的功能脚本&#xff0c;只能使用python或者安装ActiveTcl 1、安装python Microsoft Store搜索python直接安装&#xff0c;默认会直接添加到环境变量https://www.python.org/官网下载&#xff0c;点击安装时会提…

初识MySQL(一)(创建数据库,查看删除数据库,备份和恢复,创建表,数据类型)

目录 客户端 ------> MySQL服务(3306) 数据库三层结构-破除MySQL神秘 创建数据库 查看删除数据库 备份恢复数据库 创建表 MySQL常用的数据类型(列类型) ​编辑 ​编辑整型 bit类型 小数类型 字符串类型 日期类型的使用 客户端 ------> MySQL服务(3306) 连接…

【斗破年番】毒宗决战万蝎门,小医仙惨遭背叛,斗宗巅峰蝎毕岩

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析国漫资讯。 深度爆料斗破年番第七十话剧情解析&#xff0c;万蝎门宗主蝎毕岩&#xff0c;也算是这片大陆上的一小小块势力。一年前&#xff0c;他曾与小医仙正面交锋&#xff0c;即使当时的蝎毕岩已经站在了四星斗宗&…

2023年【广东省安全员B证第四批(项目负责人)】考试及广东省安全员B证第四批(项目负责人)试题及解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 广东省安全员B证第四批&#xff08;项目负责人&#xff09;考试考前必练&#xff01;安全生产模拟考试一点通每个月更新广东省安全员B证第四批&#xff08;项目负责人&#xff09;试题及解析题目及答案&#xff01;多…

若依系统富文本框上传图片报错!

报错如下&#xff1a; 原因&#xff1a;如图&#xff0c;富文本路径中存在 / 字符&#xff0c;导致上传出错。 解决方案&#xff1a;将富文本框内容在前端进行加密&#xff0c;后端再解密。 前端&#xff1a; 安装 crypto-js 插件 npm install crypto-js 创建工具类 :在 sr…

阿里云国际站:应用实时监控服务

文章目录 一、阿里云应用实时监控服务的概念 二、阿里云应用实时监控服务的优势 三、阿里云应用实时监控服务的功能 四、写在最后 一、阿里云应用实时监控服务的概念 应用实时监控服务 (Application Real-Time Monitoring Service) 作为一款云原生可观测产品平台&#xff…

微服务拆分的一些基本原则

文章首发公众号&#xff1a;海天二路搬砖工 单一职责原则 什么是单一职责原则 单一职责原则原本是面向对象设计中的一个基本原则&#xff0c;它指的是一个类只负责一项职责&#xff0c;不要存在多于一个导致类变更的原因。 在微服务架构中&#xff0c;一个微服务也应该只负…