知识点引入
关于框架
框架( Framework )是一个集成了基本结构、规范、设计模式、编程语言和程序库等基础组件的软件系统,它可以用来构建更高级别的应用程序。框架的设计和实现旨在解决特定领域中的常见问题,帮助开发人员更高效、更稳定地实现软件开发目标
关于组件
在服务端:
- 负责处理客户端的请求、向客户端发送响应的controller层就是控制层
- 负责业务逻辑处理的service层就是业务逻辑层,业务逻辑层由controller层调用,完成下一步的业务逻辑处理
- 负责进行数据库操作的dao层就是持久化层,持久化层由业务逻辑层调用,完成下一步的对数据库的操作
组件便是其中各个层的可以复用的Java对象
组件管理
组件可以交给Spring 框架进行管理,Spring框架替代了程序员原有的new对象和对象属性赋值动作等
Spring具体的组件管理动作包含:
- 组件对象实例化
- 组件属性属性赋值
- 组件对象之间引用
- 组件对象存活周期管理
- 等等
- 我们只需要编写元数据(配置文件)告知Spring 管理哪些类组件和他们的关系即可
注:
- 组件是映射到应用程序中所有可重用组件的Java对象,应该是可复用的功能对象
- 组件一定是对象,,对象不一定是组件
即,Spring 充当一个组件容器,创建、管理、存储组件
SpringIoc容器介绍
Spring IoC 容器,负责实例化、配置和组装 bean(组件)。容器通过读取配置元数据来获取有关要实例化、配置和组装组件的指令。配置元数据以 XML、Java 注解或 Java 代码形式表现。它允许表达组成应用程序的组件以及这些组件之间丰富的相互依赖关系
SpringIoc容器的接口和实现类
SpringIoc容器接口:
BeanFactory
接口提供了一种高级配置机制,能够管理任何类型的对象,它是SpringIoC容器标准化超接口
ApplicationContext
是 BeanFactory
的子接口。它扩展了以下功能:
- 更容易与 Spring 的 AOP 功能集成
- 消息资源处理(用于国际化)
- 特定于应用程序给予此接口实现,例如Web 应用程序的
WebApplicationContext
BeanFactory
提供了配置框架和基本功能,而 ApplicationContext
添加了更多特定于企业的功能
ApplicationContext容器实现类:
常见的四个实现类
类型名 | 简介 |
---|---|
ClassPathXmlApplicationContext | 通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象 |
FileSystemXmlApplicationContext | 通过文件系统路径读取 XML 格式的配置文件创建 IOC 容器对象 |
AnnotationConfigApplicationContext | 通过读取Java配置类创建 IOC 容器对象 |
WebApplicationContext | 专门为 Web 应用准备,基于 Web 环境创建 IOC 容器对象,并将对象引入存入 ServletContext 域中。 |
Spring容器配置方式
- XML配置方式:是Spring框架最早的配置方式之一,通过在XML文件中定义Bean及其依赖关系、Bean的作用域等信息,让Spring IoC容器来管理Bean之间的依赖关系。该方式从Spring框架的第一版开始提供支持。
- 注解方式:从Spring 2.5版本开始提供支持,可以通过在Bean类上使用注解来代替XML配置文件中的配置信息。通过在Bean类上加上相应的注解(如@Component, @Service, @Autowired等),将Bean注册到Spring IoC容器中,这样Spring IoC容器就可以管理这些Bean之间的依赖关系。
- Java配置类方式:从Spring 3.0版本开始提供支持,通过Java类来定义Bean、Bean之间的依赖关系和配置信息,从而代替XML配置文件的方式。Java配置类是一种使用Java编写配置信息的方式,通过@Configuration、@Bean等注解来实现Bean和依赖关系的配置。
Spring IoC / DI概念
-
IoC容器
Spring IoC 容器,负责实例化、配置和组装 bean(组件)核心容器。容器通过读取配置元数据来获取有关要实例化、配置和组装组件的指令。
-
IoC(Inversion of Control)控制反转
IoC 主要是针对对象的创建和调用控制而言的,也就是说,当应用程序需要使用一个对象时,不再是应用程序直接创建该对象,而是由 IoC 容器来创建和管理,即控制权由应用程序转移到 IoC 容器中,也就是“反转”了控制权。这种方式基本上是通过依赖查找的方式来实现的,即 IoC 容器维护着构成应用程序的对象,并负责创建这些对象。
-
DI (Dependency Injection) 依赖注入
DI 是指在组件之间传递依赖关系的过程中,将依赖关系在容器内部进行处理,这样就不必在应用程序代码中硬编码对象之间的依赖关系,实现了对象之间的解耦合。在 Spring 中,DI 是通过 XML 配置文件或注解的方式实现的。它提供了三种形式的依赖注入:构造函数注入、Setter 方法注入和接口注入。
Spring IoC / DI的实现步骤
- 编写配置信息(配置元数据)
- 实例化ioc容器
- 获取Bean(获取组件)
基于xml文件配置ioc
无参构造函数实例化
例:有一个类,其中只有一个无参构造
package com.ergou.ioc; public class HappyComponent { //默认包含无参数构造函数 public void doWork() { System.out.println("HappyComponent.doWork"); } }
- 在resources目录下创建xml文件(选择 xml配置 ---》spring配置)
- 在beans标签中创建bean标签,一个bean标签中写一个组件的信息,一个bean标签对应一个组件对象
- 在bean标签中写id属性和class属性的值
- id属性是组件的标识,是唯一的,方便读取组件
- class属性是组件的类的权限定符,其中写上对应类的路径
<?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 = "happyComponent1" class = "com.ergou.ioc.HappyComponent"/> </beans>
静态工厂方法实例化
例:若有一个类是静态工厂方法实例化的
package com.ergou.ioc; public class ClientService { private static ClientServiceclientService= new ClientService(); private ClientService() {} public static ClientService createInstance() { returnclientService; } }
同样是用bean标签,id属性写标识名,class写类路径,除此之外,还要加上写factory-method属性,其中写上静态方法的方法名
<?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 = "happyComponent1" class = "com.ergou.ioc.HappyComponent"/> <!--静态工厂类声明工厂方法进行ioc配置--> <bean id="clientService" class="com.ergou.ioc.ClientService" factory-method="createInstance"/> </beans>
实例工厂方法实例化(非静态方法)
例:若有一个类(ClientServiceImpl)是实例工厂方法实例化的
package com.ergou.ioc; public class DefaultServiceLocator { private static ClientServiceImplclientService= new ClientServiceImpl(); public ClientServiceImpl createClientServiceInstance() { returnclientService; } }
先写一个bean标签配置此工厂类的组件信息,然后在另一个bean标签中的factory-bean属性中写此工厂类的组件的bean的id属性值,再写factory-method属性中写实例工厂方法的方法名
<!--实例工厂类声明ioc配置--> <!--配置工厂类的组件信息--> <bean id="defaultServiceLocator" class="com.ergou.ioc.DefaultServiceLocator"/> <!--指定非静态工厂对象和方法名来配置生成的ioc信息--> <bean id="clientService2" factory-bean="defaultServiceLocator" factory-method="createClientServiceInstance"/>
基于xml文件配置di
基于构造函数的依赖注入
①单个构造参数注入
引用和被引用的组件,必须全部在ioc容器内。
一个组件要引入另一个组件时,使用的bean标签应该是双标签
在此bean双标签内,用constructor-arg标签引入信息
关于constructor-arg标签:
当要注入一些基本类型的参数时,使用value属性,写明具体的参数即可
当要注入其他的组件时,在ref属性中写其他bean的id值即可
-
例:
<?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="userService" class="com.ergou.ioc02.UserService"> <constructor-arg ref="userDao"/> </bean> <bean id="userDao" class="com.ergou.ioc02.UserDao"/> </beans>
②多个构造参数注入
- 构造参数的顺序填写参数:
UserService的组件bean使用bean双标签,依次在其中写多个constructor-arg标签中填写参数即可
-
例:
若UserService类为:
public class UserService { private UserDao userDao; private int age; private String name; public UserService(UserDao userDao) { this.userDao = userDao; } public UserService(int age , String name ,UserDao userDao) { this.age = age; this.name = name; this.userDao = userDao; } }
要使用第二个多参的构造器
<?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="userService" class="com.ergou.ioc02.UserService"> <constructor-arg ref="userDao"/> </bean> <bean id="userDao" class="com.ergou.ioc02.UserDao"/> <bean id="userService2" class="com.ergou.ioc02.UserService"> <constructor-arg value="18"/> <constructor-arg value="二狗"/> <constructor-arg ref="userDao"/> </bean> </beans>
- 使用name属性值指定参数名,再写value属性进行参数的赋值,例
<bean id="userService2" class="com.ergou.ioc02.UserService"> <constructor-arg name="age" value="18"/> <constructor-arg name="name" value="二狗"/> <constructor-arg name="userDao" ref="userDao"/> </bean>
- 使用参数的下角标(按参数的次序,从左到右,从0开始)
<bean id="userService2" class="com.ergou.ioc02.UserService"> <constructor-arg index="0" value="18"/> <constructor-arg index="1" value="二狗"/> <constructor-arg index="2" ref="userDao"/> </bean>
基于setter方法注入
需要引入的组件的bean也是bean的双标签
在bean双标签中,使用property标签引入
property标签的属性:
- name:调用setter方法的名,为具体的setter方法名去掉开头的set后,将首字母小写,例如,setter方法名为setMovieName,对应name属性值应该为movieName
- value:引用基本类型的值
- ref:引用其他组件
例:
有两个类为
public class MovieFinder{ }
public class SimpleMovieLister { private MovieFinder movieFinder; private String movieName; public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } public void setMovieName(String movieName){ this.movieName = movieName; } // business logic that actually uses the injected MovieFinder is omitted... }
<bean id="movieFinder" class="com.ergou.ioc02.MovieFinder"/> <bean id="simpleMovieLister" class="com.ergou.ioc02.SimpleMovieLister"> <property name="movieName" value="我想吃掉你的胰脏"/> <property name="movieFinder" ref="movieFinder"/> </bean>
创建和使用ioc容器
创建ioc容器
先选择容器,以ClassPathXmlApplicationContext
为例
创建方式一:直接创建容器并且指定配置文件即可
//方式①,直接创建容器并且指定配置文件即可
//其构造方法内填写配置文件名,可以填多个配置文件
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-03.xml");
创建方式二:使用setConfigLocations方法,后面再调用refresh方法刷新
//方式二:使用setConfigLocations方法,后面再调用refresh方法刷新
ClassPathXmlApplicationContext applicationContext1 = new ClassPathXmlApplicationContext();
//可以有多个配置文件
applicationContext1.setConfigLocations("spring-03.xml");
applicationContext1.refresh();
ioc容器获取bean的方式
方式一:直接用容器的getBean方法根据bean的id属性来获取(返回值是Object,需要手动强转类型)
方式二:使用getBean方法,根据bean的id值,并传入类对应的class实例
方法三:直接根据类的class实例获取(要保证此类在ioc容器中只有一个bean)
例:
@Test
public void getBeanFromIoC(){
//创建容器对象
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext();
applicationContext.setConfigLocations("spring-03.xml");
applicationContext.refresh();
//方式一:直接根据bean的id获取,返回值是Object类型,需要强转
HappyComponent happyComponent = (HappyComponent) applicationContext.getBean("happyComponent");
//方式二:根据bean的id,同时指定bean的类型获取
HappyComponent happyComponent1 = applicationContext.getBean("happyComponent",HappyComponent.class);
//方式三:直接根据类的class实例获取
//要保证此类在ioc容器中只有一个bean
HappyComponent happyComponent2 = applicationContext.getBean(HappyComponent.class);
happyComponent2.doWork();
System.out.println(happyComponent1 == happyComponent);
System.out.println(happyComponent1 == happyComponent2);
}
组件周期方法
可以在组件类中定义方法,然后当IoC容器实例化和销毁组件对象的时候进行调用,这两个方法称为为生命周期方法
周期方法要求权限修饰符为public,返回值类型为void,是无参数的
最后用bean中的init-method属性写入初始化方法的方法名,destory-method属性写入销毁方法的方法名即可
例:
public class BeanOne {
//周期方法要求: 方法命名随意,但是要求方法必须是 public void 无形参列表
public void init() {
// 初始化逻辑
}
}
public class BeanTwo {
public void cleanup() {
// 释放资源逻辑
}
}
<beans>
<bean id="beanOne" class="examples.BeanOne" init-method="init" />
<bean id="beanTwo" class="examples.BeanTwo" destroy-method="cleanup" />
</beans>
组件作用域配置
-
Bean作用域概念
bean
标签声明Bean,只是将Bean的信息配置给SpringIoC容器在IoC容器中,这些
bean
标签对应的信息转成Spring内部BeanDefinition
对象,BeanDefinition
对象内,包含定义的信息(id,class,属性等)这意味着,
BeanDefinition
与类
概念一样,SpringIoC容器可以可以根据BeanDefinition
对象反射创建多个Bean对象实例。具体创建多少个Bean的实例对象,由Bean的作用域scope属性指定
- 作用域(scope)可选值
取值 含义 创建对象的时机 默认值 singleton 在 IOC 容器中,这个 bean 的对象始终为单实例 IOC 容器初始化时 是 prototype 这个 bean 在 IOC 容器中有多个实例 获取 bean 时 否 如果是在WebApplicationContext环境下还会有另外两个作用域(不常用):
取值 含义 创建对象的时机 默认值 request 请求范围内有效的实例 每次请求 否 session 会话范围内有效的实例 每次会话 否 <!-- scope属性:取值singleton(默认值),bean在IOC容器中只有一个实例,IOC容器初始化时创建对象--> <!-- scope属性:取值prototype,bean在IOC容器中可以有多个实例,getBean()时创建对象--> <bean id="javaBean2" scope="prototype" class="com.ergou.ioc04.JavaBean2"> <property name="age" value="19"/> </bean> <bean id="happyComponent8" scope="singleton" class="com.ergou.ioc04.JavaBean3"> <property name="age" value="20"/> </bean>
FactoryBean的使用
FactoryBean
接口是Spring IoC容器实例化逻辑的可插拔性点。用于配置复杂的Bean对象,可以将创建过程存储在
FactoryBean
的getObject方法FactoryBean<T>
接口提供三种方法:-
T getObject()
:返回此工厂创建的对象的实例。该返回值会被存储到IoC容器。此方法中写目标类的实例的创建代码
-
boolean isSingleton()
:如果此
FactoryBean
返回单例,则返回true
,否则返回false
。此方法的默认实现返回true
(注意,lombok插件使用,可能影响效果),此方法中写return true或return false -
Class<?> getObjectType()
: 返回getObject()
方法返回的对象类型,如果事先不知道类型,则返回null
。此方法中写目标类的class实例
使用步骤:
- 实现FactoryBean接口,指定返回值类型为泛型
- 实现getObject和getObjectType方法,isSingleton方法按需求选择实现
- 配置xml文件,bean标签只要写上id属性和class属性,class属性其中写上FactoryBean类的路径即可,id属性建议与FactoryBean返回对象的类有关联
public class JavaBean { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
public class JavaBeanFactoryBean implements FactoryBean<JavaBean> { @Override public JavaBean getObject() throws Exception { JavaBean javaBean = new JavaBean(); return javaBean; } @Override public Class<?> getObjectType() { return JavaBean.class; } }
<bean id="javaBean" class="com.ergou.ioc05.JavaBeanFactoryBean"/>
注:
- FactoryBean类也会被加到ioc容器中,其id值为目标类实例的bean标签的id值前加上&,比如在以上例子中FactoryBean的组件的id值为&javaBean
- 在javaBean标签,使用property或constructor-arg标签时,是在给JavaBeanFactoryBean的属性赋值,而不是JavaBean的属性。要使用property或constructor-arg方法给JavaBean赋值,可以在JavaBeanFactoryBean类中创建相应的属性,然后用JavaBeanFactoryBean的属性在getObject方法中给JavaBean的组件的属性赋值