一、技术体系结构
1.1 总体技术体系
-
单一架构
一个项目,一个工程,导出为一个war包,在一个Tomcat上运行。也叫all in one。
单一架构,项目主要应用技术框架为:Spring , SpringMVC , Mybatis
-
分布式架构
一个项目(对应 IDEA 中的一个 project),拆分成很多个模块,每个模块是一个 IDEA 中的一个 module。每一个工程都是运行在自己的 Tomcat 上。模块之间可以互相调用。每一个模块内部可以看成是一个单一架构的应用。
分布式架构,项目主要应用技术框架:SpringBoot (SSM), SpringCloud , 中间件等
1.2 框架概念和理解
框架( Framework )是一个集成了基本结构、规范、设计模式、编程语言和程序库等基础组件的软件系统,它可以用来构建更高级别的应用程序。框架的设计和实现旨在解决特定领域中的常见问题,帮助开发人员更高效、更稳定地实现软件开发目标。
框架的优点包括以下几点:
1、提高开发效率
2、降低开发成本
3、提高应用程序的稳定性
4、提供标准化的解决方案
框架的缺点:
1、学习成本高
2、可能存在局限性
3、版本变更和兼容性问题
4、架构风险
站在文件结构的角度理解框架,可以将框架总结:框架 = jar包+配置文件
二、SpringFramework介绍
2.1 Spring和SpringFramework概念
广义的 Spring:Spring 技术栈(全家桶)
广义上的 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 框架。
Spring Framework(Spring框架)是一个开源的应用程序框架,由SpringSource公司开发,最初是为了解决企业级开发中各种常见问题而创建的。它提供了很多功能,例如:依赖注入(Dependency Injection)、面向切面编程(AOP)、声明式事务管理(TX)等。其主要目标是使企业级应用程序的开发变得更加简单和快速,并且Spring框架被广泛应用于Java企业开发领域。
Spring全家桶的其他框架都是以SpringFramework框架为基础!
2.2 SpringFramework 主要功能模块
功能模块 | 功能介绍 |
---|---|
Core Container | 核心容器,在Spring环境下使用任何功能都必须基于IOC容器 |
AOP&Aspects | 面向切面编程 |
TX | 声明式事务管理 |
Spring MVC | 提供了面向Web应用程序的集成功能 |
2.3 SpringFramework主要优势
1、丰富的生态系统
2、模块化的设计
3、简化Java开发
4、不断创新和发展
因此,这些优点使得 Spring Framework 成为了一个稳定、可靠、且创新的框架,为企业级 Java 开发提供了一站式的解决方案。
Spring 使创建 Java 企业应用程序变得容易。它提供了在企业环境中采用 Java 语言所需的一切,支持 Groovy 和 Kotlin 作为 JVM 上的替代语言,并且可以根据应用程序的需求灵活地创建多种架构。从Spring Framework 6.0.6开始,Spring 需要 Java 17+。
三、Spring IoC容器和核心概念
3.1.1 组件和组件管理概念
回顾常规的三层架构处理请求流程:
整个项目就是由各种组件搭建而成的:
3.1.2 Spring充当组件管理角色(IoC)
那么谁帮我们完成我们的期待,帮我们管理组件呢?
当然是Spring 框架了!
组件可以完全交给Spring 框架进行管理,Spring框架替代了程序员原有的new对象和对象属性赋值动作等!
Spring具体的组件管理动作包含:
- 组件对象实例化
- 组件属性属性赋值
- 组件对象之间引用
- 组件对象存活周期管理
- …
我们只需要编写元数据(配置文件)告知Spring 管理哪些类组件和他们的关系即可!
注意:组件是映射到应用程序中所有可重用组件的Java对象,应该是可复用的功能对象!
- 组件一定是对象
- 对象不一定是组件
综上所述,Spring 充当一个组件容器,创建、管理、存储组件,减少了我们的编码压力,让我们更加专注进行业务编写!
3.1.3 组件交给Spring管理的优势
- 降低了组件之间的耦合性:Spring IoC容器通过依赖注入机制,将组件之间的依赖关系削弱,减少了程序组件之间的耦合性,使得组件更加松散地耦合。
- 提高了代码的可重用性和可维护性:将组件的实例化过程、依赖关系的管理等功能交给Spring IoC容器处理,使得组件代码更加模块化、可重用、更易于维护。
- 方便了配置和管理:Spring IoC容器通过XML文件或者注解,轻松的对组件进行配置和管理,使得组件的切换、替换等操作更加的方便和快捷。
- 交给Spring管理的对象(组件),方可享受Spring框架的其他功能(AOP,声明事务管理)等
3.2.1 Spring IoC容器和容器实现
SpringIoC 容器也是一个复杂容器。它们不仅要负责创建组件的对象、存储组件的对象,还要负责调用组件的方法让它们工作,最终在特定情况下销毁组件。
Spring IoC 容器,负责实例化、配置和组装 bean(组件)。容器通过读取配置元数据来获取有关要实例化、配置和组装组件的指令。配置元数据以 XML、Java 注解或 Java 代码形式表现。它允许表达组成应用程序的组件以及这些组件之间丰富的相互依赖关系。
3.2.2 SpringIoC容器具体接口和实现类
容器接口:
BeanFactory
接口提供了一种高级配置机制,能够管理任何类型的对象,它是SpringIoC容器标准化超接口!
ApplicationContext
是 BeanFactory
的子接口。它扩展了以下功能:
- 更容易与 Spring 的 AOP 功能集成
- 消息资源处理(用于国际化)
- 特定于应用程序给予此接口实现,例如Web 应用程序的
WebApplicationContext
简而言之, BeanFactory
提供了配置框架和基本功能,而 ApplicationContext
添加了更多特定于企业的功能。 ApplicationContext
是 BeanFactory
的完整超集!
ApplicationContext容器实现类:
类型名 | 简介 |
---|---|
ClassPathXmlApplicationContext | 通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象 |
FileSystemXmlApplicationContext | 通过文件系统路径读取 XML 格式的配置文件创建 IOC 容器对象 |
AnnotationConfigApplicationContext | 通过读取Java配置类创建 IOC 容器对象 |
WebApplicationContext | 专门为 Web 应用准备,基于 Web 环境创建 IOC 容器对象,并将对象引入存入 ServletContext 域中。 |
Spring框架提供了多种配置方式:XML配置方式、注解方式和Java配置类方式
- 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和依赖关系的配置。
为了迎合当下开发环境,我们将以配置类+注解方式为主进行讲解!
3.3 IoC/DI
IoC(Inversion of Control)控制反转
IoC 主要是针对对象的创建和调用控制而言的,也就是说,当应用程序需要使用一个对象时,不再是应用程序直接创建该对象,而是由 IoC 容器来创建和管理,即控制权由应用程序转移到 IoC 容器中,也就是“反转”了控制权。这种方式基本上是通过依赖查找的方式来实现的,即 IoC 容器维护着构成应用程序的对象,并负责创建这些对象。
DI(Dependency Injection)依赖注入
DI 是指在组件之间传递依赖关系的过程中,将依赖关系在容器内部进行处理,这样就不必在应用程序代码中硬编码对象之间的依赖关系,实现了对象之间的解耦合。在 Spring 中,DI 是通过 XML 配置文件或注解的方式实现的。它提供了三种形式的依赖注入:构造函数注入、Setter 方法注入和接口注入。
四、Spring IoC实践和应用
4.2 基于XML配置方式组件管理
4.2.1 实验一:组件(Bean)信息声明配置(IoC)
导入SpringIoC相关依赖
<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>
- 基于无参数构造方法
当通过构造函数方法创建一个 bean(组件对象) 时,所有普通类都可以由 Spring 使用并与之兼容。也就是说,正在开发的类不需要实现任何特定的接口或以特定的方式进行编码。只需指定 Bean 类信息就足够了。但是,默认情况下,我们需要一个默认(空)构造函数。
package com.triticale.ioc_01;
public class HappyComponent {
public void doWork() {
System.out.println("HappyComponent.doWork");
}
}
<!--1.使用无参数构造函数实例化的组件
<bean
id = "组件的标识"
class = "工厂类的全限定符"
-->
<bean id="happyComponent" class="com.triticale.ioc_01.HappyComponent"/>
- 基于静态工厂方法实例化
除了使用构造函数实例化对象,还有一类是通过工厂模式实例化对象。接下来我们讲解如何定义使用静态工厂方法创建Bean的配置 !
package com.triticale.ioc_01;
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}
<!--2.静态工厂类如何声明方法进行ioc的配置
<bean
id = "组件的标识“
class = "工厂类的全限定符"
factory-method = "静态工厂方法"
-->
<bean id="clientService" class="com.triticale.ioc_01.ClientService" factory-method="createInstance"/>
- 基于实例工厂方法实例化
package com.triticale.ioc_01;
public class DefaultServiceLocator {
private static ClientServiceImplclientService clientServiceImplclientService = new ClientServiceImplclientService();
public ClientServiceImplclientService createClientServiceInstance() {
return clientServiceImplclientService;
}
}
package com.triticale.ioc_01;
public class ClientServiceImplclientService {
}
<!--3.非静态工厂如何声明ioc配置-->
<!--3.1 配置工厂类的组件信息-->
<bean id="defaultServiceLocator" class="com.triticale.ioc_01.DefaultServiceLocator"/>
<!--3.2 通过指定非静态工厂对象和方法名来配置生成的ioc信息-->
<bean id="clientServiceImplclientService" factory-bean="defaultServiceLocator" factory-method="createClientServiceInstance"/>
总的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">
<!--1.使用无参数构造函数实例化的组件
<bean
id = "组件的标识"
class = "工厂类的全限定符"
-->
<bean id="happyComponent" class="com.triticale.ioc_01.HappyComponent"/>
<!--2.静态工厂类如何声明方法进行ioc的配置
<bean
id = "组件的标识“
class = "工厂类的全限定符"
factory-method = "静态工厂方法"
-->
<bean id="clientService" class="com.triticale.ioc_01.ClientService" factory-method="createInstance"/>
<!--3.非静态工厂如何声明ioc配置-->
<!--3.1 配置工厂类的组件信息-->
<bean id="defaultServiceLocator" class="com.triticale.ioc_01.DefaultServiceLocator"/>
<!--3.2 通过指定非静态工厂对象和方法名来配置生成的ioc信息-->
<bean id="clientServiceImplclientService" factory-bean="defaultServiceLocator" factory-method="createClientServiceInstance"/>
</beans>
图解IoC配置流程
4.2.2 实验二:组件(Bean)依赖注入配置(DI)
单个构造参数注入
准备组件类
public class UserDao {
}
public class UserService {
private UserDao userDao;
public UserService(UserDao userDao) {
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">
<!--步骤1:将他们都存放在ioc容器-->
<bean id="userDao" class="com.triticale.ioc_02.UserDao"/>
<!--单个构造参数注入-->
<bean id="userService" class="com.triticale.ioc_02.UserService">
<!--构造参数传值 di的配置
<constructor-arg 构造参数传值的di配置
value = 直接属性值
ref = 引用其他的bean beanId的值
-->
<constructor-arg ref="userDao"/>
</bean>
<!--springioc容器是一个高级容器,内部会有缓存动作!1、先创建对象[ioc],2、在进行赋值[di]-->
</beans>
多个构造参数注入
准备组件类
package com.triticale.ioc_02;
public class UserService {
private UserDao userDao;
private int age;
private String name;
public UserService(int age, String name,UserDao userDao) {
this.userDao = userDao;
this.age = age;
this.name = name;
}
}
package com.triticale.ioc_02;
public class 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">
<!--步骤1:将他们都存放在ioc容器-->
<bean id="userDao" class="com.triticale.ioc_02.UserDao"/>
<!--多个构造参数注入-->
<bean id="userService2" class="com.triticale.ioc_02.UserService">
<!--方案一:构造参数的顺序填写-->
<constructor-arg value="18"/>
<constructor-arg value="张三"/>
<constructor-arg ref="userDao"/>
</bean>
<bean id="userService3" class="com.triticale.ioc_02.UserService">
<!--方案二:构造参数的名字填写-->
<constructor-arg name="age" value="19"/>
<constructor-arg name="name" value="李四"/>
<constructor-arg name="userDao" ref="userDao"/>
</bean>
<bean id="userService4" class="com.triticale.ioc_02.UserService">
<!--方案三:构造参数的下标填写-->
<constructor-arg index="0" value="18"/>
<constructor-arg index="1" value="张三"/>
<constructor-arg index="2" ref="userDao"/>
</bean>
</beans>
基于setter方法的注入
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;
}
}
<?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">
<!--触发setter方法进行注入-->
<bean id="movieFinder" class="com.triticale.ioc_02.MovieFinder"/>
<bean id="simpleMovieLister" class="com.triticale.ioc_02.SimpleMovieLister">
<!--
name -> 属性名 setter方法的去掉set和首字母小写的值
value/ref 二选一
-->
<property name="movieName" value="张三"/>
<property name="movieFinder" ref="movieFinder"/>
</bean>
</beans>
4.2.3 实验三:IoC容器创建和使用
public class SpringIoCTest {
/**
* 讲解如果创建ioc容器并且读取配置文件
*/
public void creatIoc(){
//创建容器 选择合适容器实现即可
//构造函数(String...配置文件) 可以填一个或者多个
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-04.xml");
//方式二:先创建ioc容器对象,在指定配置文件,再刷新
ClassPathXmlApplicationContext applicationContext1 = new ClassPathXmlApplicationContext();
applicationContext1.setConfigLocations("spring-04.xml");
applicationContext1.refresh(); //调用ioc和di的流程
}
@Test
public void getBeanFromIoC(){
//1、创建ioc容器对象
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-04.xml");
//2、读取ioc容器的组件
//方案1:直接根据beanId获取即可, 返回值类型是Object 需要强转[不推荐]
HappyComponent happyComponent = (HappyComponent) applicationContext.getBean("happyComponent");
//方案2:根据beanId,同时指定bean的类型Class
HappyComponent happyComponent1 = applicationContext.getBean("happyComponent", HappyComponent.class);
//方案3:直接根据类型获取
HappyComponent happyComponent2 = applicationContext.getBean(HappyComponent.class);
System.out.println(happyComponent1 == happyComponent2);
System.out.println(happyComponent1 == happyComponent);
}
}
4.2.4 实验四:组件(Bean)作用域和周期方法配置
我们可以在组件类中定义方法,然后当IoC容器实例化和销毁组件对象的时候进行调用!这两个方法我们成为生命周期方法!
类似于Servlet的init/destroy方法,我们可以在周期方法完成初始化和释放资源等工作。
周期方法声明
public class JavaBean {
public void init(){
System.out.println("init");
}
public void clear(){
System.out.println("destroy");
}
}
周期方法配置
<bean id="javaBean" class="com.triticale.ioc_04.JavaBean" init-method="init" destroy-method="clear"/>
组件作用域配置
-
Bean作用域概念
<bean
标签声明Bean,只是将Bean的信息配置给SpringIoC容器!在IoC容器中,这些
<bean
标签对应的信息转成Spring内部BeanDefinition
对象,BeanDefinition
对象内,包含定义的信息(id,class,属性等等)!这意味着,
BeanDefinition
与类
概念一样,SpringIoC容器可以可以根据BeanDefinition
对象反射创建多个Bean对象实例。具体创建多少个Bean的实例对象,由Bean的作用域Scope属性指定!
取值 | 含义 | 创建对象的时机 | 默认值 |
---|---|---|---|
singleton | 在IoC容器中,这个bean的对象始终为单实例 | IoC容器初始化时 | 是 |
prototype | 这个bean在IoC容器中有多个实例 | 获取bean时 | 否 |
如果是在WebApplicationContext环境下还会有另外两个作用域(但不常用)
取值 | 含义 | 创建对象的时机 | 默认值 |
---|---|---|---|
request | 请求范围内有效的实例 | 每次请求 | 否 |
session | 会话范围内有效的实例 | 每次会话 | 否 |
4.2.5 实验五:高级特性:FactoryBean 特性和使用
-
FactoryBean简介
FactoryBean
接口是Spring IoC容器实例化逻辑的可插拔性点。用于配置复杂的Bean对象,可以将创建过程存储在
FactoryBean
的getObject方法!FactoryBean<T>
接口提供三种方法:-
T getObject()
:返回此工厂创建的对象的实例。该返回值会被存储到IoC容器!
-
boolean isSingleton()
:如果此
FactoryBean
返回单例,则返回true
,否则返回false
。此方法的默认实现返回true
(注意,lombok插件使用,可能影响效果)。 -
Class<?> getObjectType()
: 返回getObject()
方法返回的对象类型,如果事先不知道类型,则返回null
。
-
-
FactoryBean使用场景
- 代理类的创建
- 第三方框架整合
- 复杂对象实例化等
public class JavaBean {
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "JavaBean{" +
"name='" + name + '\'' +
'}';
}
}
public class JavaBeanFactoryBean implements FactoryBean {
private String value;
public void setValue(String value) {
this.value = value;
}
@Override
public Object getObject() throws Exception {
//使用自己的方式实例化对象
JavaBean javaBean = new JavaBean();
//如果要给Javabean的属性赋值,无法在xml文件中通过<property>标签赋值,因为是给工厂类进行赋值
//但是可以通过工厂类进行桥接赋值
javaBean.setName(value);
return javaBean;
}
@Override
public Class<?> getObjectType() {
return JavaBean.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="javaBean" class="com.triticale.ioc_05.JavaBeanFactoryBean">
<property name="value" value="张三"/>
</bean>
</beans>
@Test
public void test_05(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-06.xml");
JavaBean javaBean = applicationContext.getBean("javaBean", JavaBean.class);
System.out.println(javaBean.toString());
//TODO:FactoryBean工厂也会加入ioc容器! 名字 &id
Object bean = applicationContext.getBean("&javaBean");
System.out.println(bean);
}
4.3 基于注解方式管理 Bean
4.3.1 实验一:Bean注解标记和扫描(IoC)
和 XML 配置文件一样,注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能是框架检测到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作。
本质上:所有一切的操作都是 Java 代码来完成的,XML 和注解只是告诉框架中的 Java 代码如何执行。
@Component
public class CommonComponent {
}
@Controller
public class XxxController {
}
@Service
public class XxxService {
}
@Repository
public class XxxDao {
}
注解 | 说明 |
---|---|
@Component | 该注解用于描述 Spring 中的 Bean,它是一个泛化的概念,仅仅表示容器中的一个组件(Bean),并且可以作用在应用的任何层次,例如 Service 层、Dao 层等。 使用时只需将该注解标注在相应类上即可。 |
@Repository | 该注解用于将数据访问层(Dao 层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。 |
@Service | 该注解通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。 |
@Controller | 该注解通常作用在控制层(如SpringMVC 的 Controller),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。 |
Spring 为了知道程序员在哪些地方标记了什么注解,就需要通过扫描的方式,来进行检测。然后根据注解进行后续操作。
<context:component-scan base-package="com.triticale.ioc_01"/>
<!--指定包,但是排除注解-->
<context:component-scan base-package="com.triticale.ioc_01">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
<!--指定包,指定包含注解
use-default-filters="false" 指定包的所有注解不生效-->
<context:component-scan base-package="com.triticale.ioc_01" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
测试类
public class SpringIoCTest {
@Test
public void testIoc_01(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-01.xml");
XxxDao bean = applicationContext.getBean(XxxDao.class);
System.out.println("bean = " + bean);
//添加ioc注解,默认的组件名是 类的首字母小写
Object bean1 = applicationContext.getBean("xxxService");
System.out.println("bean1 = " + bean1);
}
4.3.2 实验二:组件(Bean)作用域和周期方法注解
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_SINGLETON) //单例,默认值
@Component
public class JavaBean {
//初始化
@PostConstruct
public void init(){
System.out.println("init");
}
//销毁
@PreDestroy
public void destroy(){
System.out.println("destroy");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.triticale.ioc_02"/>
</beans>
测试代码
@Test
public void testIoc_02(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-02.xml");
JavaBean bean = applicationContext.getBean(JavaBean.class);
applicationContext.close();
}
4.3.3 实验三:Bean 属性赋值:引用类型自动装配(DI)
@Controller
public class UserController {
@Autowired
//自动装配注解(DI): 1、ioc容器中查找符合类型的组件对象 2、设置给当前属性(di)
//如果同一个类型有多个对应的组件,@Autowired也会报错,无法选择,
//解决1:成员属性名指定 @Autowired 多个组件的时候,默认会根据成员属性名查找
//解决2:和@Qualifier(value = "")一起使用
//优化点,使用@Resource(value = "") ,这个注解等于两个注解合起来使用,但是使用该注解需要导依赖
private UserServiceImpl userService;
public void show(){
String show = userService.show();
System.out.println("show =" + show);
}
public interface UserService {
public String show();
}
@Service
public class UserServiceImpl implements UserService{
@Override
public String show() {
return "service";
}
}
使用@Resource注解需要导依赖
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
4.3.4 实验四:Bean属性赋值:基本类型暑假赋值(DI)
@Value 通常用于注入外部化属性
声明外部配置
application.properties
catalog.name=MovieCatalog
xml引入外部配置
<!-- 引入外部配置文件-->
<context:property-placeholder location="application.properties" />
@Value 注解读取配置
@Component
public class CommonComponent {
/**
* 情况1: ${key} 取外部配置key对应的值!
* 情况2: ${key:defaultValue} 没有key,可以给与默认值
*/
@Value("${catalog:hahaha}")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
4.4 基于配置类的方式管理Bean
/**
* 1、包扫描注解配置
* 2、引用外部的配置文件
* 3、声明第三方依赖的bean组件
*
* 步骤一:添加@Configuration 代表是配置类
* 步骤二:实现上面的三个功能注解
*/
@Configuration
@ComponentScan("com.triticale.ioc_01")
@PropertySource(value = "classpath:jdbc.properties")
public class JavaConfiguration {
/**
* 方法的返回值类型 == bean组件的类型或他的接口或父类
* 方法的名字 == bean id
*
* 方法体可以自定义实现过程即可
* 最重要的一步:@Bean会真正让配置类的方法创建的组件存储到ioc容器
*
* 问题一: beanName的问题
* 默认: 方法名
* 指定: name/value 属性起名字,覆盖方法名
* 问题二: 周期方法如何指定
* 原有注解方案: PostConstruct + PreDestroy 注解指定
* bean属性指定:initMethod / destroyMethod
* 问题三: 作用域
* 和以前一样@Scope
*/
@Bean
public DruidDataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("");
dataSource.setDriverClassName("");
dataSource.setUsername("");
dataSource.setPassword("");
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(){
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//需要DataSource 需要ioc容器的其他组件
//方案一:如果其他组件也是@Bean方法,可以直接调用 | 从ioc容器获取组件
jdbcTemplate.setDataSource(dataSource());
return jdbcTemplate;
}
@Bean
public JdbcTemplate jdbcTemplate2(DataSource dataSource){
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//需要DataSource 需要ioc容器的其他组件
//方案二:形参列表声明想要的组件类型,可以是一个也可以是多个,ioc容器也会注入
//形参变量注入,要求必须有对应类型的组件,如果没有抛异常
//如果有多个,可以使用形参名等同于对应的beanId标识即可
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
}
@Import扩展
@Import
注释允许从另一个配置类加载 @Bean
定义,如以下示例所示:
@Configuration
public class ConfigA {
@Bean
public A a() {
return new A();
}
}
@Configuration
@Import(ConfigA.class)
public class ConfigB {
@Bean
public B b() {
return new B();
}
}
现在,在实例化上下文时不需要同时指定 ConfigA.class
和 ConfigB.class
,只需显式提供 ConfigB
,如以下示例所示:
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);
// now both beans A and B will be available...
A a = ctx.getBean(A.class);
B b = ctx.getBean(B.class);
}