在现实生活中,框架可以比喻为我们搭建房子的框架。
在框架的基础上,我们可以专注于我们自己的工作,而不用在意这些底层工作如何实现。
框架的优点包括以下几点:
1. 提高开发效率:框架提供了许多预先设计好了的组件和工具,能够帮助开发人员快速进行开发。相较于传统手写代码,在框架提供的规范化环境中,开发者可以更快地实现项目的各种要求。
2. 降低开发成本:框架的提供标准化的编程语言、数据操作等代码片段,避免了重复开发的问题,降低了开发成本,提供深度优化的系统,降低了维护成本,增强了系统的可靠性。
3. 提高应用程序的稳定性:框架通常经过了很长时间的开发和测试,其中的许多组件、代码片段和设计模式都得到了验证。重复利用这些组件有助于减少bug的出现,从而提高了应用程序的稳定性。
4. 提供标准化的解决方案:框架通常是针对某个特定领域的,通过提供标准化的解决方案,可以为开发人员提供一种共同的语言和思想基础,有助于更好地沟通和协作。
框架的缺点包括以下几个方面:
1. 学习成本高:框架通常具有特定的语言和编程范式。对于开发人员而言,需要花费时间学习其背后的架构、模式和逻辑,这对于新手而言可能会耗费较长时间。
2. 可能存在局限性:虽然框架提高了开发效率并可以帮助开发人员解决常见问题,但是在某些情况下,特定的应用需求可能超出框架的范围,从而导致应用程序无法满足要求。开发人员可能需要更多的控制权和自由度,同时需要在框架和应用程序之间进行权衡取舍。
3. 版本变更和兼容性问题:框架的版本发布和迭代通常会导致代码库的大规模变更,进而导致应用程序出现兼容性问题和漏洞。当框架变更时,需要考虑框架是否向下兼容,以及如何进行适当的测试、迁移和升级。
4. 架构风险:框架涉及到很多抽象和概念,如果开发者没有足够的理解和掌握其架构,可能会导致系统出现设计和架构缺陷,从而影响系统的健康性和安全性。
站在文件结构的角度理解框架,可以将框架总结:框架 = jar包+配置文件
对于java框架,其实就是提前写好的jar包,将底层的代码实现封装,只对我们提供一个接口API的使用,但是框架也要包含配置文件,即我们可以动态去配置一些内容,而不是只可以简单的使用其api。
广义上的 Spring 泛指以 Spring Framework 为基础的 Spring 技术栈。
经过十多年的发展,Spring 已经不再是一个单纯的应用框架,而是逐渐发展成为一个由多个不同子项目(模块)组成的成熟技术,例如 Spring Framework、Spring MVC、SpringBoot、Spring Cloud、Spring Data、Spring Security 等,其中 Spring Framework 是其他子项目的基础。
这些子项目涵盖了从企业级应用开发到云计算等各方面的内容,能够帮助开发人员解决软件发展过程中不断产生的各种实际问题,给开发人员带来了更好的开发体验。
狭义的 Spring 特指 Spring Framework,通常我们将它称为 Spring 框架。
Spring Framework(Spring框架)是一个开源的应用程序框架,由SpringSource公司开发,最初是为了解决企业级开发中各种常见问题而创建的。它提供了很多功能,例如:依赖注入(Dependency Injection)、面向切面编程(AOP)、声明式事务管理(TX)等。其主要目标是使企业级应用程序的开发变得更加简单和快速,并且Spring框架被广泛应用于Java企业开发领域。
Spring全家桶的其他框架都是以SpringFramework框架为基础!
框架结构图:
组件其实就是一个可以重复的对象,组件一定是对象,但对象不一定是容器(因为有的对象使用一次就被销毁了)。
回顾常规的三层架构处理请求流程:整个项目其实就是有多个组件搭建而成的。
只不过在没有Spring容器时,我们需要自己去实例化组件对象,并为其赋值以及一些其它操作。有了Spring,就可以大大简化我们的操作。
组件可以完全交给Spring 框架进行管理,Spring框架替代了程序员原有的new对象和对象属性赋值动作等!
Spring具体的组件管理动作包含:
- 组件对象实例化
- 组件属性属性赋值
- 组件对象之间引用
- 组件对象存活周期管理
- ......
我们只需要编写元数据(配置文件)告知Spring 管理哪些类组件和他们的关系即可!
注意:组件是映射到应用程序中所有可重用组件的Java对象,应该是可复用的功能对象!
- 组件一定是对象
- 对象不一定是组件
综上所述,Spring 充当一个组件容器,创建、管理、存储组件,减少了我们的编码压力,让我们更加专注进行业务编写!
1. 降低了组件之间的耦合性:Spring IoC容器通过依赖注入机制,将组件之间的依赖关系削弱,减少了程序组件之间的耦合性,使得组件更加松散地耦合。
2. 提高了代码的可重用性和可维护性:将组件的实例化过程、依赖关系的管理等功能交给Spring IoC容器处理,使得组件代码更加模块化、可重用、更易于维护。
3. 方便了配置和管理:Spring IoC容器通过XML文件或者注解,轻松的对组件进行配置和管理,使得组件的切换、替换等操作更加的方便和快捷。
4. 交给Spring管理的对象(组件),方可享受Spring框架的其他功能(AOP,声明事务管理)等。
java代码中也会有自己的组件,但是java代码中的组件并不能享受Spring框架的其它功能。而交给Spring管理的组件,可以享用。
普通容器 :
只有存储功能,比如 list,set等。像我们日常生活中的杯子一样,只能装水,并没有其它复杂功能。
复杂容器:
除了存储,还有管理组件周期等其它复杂功能。可以用我们日常生活中的政府来举例,我们一生都要在政府的规定下做事,政府管理我们的一生,比如出生的出生证明....
Spring IoC 容器,负责实例化、配置和组装 bean(组件)。容器通过读取配置元数据来获取有关要实例化、配置和组装组件的指令。配置元数据以 XML、Java 注解或 Java 代码形式表现。它允许表达组成应用程序的组件以及这些组件之间丰富的相互依赖关系。
有了Spring IOC容器,我们只需要编写配置元数据以及获取组件。
那我们获取组件的容器的具体接口是什么呢?就是通过哪个API来获取 Spring为我们管理的组件呢?
SpringIoc容器接口:
`BeanFactory` 接口提供了一种高级配置机制,能够管理任何类型的对象,它是SpringIoC容器标准化超接口!
`ApplicationContext` 是 `BeanFactory` 的子接口。它扩展了以下功能:
- 更容易与 Spring 的 AOP 功能集成
- 消息资源处理(用于国际化)
- 特定于应用程序给予此接口实现,例如Web 应用程序的 `WebApplicationContext`
简而言之, `BeanFactory` 提供了配置框架和基本功能,而 `ApplicationContext` 添加了更多特定于企业的功能。 `ApplicationContext` 是 `BeanFactory` 的完整超集!
ApplicationContext容器实现类:
容器接口只是一些规范,但并没有具体实现,我们需要通过容器的具体实现类才能获取我们的组件。
以下列出的并不是全部,而是一些我们经常使用的。通过配置文件的格式以及项目属性的不同有不同的容器实现类接口。通过该容器实现类,我们可以通过配置文件来获取具体的Spring容器,从从而来更好更简便的管理我们的组件。
Spring框架提供了多种配置方式:XML配置方式、注解方式和Java配置类方式
1. XML配置方式:是Spring框架最早的配置方式之一,通过在XML文件中定义Bean及其依赖关系、Bean的作用域等信息,让Spring IoC容器来管理Bean之间的依赖关系。该方式从Spring框架的第一版开始提供支持。
2. 注解方式:从Spring 2.5版本开始提供支持,可以通过在Bean类上使用注解来代替XML配置文件中的配置信息。通过在Bean类上加上相应的注解(如@Component, @Service, @Autowired等),将Bean注册到Spring IoC容器中,这样Spring IoC容器就可以管理这些Bean之间的依赖关系。
3. Java配置类方式:从Spring 3.0版本开始提供支持,通过Java类来定义Bean、Bean之间的依赖关系和配置信息,从而代替XML配置文件的方式。Java配置类是一种使用Java编写配置信息的方式,通过@Configuration、@Bean等注解来实现Bean和依赖关系的配置。
Spring IoC 容器,负责实例化、配置和组装 bean(组件)核心容器。容器通过读取配置元数据来获取有关要实例化、配置和组装组件的指令。
IoC 主要是针对对象的创建和调用控制而言的,也就是说,当应用程序需要使用一个对象时,不再是应用程序直接创建该对象,而是由 IoC 容器来创建和管理,即控制权由应用程序转移到 IoC 容器中,也就是“反转”了控制权。这种方式基本上是通过依赖查找的方式来实现的,即 IoC 容器维护着构成应用程序的对象,并负责创建这些对象。
DI 是指在组件之间传递依赖关系的过程中,将依赖关系在容器内部进行处理,这样就不必在应用程序代码中硬编码对象之间的依赖关系,实现了对象之间的解耦合。在 Spring 中,DI 是通过 XML 配置文件或注解的方式实现的。它提供了三种形式的依赖注入:构造函数注入、Setter 方法注入和接口注入。
我们总结下,组件交给Spring IoC容器管理,并且获取和使用的基本步骤!
1.配置元数据(配置)
配置元数据,既是编写交给SpringIoC容器管理组件的信息,配置方式有三种。
基于 XML 的配置元数据的基本结构:
- "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
- https://www.springframework.org/schema/beans/spring-beans.xsd">
-
- <bean id="..." [1] class="..." [2]>
-
- bean>
-
- <bean id="..." class="...">
-
- bean>
-
- beans>
Spring IoC 容器管理一个或多个组件。这些 组件是使用你提供给容器的配置元数据(例如,以 XML `
- `id` 属性是标识单个 Bean 定义的字符串。
- `class` 属性定义 Bean 的类型并使用完全限定的类名。
2.实例IOC容器
提供给 `ApplicationContext` 构造函数的位置路径是资源字符串地址,允许容器从各种外部资源(如本地文件系统、Java `CLASSPATH` 等)加载配置元数据。
我们应该选择一个合适的容器实现类,进行IoC容器的实例化工作:
- //实例化ioc容器,读取外部配置文件,最终会在容器内进行ioc和di动作
- ApplicationContext context =
- new ClassPathXmlApplicationContext("services.xml", "daos.xml");
3、获取Bean(组件)
`ApplicationContext` 是一个高级工厂的接口,能够维护不同 bean 及其依赖项的注册表。通过使用方法 `T getBean(String name, Class
允许读取 Bean 定义并访问它们,如以下示例所示:
- //创建ioc容器对象,指定配置文件,ioc也开始实例组件对象
- ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
- //获取ioc容器的组件对象
- PetStoreService service = context.getBean("petStore", PetStoreService.class);
- //使用组件对象
- List<String> userList = service.getUsernameList();
Spring IoC 容器管理一个或多个 bean。这些 Bean 是使用您提供给容器的配置元数据创建的(例如,以 XML `
我们学习,如何通过定义XML配置文件,声明组件类信息,交给 Spring 的 IoC 容器进行组件管理!
一般我们实例化对象的方法有两大类,通过构造函数(包括无参数构造和有参数构造),通过工厂方法(静态工厂和实例化工厂)。在这里我们不讲解有参数构造的实例化,因为有参数构造就相当于DI,即依赖注入,我们等到讲解DI时在讲该构造方法。在这里我们主要演示其它三种。
前提:
创建一个Maven工程
导入相关依赖
- <dependencies>
- <!--spring context依赖-->
- <!--当你引入Spring Context依赖之后,表示将Spring的基础依赖引入了-->
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-context</artifactId>
- <version>6.0.6</version>
- </dependency>
- <!--junit5测试-->
- <dependency>
- <groupId>org.junit.jupiter</groupId>
- <artifactId>junit-jupiter-api</artifactId>
- <version>5.3.1</version>
- </dependency>
- </dependencies>
(1)基于无参数的构造函数
当通过构造函数方法创建一个 bean(组件对象) 时,所有普通类都可以由 Spring 使用并与之兼容。也就是说,正在开发的类不需要实现任何特定的接口或以特定的方式进行编码。只需指定 Bean 类信息就足够了。但是,默认情况下,我们需要一个默认(空)构造函数。
a. 准备组件类
b.创建一个spring 的xml配置文件
c.编写配置文件 创建组件
<bean id="happy" class="com_cky.Happy"></bean>
id 是我们组件的唯一标识
class 是组件类的全限定符
注意:要求当前组件类必须包含无参数构造函数!
(2)基于静态工厂方法实例化
a.编写组件信息
- package com_cky;
-
- public class Sad {
- private static Sad sad=new Sad();
- private Sad(){};
- public static Sad CreateInstance(){
- return sad;
- }
- }
b. xml配置文件编写
<bean id="sad" class="com_cky.Sad" factory-method="CreateInstance"></bean>
- class属性:指定工厂类的全限定符!
- factory-method: 指定静态工厂方法,注意,该方法必须是static方法。
(3)基于实例工厂实例化
a. 编写组件类。
package com_cky; public class Grateful { private Grateful grateful=new Grateful(); public Grateful crateGra(){ return grateful; } }
b.配置XML文件
- <bean id="factory" class="com_cky.Grateful" >bean>
- <bean id="grateful" factory-bean="factory" factory-method="crateGra">bean>
首先对工厂类进行IOC配置
接着根据工厂对象的实例工厂方法实例化组件对象
- factory-bean属性:指定当前容器中工厂Bean 的名称。
- factory-method: 指定实例工厂方法名。注意,
实例方法必须是非static的!
通过配置文件,实现IoC容器中Bean之间的引用(依赖注入DI配置)。
主要涉及注入场景:基于构造函数的依赖注入和基于 Setter 的依赖注入。
(1)基于构造函数的依赖注入(单个构造参数)
a.编写组件信息
package demo01; public class UserServe { }package demo01; public class UserDao { private UserServe userServe; public UserDao(UserServe userServe){ this.userServe=userServe; } }
b. 编写xml信息
constructor-arg标签:可以引用构造参数。 ref引用其他bean的标识。
(2)基于构造函数的依赖注入(多个构造参数)
a.编写组件类
package demo01; public class UserBook { private UserServe userServe; private String s; public UserBook(String s,UserServe userServe){ this.s=s; this.userServe=userServe; } }
b.编写xml配置类信息
- constructor-arg标签:指定构造参数和对应的值
- constructor-arg标签:name属性指定参数名、index属性指定参数角标、value属性指定普通属性值 。
(3)基于setter方法注入
a.准备组件类
package demo02; public class MovieUser { }package demo02; public class MovieSerive { private MovieUser movieuser; private String movieName; public void setMovieSerive(MovieUser movieuser){ this.movieuser=movieuser; } public void setMovieName(String s){ this.movieName=s; }}
b.编写xml配置
- property标签: 可以给setter方法对应的属性赋值
- property 标签: name属性代表**set方法标识**、ref代表引用bean的标识id、value属性代表基本属性值 。name是去除setter方法去除set后第一个字母变小写的名字。
首先我们需要有自己的组件,编写配置信息,之后再去创建ioc容器,通过容器去读取我们的组件信息。
1、容器实例化
//方式1:实例化并且指定配置文件
//参数:String...locations 传入一个或者多个配置文件
ApplicationContext context =
new ClassPathXmlApplicationContext("services.xml", "daos.xml");
//方式2:先实例化,再指定配置文件,最后刷新容器触发Bean实例化动作 [springmvc源码和contextLoadListener源码方式]
ApplicationContext context =
new ClassPathXmlApplicationContext();
//设置配置配置文件,方法参数为可变参数,可以设置一个或者多个配置
iocContainer1.setConfigLocations("services.xml", "daos.xml");
//后配置的文件,需要调用refresh方法,触发刷新配置
iocContainer1.refresh();
2、Bean对象读取
//方式1: 根据id获取
//没有指定类型,返回为Object,需要类型转化!
HappyComponent happyComponent =
(HappyComponent) iocContainer.getBean("bean的id标识");
//使用组件对象
happyComponent.doWork();//方式2: 根据类型获取
//根据类型获取,但是要求,同类型(当前类,或者之类,或者接口的实现类)只能有一个对象交给IoC容器管理
//配置两个或者以上出现: org.springframework.beans.factory.NoUniqueBeanDefinitionException 问题
HappyComponent happyComponent = iocContainer.getBean(HappyComponent.class);
happyComponent.doWork();//方式3: 根据id和类型获取
HappyComponent happyComponent = iocContainer.getBean("bean的id标识", HappyComponent.class);
happyComponent.doWork();根据类型来获取bean时,在满足bean唯一性的前提下,其实只是看:『对象 instanceof 指定的类型』的返回结果,
只要返回的是true就可以认定为和类型匹配,能够获取到。
注意:xml配置的组件必须是实现类,不能是接口类。因为我们在代码中需要去实例化该组件。
1. 周期方法概念
我们可以在组件类中定义方法,然后当IoC容器实例化和销毁组件对象的时候进行调用!这两个方法我们成为生命周期方法!
类似于Servlet的init/destroy方法,我们可以在周期方法完成初始化和释放资源等工作。
2.周期方法声明:
a.编写组件类信息:
package demo01;
public class UserServe {
public void doWork() {
System.out.println("HappyComponent.doWork");
}
周期方法要求: 方法命名随意,但是要求方法必须是 public void 无形参列表
public void init(){
System.out.println("init");
}
public void clear(){
System.out.println("destory");
}
}
b.编写配置类信息
init-method="init" destroy-method="clear">
c.进行测试
public void text_04(){ //容器实例创建时,就会调用组件的初始化方法。即在refresh这一步,组件就会进行实例化。 ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("demo02.xml"); UserServe userServe=context.getBean("userserve",UserServe.class); userServe.doWork(); //销毁组件 context.close(); }
1、概念
` 在IoC容器中,这些` 这意味着,`BeanDefinition`与`类`概念一样,SpringIoC容器可以可以根据`BeanDefinition`对象反射创建多个Bean对象实例。 具体创建多少个Bean的实例对象,由Bean的作用域Scope属性指定! 2、作用域可选值 3.作用域配置 配置scope范围 测试 //最后输出的是False,因为我们使用的是多例而不是单例。 1、FactoryBean简介 FactoryBean是一个接口,需要我们实现它的抽象方法。 用于配置复杂的Bean对象,可以将创建过程存储在`FactoryBean` 的getObject方法! `FactoryBean - `T getObject()`: 返回此工厂创建的对象的实例。该返回值会被存储到IoC容器! 如果此 `FactoryBean` 返回单例,则返回 `true` ,否则返回 `false` 。此方法的默认实现返回 `true` (注意,lombok插件使用,可能影响效果)。 在实现FactoryBean过程中,一般而言我们只需实现它的 getObject 方法和getObjectType方法即可。 其中最主要的是getObject 方法,我们在该方法中进行复杂对象的配置。 2.FactoryBean 使用场景 1. 代理类的创建 3、应用演示 a.配置组件类 b. 配置xml文件 c.演示 4、FactoryBean与BeanFactory的区别 FactoryBean是一个组件接口,是 Spring 中一种特殊的 bean,可以在 getObject() 工厂方法自定义的逻辑创建Bean!是一种能够生产其他 Bean 的 Bean。FactoryBean 在容器启动时被创建,而在实际使用时则是通过调用 getObject() 方法来得到其所生产的 Bean。因此,FactoryBean 可以自定义任何所需的初始化逻辑,生产出一些定制化的 bean。 一般情况下,整合第三方框架,都是通过定义FactoryBean实现!!! BeanFactory是IOC容器的最大接口,是 Spring 框架的基础,其作为一个顶级接口定义了容器的基本行为,例如管理 bean 的生命周期、配置文件的加载和解析、bean 的装配和依赖注入等。BeanFactory 接口提供了访问 bean 的方式,例如 getBean() 方法获取指定的 bean 实例。它可以从不同的来源(例如 Mysql 数据库、XML 文件、Java 配置类等)获取 bean 定义,并将其转换为 bean 实例。同时,BeanFactory 还包含很多子类(例如,ApplicationContext 接口)提供了额外的强大功能。 总的来说,FactoryBean 和 BeanFactory 的区别主要在于前者是用于创建 bean 的接口,它提供了更加灵活的初始化定制功能,而后者是用于管理 bean 的框架基础接口,提供了基本的容器功能和 bean 生命周期管理。
//默认是单例,singleton 即我们在容器实例化是就会创建一个对象,且每次GetBean都是这一个
在这里我们使用多例,即每次是在getBean时才实例化,这种方式下,每次获取Bean都不是同一个对象。
public void text_05(){
ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("demo02.xml");
UserServe userServe=context.getBean("userserve",UserServe.class);
UserServe userServe1=context.getBean("userserve",UserServe.class);
System.out.println(userServe1==userServe);
}
4.2.6 FactoryBean 特性和使用
- `boolean isSingleton()`:
- `Class> getObjectType()`: 返回 `getObject()` 方法返回的对象类型,如果事先不知道类型,则返回 `null` 。
2. 第三方框架整合
3. 复杂对象实例化等
package demo03;
public class JavaBean {
private String name;
public void setName(String name){
this.name=name;
}
public String getName(){
return name;
}
}
package demo03;
import org.springframework.beans.factory.FactoryBean;
public class JavaBeanFactoryBean implements FactoryBean
@Test
public void text_06(){
ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("demo03.xml");
//注意: 直接根据声明FactoryBean的id,获取的是getObject方法返回的对象
JavaBean javaBean=context.getBean("javabean", JavaBean.class);
System.out.println(javaBean);
//如果想要获取FactoryBean对象, 直接在id前添加&符号即可! &id 这是一种固定的约束
JavaBeanFactoryBean javabeanFactoryBean=context.getBean("&javabean",JavaBeanFactoryBean.class);
System.out.println(javabeanFactoryBean);
System.out.println(javaBean.getName());
}