一:技术体系架构二:SpringFramework介绍三:Spring loC容器和核心概念3.1 组件和组件管理的概念3.1.1什么是组件:3.1.2:我们的期待3.1.3Spring充当组件管理角色(IOC)3.1.4 Spring优势3.2 Spring Ioc容器和容器实现3.2.1普通和复杂容器3.2.2 SpringIOC的容器介绍3.2.3 Spring IOC具体的接口和实现类3.2.4 SpringIOC容器配置方式3.3 Spring Ioc/DI 概念总结3.3.1 IOC容器3.3.2 IOC(inversion of Control)控制反转3.3.3 DI(Dependency Injection)依赖注入四:Spring Ioc的实践和应用4.1SpringIOC/DI实现步骤4.2基于XML配置方式组件管理4.2.1 实验一:组件(Bean)信息声明配置(IOC)1.目标:2.思路3.无参构造函数的声明方法4.静态工厂类5.非静态工厂模式如何创建6.静态工厂类和非静态类的区分4.2.2 实验二:组件(Bean)依赖注入配置(DI)1.目标2.思路3.基于构造函数的依赖注入(单个构造函数)4.基于构造函数的依赖注入(多个构造函数)5.基于Setter方法来依赖注入(重点中的重点)4.2.3 实验三:IOC容器的创建和使用A.IOC容器的创建B.IOC容器的使用4.2.4 实验四:高级特性: 组件(Bean)作用域和周期方法配置4.2.4.1.组件周期方法配置a.周期方法概念b.周期方法声明c.周期方法配置4.2.4.2.组件作用域配置a.Bean作用域b.作用域可选值:c.作用域的配置4.2.5 实验五:高级特性: FactoryBean特性和使用4.2.5.1.FactoryBean的认知4.2.5.2.FactoryBean运用4.2.5.3.FactoryBean实际情况4.2.6 实验六:基于XML方式整合三层架构组件4.3基于 注解 方式管理Bean4.3.1 实验一: Bean注解标记和扫描(IOC)4.3.1.1.注解是什么:4.3.1.2扫描理解4.3.1.3.准备Spring项目的组件:4.3.1.4.组件标记注解和区别:4.3.1.5.配置文件确定扫描范围A情况:基本扫描配置B情况:指定排除组件C情况:指定包含组件4.3.2 实验二: 组件(Bean)作用域和周期方法注解4.3.2.1组件周期方法的配置4.3.2.2组件作用域配置4.3.3 实验三: Bean属性赋值: 引用类型自动装配(DI)4.3.4 实验四: Bean属性赋值: 基本类型属性赋值(DI)4.3.5 实验五: 基于注解+XML方式 整合三层架构组件4.4基于 配置类 方式管理Bean4.4.1 完全注解开发理解4.4.2 实验一: 配置类和扫描注解4.4.3 实验二: @Bean定义组件4.4.4 实验三: 高级特性: @Bean注解细节4.4.5 实验四: 高级特性: @Import扩展4.4.6 实验五: 基于注解+配置类整合方式 整合三层架构组件4.5三种配置方式总结4.6整合Spring5-Test5搭建测试 环境五:SpringAOP面向切面编程5.1 场景设定和问题复现5.2 解决技术代理模式(AOP)1.代理模式2.静态代理技术3.动态代理技术(实际开发使用)5.3 面向切面编程思维5.4 Spring AOP框架介绍和关系梳理5.5 Spring AOP基于注解方式实现和细节5.5.1 Spring AOP底层技术组成5.5.2 初步实现5.5.3 获取通知细节信息'5.5.4 切点表达式语法5.5.5 重用(提取)切点表达式5.5.6 环绕通知5.5.7 切面优先级设置5.5.8 CGLib动态代理生效5.5.9 注解实现小结5.6 Spring AOP基于XML方式实现和准备工作5.7 SpringAOP对获取Bean的影响理解六:Spring声名式事务七:Spring核心掌握总结
一:技术体系架构
二:SpringFramework介绍
三:Spring loC容器和核心概念
3.1 组件和组件管理的概念
3.1.1什么是组件:
3.1.2:我们的期待
-
有人替我们创建 组件的 对象
-
有人帮我们保存 组件的 对象
-
有人帮我们自动组成
-
有人替我们管理事务
-
有人协助我们整合其他的框架
3.1.3Spring充当组件管理角色(IOC)
那么谁来帮我们完成这些期待呢?
是Spring框架来实现的
组件可以完全交给Spring框架来进行管理,Spring框架替代了程序员原有的new对象,和对象属性赋值的动作
Spring具体组件管理动作包含:
-
组件的对象实例化
-
组件属性属性赋值
-
组件对象之间的引用
-
组件对象的存活周期的管理
我们只需要编写 元数据(配置文件->xml/注解配置/java类来配置) 告知spring哪些是需要它管理的
组件:可以复用的java对象
组件一定是对象,对象不一定是组件
3.1.4 Spring优势
-
降低组件之间的糅合性:Spring IOC通过依赖注入机制,将组件之间的管理依赖削弱,减少程序组件之间的耦合性
-
提高了代码的可重用性和可维护性:组件的实例化过程/依赖关系的管理/交给spring ioc去处理,使得组件代码更加模块化,可重用,更易于维护
-
方便了配置和管理:Spring IOC通过xml文件或者注解来对组件进行配置和管理,将组件的切换,替代操作更加便捷
-
交给Spring管理的对象(组件),可以享受享受Spring框架的其他功能(AOP事务管理)
3.2 Spring Ioc容器和容器实现
3.2.1普通和复杂容器
普通容器:普通容器只能储存
程序中的普通容器:
-
数组
-
集合:List:元素有序放入,元素可以重复
-
集合:Set:元素无序放入,元素不可重复,无索引->检索效率低,crud效率高
名称 特点 原理 HashSet 无序,不重复,无索引 底层是基于哈希表来储存的数据 JDK8以前,hashtable是由数组和链表组成的,在JDK8以后,是由数组+链表+红黑树组成的/哈希值:是jdk根据对象的地址,按照规则算出的int类型的数值->同一个对象,多次调用hashCode()方法返回的哈希值是相同的 String address = "岳阳市"; address.hashcode();获取到25299637(通过字符串的地址算出来的int类型的值)/哈希算法: 元素的哈希值和数组的长度求余数算出应该存入的位置,比如数组长度是 16,哈希值和16取余数,就一定是0到15之间的数字->JDK7新元素占用老元素的位置,并且新元素会指向老元素.JDK8以后:新元素挂载在老元素的后面 LinkedHashSet 有序,不重复,无索引 在哈希表的原理基础上,为每一个元素又额外的多了一个双链表的机制记录储存的顺序 TreeSet 默认升序,不重复,无索引 根据红黑树来实现的
复杂容器:复杂容器可以储存,还可以管理其中的对象
程序中的复杂容器:
Servlet容器可以管理Servlet(init/service/destory) Filter,Listener这样组件的一生,所以是一个复杂容器
名称 | 时机 | 次数 |
---|---|---|
创建对象 | 默认时机:接收到第一次请求 | 一次 |
初始化操作 | 创建对象之后 | 一次 |
处理请求 | 接收到请求 | 多次 |
销毁操作 | Web应用卸载之前 | 一次 |
我们将要学习的SpringIOC容器是一个复杂容器,它不仅会负责创建组件的对象,储存组件的对象,而且负责调用组件的方法让它们工作,最终在特定情况下销毁组件
总结:Spring管理的容器:就是一个复杂组件,不仅储存组件,而且可以管理组件之间的依赖关系,并且可以创建和销毁组件
3.2.2 SpringIOC的容器介绍
3.2.3 Spring IOC具体的接口和实现类
A.SpringIOC接口:
BeanFactory接口提供了高级配置机制:可以管理任何类型的对象
ApplicationContext是Beanfactory子接口,扩展了如下功能:
-
更容易与Spring的AOP功能集成
-
资源消息处理
-
特定于应用程序给予这个接口的实现,例如Web应用程序的WebApplicationContext
BeanFactory提供了配置框架和基本功能,而 ApplicationContext添加更多特定于企业的功能.ApplicationContext是BeanFactory的超集
B.ApplicationContext实现类:
类型名 | 简介 |
---|---|
ClassPathXmlApplicationContext | 通过读取类路径下的XML格式的配置文件来创建IOC的容器对象 |
FileSystemXmlApplicationContext | 通过文件系统路径,来读取XML格式的配置文件创建IOC容器对象 |
AnnotationConfigApplicationContext | 通过读取Java的配置类创建IOC的容器对象 |
WebApplicationContext | 专门为Web应用准备,基于Web环境,常见IOC容器对象,并且将其存入 ServletContext中 |
我们主推的是 注解+配置类的方式:迎合Springboot不用Xml,并且xml这种配置方式逐渐被淘汰了
3.2.4 SpringIOC容器配置方式
A.xml配置
B.注解配置
C.java的配置类
3.3 Spring Ioc/DI 概念总结
3.3.1 IOC容器
Spring IOC容器:负责实例化,配置和组装bean(组件).容器通过读取配置元数据来获取有关重要实例化,配置和组装组件的指令
3.3.2 IOC(inversion of Control)控制反转
IOC主要是针对对象的创建和调用来说的,也就是说,当程序需要使用一个对象的时候,不再是应用程序直接创建该对象,而是让IOC容器来创建和管理对象,也就是说控制权从应用程序转移到了IOC容器中,也就是"反转了"控制权,这种方法一般上是通过依赖查找的方式实现的,也就是说IOC容器维护着构成应用程序的对象,并且负责创建这些对象
3.3.3 DI(Dependency Injection)依赖注入
DI是指在组件之间传递依赖关系的过程中,将依赖关系在容器内部处理,这样就不必在应用程序代码中硬编码对象之间的依赖关系,实现了对象之间的解耦合,在Spring中,DI是通过XML配置文件或注解的方式实现的,它提供了三种形式的依赖注入:构造函数注入,Setter方法注入,接口注入
四:Spring Ioc的实践和应用
4.1SpringIOC/DI实现步骤
1.配置元数据(配置)
<?xml version="1.0" encoding="UTF-8"?> //此处添加一些约束 <beans xmlns="" xnlns:xs1:"" xsi:schemaLocation=""></beans> <bean id="" class=""></bean> //一个bean就是一个属性信息 id是组件对象的标识 class是你要实例化的类
2.实例化IOC容器
提供给ApplicationContext构造函数的路径是资源字符串地址,允许容器从外部资源(本地文件系统/Java class/)加载文件数据
我们应该选择一个合适的容器实现类,来进行Ioc的实例化工作
//实例化ioc容器,读取外部配置文件,最终在容器内进行ioc和di动作 ApplicationContext context = new ClassPathXmlApplicationContext("services.xml","daos".xml); //接口+实现类来实现多态
3.获取组件
ApplicationContext是一个高级工厂的接口,可以维护不同的bean以及依赖项的注册表,通过使用方法 T getBean(Stringname,Class<T>requriedType),您可以检索bean的实例
//创建ioc的容器对象,指定配置文件,ioc也开始实例组件对象 ApplicationContext context = new ClassPathXmlApplicationContext("services.xml","daos.xml"); //获取ioc容器的组件对象 PetStoreService service = context.getBean("petStore",PetStoreService.class); //使用组件对象 List<String> userList = service.getUsernameList();
4.2基于XML配置方式组件管理
4.2.1 实验一:组件(Bean)信息声明配置(IOC)
1.目标:
SpringIOC容器管理一个或多个Bean,这些Bean是使用配置文件创建的
我们学习,如何定义XML配置文件,声明组件类信息,交给SpringIOC的容器进行组件管理
2.思路
实例化一个类的方案:
实例化的类型 | 细节分布 |
---|---|
构造函数实例化 | 无参数构造函数/有参数构造函数实例化 |
工厂模式实例化 | 静态工厂/非静态工厂 |
不同的实例化方式对象和组件ioc配置方式是不同的
3.无参构造函数的声明方法
package com.atguigu.ioc_01; public class HappyComponent { public void doWork(){ System.out.println("HappyComponent doWork"); } }
<?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:组件的标识-要唯一,方便后期读取 class:组件的类的权限定符 > 下面实际是将一个组件声明了两个组件信息 因为默认会单例模式,因此会实例化两个组件对象 相当于就是new了两个对象 --> <bean id="happyComponent" class="com.atguigu.ioc_01.HappyComponent"> </bean> <bean id="happyComponent2" class="com.atguigu.ioc_01.HappyComponent"></bean> </beans>
4.静态工厂类
package com.atguigu.ioc_01; public class ClientService { private static ClientService clientService = new ClientService(); private ClientService(){}; public static ClientService createInstance(){ return clientService; } //因为静态方法可以直接调用 不需要实例化 }
<?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"> <!-- 2.静态工厂类如何声明工厂方法进行ioc的配置 id= class="工厂类的全限定符" factory-method="工厂类中的静态方法名" --> <bean id="clientService" class="com.atguigu.ioc_01.ClientService" factory-method="createInstance"> </bean> </beans>
5.非静态工厂模式如何创建
package com.atguigu.ioc_01; public class DefaultServiceLocator { private static clientServiceImpl clientService = new clientServiceImpl(); public clientServiceImpl createServiceInstance(){ return clientService; } }
<?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"> <!--3.非静态工厂如何声明ioc配置--> <!--3.1配置工厂类的组件信息--> <bean id="defaultServiceLocator" class="com.atguigu.ioc_01.DefaultServiceLocator"></bean> <!--3.2通过指定非静态工厂对象和方法名来配置生成的ioc信息--> <bean id="clientService2" factory-bean="defaultServiceLocator" factory-method="createServiceInstance"></bean> </beans>
6.静态工厂类和非静态类的区分
设计模式->工厂模式 | 特点 |
---|---|
静态工厂 | 1.不需要实例化工厂类 2.类的方法用static修饰 |
实例工厂 | 1.需要实例化工厂类 2.类的方法不需要static修饰 |
public class Car { private String brand; private Car(String brand) { this.brand = brand; } public String getBrand() { return brand; } public static Car createCar(String brand) { return new Car(brand); } } public class Main { public static void main(String[] args) { Car myCar = Car.createCar("Toyota"); System.out.println("My car brand is: " + myCar.getBrand()); } } /*上方是静态工厂 下方是实例工厂*/ public class CarFactory { private String brand; public CarFactory(String brand) { this.brand = brand; } public Car createCar() { return new Car(brand); } } public class Main { public static void main(String[] args) { CarFactory factory = new CarFactory("Toyota"); Car myCar = factory.createCar(); System.out.println("My car brand is: " + myCar.getBrand()); } }
4.2.2 实验二:组件(Bean)依赖注入配置(DI)
1.目标
通过配置文件,实现IOC容器中Bean之间的引用(依赖注入DI配置)
主要涉及的注入场景:基于构造函数的依赖注入和基于Setter的依赖注入
2.思路
3.基于构造函数的依赖注入(单个构造函数)
a.介绍:
基于构造函数的DI是通过容器调用具有多个参数的构造函数来完成的,每一个参数就是一个依赖项,下面事例演示一个只能通过构造函数注入进行依赖注入的类
b.准备组件类
package com.atguigu.ioc_02; public class UserDao { }
package com.atguigu.ioc_02; 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"> <!--引用和被引用的组件 必须全部都在ioc容器中--> <!--SpringIOC是一个高级容器,内部会有缓存动作,线创建对象ioc--> <!--1.单个构造参数注入--> <!--步骤1.将它们都放在ioc容器中--> <bean id="userDao" class="com.atguigu.ioc_02.UserDao"></bean> <!--<constructor-arg 构造参数传值 value = 直接属性值 String name="二狗子" int age =18 ref = 引用其他bean 其他bean的id值 --> <bean id="userService" class="com.atguigu.ioc_02.UserService"> <constructor-arg ref="userDao"></constructor-arg> </bean> </beans>
4.基于构造函数的依赖注入(多个构造函数)
a.介绍:
基于构造函数的DI是通过容器调用具有多个参数的构造函数来完成的 每个参数表示一个依赖项
b.准备组件类
package com.atguigu.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.atguigu.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"> <!--引用和被引用的组件 必须全部都在ioc容器中--> <!--SpringIOC是一个高级容器,内部会有缓存动作,线创建对象ioc--> <!--2.多个构造参数注入--> <bean id="userService1" class="com.atguigu.ioc_02.UserService"> <!--方案1:value是直接赋值 ref是引用 这里是按顺序来的--> <constructor-arg value="18"/> <constructor-arg value="张三"/> <constructor-arg ref="userDao"/> </bean> <bean id="userService2" class="com.atguigu.ioc_02.UserService"> <!--方案2推荐:value是直接赋值 ref是引用 构造参数的名字注明--> <constructor-arg name="name" value="张三"/> <constructor-arg name="age" value="18"/> <constructor-arg ref="userDao"/> </bean> <bean id="userService3" class="com.atguigu.ioc_02.UserService"> <!--方案3:value是直接赋值 ref是引用 参数的下角标从左到右从0开始 age=0 name=1 userDao=2--> <constructor-arg index="1" value="张三"/> <constructor-arg index="0" value="18"/> <constructor-arg index="2" ref="userDao"/> </bean> </beans>
5.基于Setter方法来依赖注入(重点中的重点)
package com.atguigu.ioc_02; public class SimpleMovieLister { private movieFinder movieFinder; private String moiveName; public void setMoiveFinder(movieFinder movieFinder){ this.movieFinder=movieFinder; } public void setMoiveName(String moiveName){ this.moiveName=moiveName; } }
package com.atguigu.ioc_02; public class movieFinder { }
<?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"> <!--引用和被引用的组件 必须全部都在ioc容器中--> <!--SpringIOC是一个高级容器,内部会有缓存动作,线创建对象ioc--> <!-- 3.触发Set方法进行注入--> <bean id="movieFinder" class="com.atguigu.ioc_02.movieFinder"></bean> <bean id="simpleMovieLister" class="com.atguigu.ioc_02.SimpleMovieLister"> <property name="moiveName" value="消失的她"/> <property name="moiveFinder" ref="movieFinder"></property> </bean> <!-- name->属性名 setter方法 去set和首字母小写的值! setMovieFinder -> movieFinder values和ref是二选一的--> </beans>
4.2.3 实验三:IOC容器的创建和使用
A.IOC容器的创建
<?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="HappyComponent" class="com.atguigu.ioc_03.HappyComponent"></bean> </beans>
package com.atguigu.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringIoCtest { //讲解如何创建Ioc容器并且读取配置文件 /* * 接口: * BeanFactory * ApplicationContext * 实现类: * ClassPathXmlApplicationContext 读取类路径下的xml配置方式 读class文件 * FileSystemXmlApplicationContext 读取指定文件位置的xml配置方式 * AnnotationConfigApplicationContext 读配置类 * WebApplicationContext 读取Web项目专属的配置ioc容器 * 可以通过构造函数实例化 * */ public void createIoc(){ //ioc和di //方案1: ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-03.xml"); //方案2:先创建ioc创建,再指定配置文件,再刷新 //创建容器 和配置文件指定分开 ClassPathXmlApplicationContext applicationContext1 = new ClassPathXmlApplicationContext(); applicationContext1.setConfigLocations("spring-03.xml"); applicationContext1.refresh(); } public void getBeanFromIOC(){ //讲解如何在Ioc容器中获取组件 } }
B.IOC容器的使用
package com.atguigu.test; import com.atguigu.ioc_03.HappyComponent; import org.junit.jupiter.api.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringIoCtest { //讲解如何创建Ioc容器并且读取配置文件 /* * 接口: * BeanFactory * ApplicationContext * 实现类: * ClassPathXmlApplicationContext 读取类路径下的xml配置方式 读class文件 * FileSystemXmlApplicationContext 读取指定文件位置的xml配置方式 * AnnotationConfigApplicationContext 读配置类 * WebApplicationContext 读取Web项目专属的配置ioc容器 * 可以通过构造函数实例化 * */ public void createIoc(){ //ioc和di //方案1: ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-03" + ".xml"); //方案2:先创建ioc荣建,再指定配置文件,再刷新 //创建容器 和配置文件指定分开 ClassPathXmlApplicationContext applicationContext1 = new ClassPathXmlApplicationContext(); applicationContext1.setConfigLocations("spring-03.xml"); applicationContext1.refresh(); } @Test public void getBeanFromIOC(){ //讲解如何在Ioc容器中获取组件 //1.创建ioc容器 ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(); applicationContext.setConfigLocations("spring-03.xml"); applicationContext.refresh(); //2.读取ioc容器的组件 //方案1.直接根据beanId获取(需要强转 不推荐) HappyComponent happyComponent = (HappyComponent) applicationContext.getBean("HappyComponent"); //方案2:根据beanId,同时指定bean的类型 Class HappyComponent happyComponent1 = applicationContext.getBean("HappyComponent",HappyComponent.class); //方案3:根据Bean的类型获取 //TODO 根据bean的类型获取,同一个类型,在ioc容器中只有一个bean //TODO 如果ioc容器存在多个同类型的bean,会出现NoUniqueDefinitionException //TODO ioc的配置一定是实现类,但是可以根据接口来获取值 getBeans HappyComponent happyComponent3 = applicationContext.getBean(HappyComponent.class); System.out.println(happyComponent3==happyComponent1); } }
4.2.4 实验四:高级特性: 组件(Bean)作用域和周期方法配置
4.2.4.1.组件周期方法配置
a.周期方法概念
我们可以在组件内定义方法,然后当IOC容器实例化和销毁对象组件的时候调用,这两个方法是我们成为生命周期方法
类似于Servlet的Init/destory方法
b.周期方法声明
package com.atguigu.ioc_04; public class JavaBean { /* 必须是public/必须是void/必须是无参数/命名随意 初始化方法->初始化业务逻辑 void是为了方便反射 */ public void init(){ System.out.println("JavaBean.init"); } public void clear(){ System.out.println("JavaBean clear"); } }
@Test public void test_04(){ //创建IOC容器 ClassPathXmlApplicationContext test04 = new ClassPathXmlApplicationContext("spring-04.xml"); //ioc 容器去调用destroy //ioc会立刻释放 死了 //2.正常结束ioc容器 test04.close(); } }
c.周期方法配置
<?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"> <!-- init-method= "初始化方法名" destory-method="销毁方法名" spring ioc容器就会在对应的时间节点回调对应的方法 我们在其中些对应的业务就可以了 --> <bean id="javaBean" class="com.atguigu.ioc_04.JavaBean" init-method="init" destroy-method="clear"></bean> </beans>
4.2.4.2.组件作用域配置
a.Bean作用域
<Bean 标签只是声明Bean 只是将Bean的信息配置给Springioc容器
在ioc容器中,这些<bean标签就会转化为Spring内部的BeanDefinition对象,在Beandefinition对象中,就包含了定义的信息,比如id class等等
这意味着,BeanDefinition与类的概念一样,SpringIOC容器可以根据BeanDefinition对象去反射创建多个Bean对象实例
具体创建多少个Bean的对象实例,由Scope属性指定
b.作用域可选值:
取值 | 含义 | 创建对象的时机 | 默认值 |
---|---|---|---|
singleton | 在IOC容器中,这个bean对象始终为单实例 | IOC容器初始化时 | 是 |
prototype | 这个bean在IOC容器中有多个实例 | 获取bean的时候 | 否 |
取值 | 含义 | 创建对象的时机 | 默认值 |
---|---|---|---|
requrest | 请求范围内有效的实例 | 每次请求 | 否 |
session | 会话范围内有效的实例 | 每次会话 | 否 |
c.作用域的配置
其实就是修改xml中bean的scope
<?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"> <!-- init-method= "初始化方法名" destory-method="销毁方法名" spring ioc容器就会在对应的时间节点回调对应的方法 我们在其中些对应的业务就可以了 --> <bean id="javaBean" class="com.atguigu.ioc_04.JavaBean" init-method="init" destroy-method="clear"></bean> <!--声明一个组件信息!默认就是单例模式 一个bean对应一个beanDefinition--> <bean id="javaBean2" class="com.atguigu.ioc_04.JavaBean2" scope="prototype"></bean> </beans>
@Test public void test_04(){ //创建IOC容器 ClassPathXmlApplicationContext test04 = new ClassPathXmlApplicationContext("spring-04.xml"); //ioc 容器去调用destroy //ioc会立刻释放 死了 //2.正常结束ioc容器 JavaBean2 bean1 = test04.getBean(JavaBean2.class); JavaBean2 bean2 = test04.getBean(JavaBean2.class); System.out.println(bean1==bean2);//这里是在判断是否为单例模式,如果是单例模式 bean1和bean2是完全一样的 } }
4.2.5 实验五:高级特性: FactoryBean特性和使用
4.2.5.1.FactoryBean的认知
FactoryBean用于配置复杂的Bean对象,可以将创建过程储存在FactoryBean的getObject方法中
FactoryBean<T>提供三种方法
-
T getObject();返回此工厂创建对象的实例,这个返回值会被储存到IOC容器
-
boolean isSingleton()l如果返回单例,则返回true,否则返回false.默认实现返回true
-
Class<?>getObjectType();返回getObject()方法返回的对象类型,如果事先不知道类型,则返回Null
4.2.5.2.FactoryBean运用
a.代理类的创建
b.第三方框架整合:Mybatis的内容
c.复杂对象实例化
4.2.5.3.FactoryBean实际情况
package com.atguigu.ioc_05; public class JavaBean { private String getName; public JavaBean() { } public JavaBean(String getName) { this.getName = getName; } /** * 获取 * @return getName */ public String getGetName() { return getName; } /** * 设置 * @param getName */ public void setGetName(String getName) { this.getName = getName; } public String toString() { return "JavaBean{getName = " + getName + "}"; } }
package com.atguigu.ioc_05; import org.springframework.beans.factory.FactoryBean; /* * 步骤1:实现FactoryBean接口<返回值类型> * * * * * */ 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; } }
<?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"> <!-- id - > getObject方法返回的对象标识 Class->FactoryBean的工厂类 --> <bean id="javaBean" class="com.atguigu.ioc_05.JavaBeanFactoryBean"></bean> </beans>
@Test public void test_05(){ //1创建IOC容器 ClassPathXmlApplicationContext test05 = new ClassPathXmlApplicationContext("spring-05/.xml"); //2.读取组件 JavaBean Javabean = test05.getBean("Javabean",JavaBean.class); System.out.println("JavaBean="+Javabean); //TODO FactoryBean工厂也会加入到IOC容器中 Object bean = test05.getBean("&JavaBean"); System.out.println("bean"+bean); } }
4.2.6 实验六:基于XML方式整合三层架构组件
CREATE TABLE students( id INT PRIMARY KEY, name VARCHAR(50) NOT NULL, gender VARCHAR(10) NOT NULL, age INT, class VARCHAR(50) ); drop table students; INSERT INTO students (id, name, gender, age, class) VALUES (1, 'Alice Smith', 'Female', 22, 'Class A'); INSERT INTO students (id, name, gender, age, class) VALUES (2, 'Bob Johnson', 'Male', 21, 'Class B'); INSERT INTO students (id, name, gender, age, class) VALUES (3, 'Cathy Brown', 'Female', 23, 'Class C'); INSERT INTO students (id, name, gender, age, class) VALUES (4, 'David Wilson', 'Male', 20, 'Class A'); INSERT INTO students (id, name, gender, age, class) VALUES (5, 'Eva Davis', 'Female', 22, 'Class B'); INSERT INTO students (id, name, gender, age, class) VALUES (6, 'Frank White', 'Male', 21, 'Class C'); INSERT INTO students (id, name, gender, age, class) VALUES (7, 'Grace Lee', 'Female', 23, 'Class A'); INSERT INTO students (id, name, gender, age, class) VALUES (8, 'Henry Clark', 'Male', 20, 'Class B');
<?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/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <context:property-placeholder location="classpath:jdbc.properties"/> <!--druid--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="url" value="${atguigu.url}"></property> <property name="driverClassName" value="${atguigu.driver}"></property> <property name="username" value="${atguigu.username}"/> <property name="password" value="${atguigu.password}"></property> </bean> <!--jdbcTemplate--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <!--dao配置di jdbcTemplate--> <bean id="studentDao" class="com.atguigu.dao.impl.StudentDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate"></property>" </bean> <!--service配置di dao--> <bean id="studentService" class="com.atguigu.service.impl.StudentServiceImpl"> <property name="studentDao" ref="studentDao" /> </bean> <!--controller配置di service--> <bean id="studentController" class="com.atguigu.controller.StudentController"> <property name="studentService" ref="studentService"></property> </bean> </beans>
4.3基于 注解 方式管理Bean
4.3.1 实验一: Bean注解标记和扫描(IOC)
4.3.1.1.注解是什么:
和XML配置文件一样,注解本身不能执行,注解只是提供一个标记,具体的功能是框架检测到注解标记的位置,然后针对这个位置按照注解标记的功能来进行具体操作
本质上:一切的操作都是由Java代码来完成的,XML和注解只是告诉框架中的Java代码,如何去运行
举例:元旦联欢会要布置教室,蓝色的地方要贴上元旦快乐,红色的地方贴上拉花,黄色的地方贴上气球.班长做了所有的标记,同学们来完成具体工作,墙上的标记就相当于我们做的注解,同学们做的工作,就是框架执行的内容
4.3.1.2扫描理解
Spring为了知道程序员在哪些地方做了哪些注解,就需要通过扫描功能来实现
4.3.1.3.准备Spring项目的组件:
4.3.1.4.组件标记注解和区别:
注解 | 说明 |
---|---|
@Component | 该注解用于描述Spring中的Bean,它是一个泛化的概念,仅仅表示容器中的一个组件(Bean),并且可以作用在任何层次,比如Service层,Dao层.使用时只需要将该注解标注在相应类上即可 |
@Repository | 该注解用于将数据访问层(Dao层)的类标识为Spring中的Bean,其功能与@Component相同 |
@Service | 此注解通常作用在业务层(Service层)用于将业务层的类标识为Spring中的Bean,其功能与Component相同 |
@Controller | 此注解通常作用在控制层(如SpringMVC中的controller)用于将控制层的类标识为Spring中的Bean,其功能与@Component相同 |
常用的配置Spring的xml文档
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>spring-xml-practice-02</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>21</maven.compiler.source> <maven.compiler.target>21</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <!-- Spring的核心工具包--> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.0.8.RELEASE</version> </dependency> <!--在基础IOC功能上提供扩展服务,还提供许多企业级服务的支持,有邮件服务、 任务调度、远程访问、缓存以及多种视图层框架的支持--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.8.RELEASE</version> </dependency> <!-- Spring IOC的基础实现,包含访问配置文件、创建和管理bean等 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.0.8.RELEASE</version> </dependency> <!-- Spring context的扩展支持,用于MVC方面 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>5.0.8.RELEASE</version> </dependency> <!-- Spring表达式语言 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>5.0.8.RELEASE</version> </dependency> <!-- Java注解包提供@Resource注解 --> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.0.8.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.46</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.0.8.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.8.RELEASE</version> </dependency> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.2</version> </dependency> </dependencies> </project>
实质上:Controller/Service/Repository的本质就是Component注解基础上起了三个新的名字
组件深层解析:对于Spring使用IOC容器管理这些组件,其实没有本质区别,也就是在语法层面上不存在区别.所以说@Controller,@Service,@Repository三个注解本质是给开发人员看的,让我们能便于分辨组件的使用
//@Component == <bean id="" class="CommonComponent"></bean> //我们不难发现 @Component组件是为了简化Xml操作的,实际上底层还是用的Xml操作 //1.标记注解 @Component //2.配置xml来指定包
4.3.1.5.配置文件确定扫描范围
A情况:基本扫描配置
<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 http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 这里使用了正确的 context 命名空间 --> <!--1.普通配置包扫描--> <!--base-package 指定ioc容器去哪些包下查找注解类 可以是单个包或者多个包 com.atguigu,com.atguigu.xxx 包,包 指定包:指定包中的所有类 指定包及其子包:指定包及其子包中的所有类--> <context:component-scan base-package="com.atguigu"/> <!-- 其他的bean配置 --> <!-- 更多的bean配置 --> </beans>
B情况:指定排除组件
<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 http://www.springframework.org/schema/context/spring-context.xsd"> <!--2.指定包排除注解--> <!-- 我们有三层架构:Controller Service Repository webioc会储存Controller 例如com.atguigu.controller rootioc会储存Service和Repository 所以rootioc可以排除@Controller注解,因为这个ioc容器里面不会放和web相关的内容 我们为了节约内存,所以需要排除包 <context:exclude-filter type= expression= > 就是用于排除的方法 --> <context:component-scan base-package="com.atguigu"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> </beans>
C情况:指定包含组件
<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 http://www.springframework.org/schema/context/spring-context.xsd"> <!--3.指定包一定包含注解--> <!-- basePackage->包下全部都成立 但是include->只包含,因此我们要关闭默认的过滤器 我们2.中就是在默认过滤器的基础上,去选择不扫描Repository和Controller 但是在3中,我们是只要,因此我们就关闭默认的过滤器,然后把Repository设定为我们要包含的组件标签 --> <context:component-scan base-package="com.atguigu" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/> </context:component-scan> </beans>
4.3.2 实验二: 组件(Bean)作用域和周期方法注解
4.3.2.1组件周期方法的配置
a.周期方法的概念
我们可以在组件类中定义方法,然后当ioc容器实例化和销毁组件对象时候进行调用,这两个方法我们称为声明周期方法
b.周期方法的声明
public class BeanOne{ //生命周期方法要求:命名随意的 但是要求必须是public void的无参列表 @PostConstruct //初始化方法注解 public void init(){ //初始化逻辑 } } public class BeanTwo{ @PreDestroy//注解指定销毁方法 public void cleanup(){ //释放资源逻辑 } } //相较于纯xml配置,你不必在spring-ioc.xml这类文件中写<bean>来声明
4.3.2.2组件作用域配置
a.Bean作用域的概念:
<bean标签声明Bean,只是将Bean的信息传递给SpringIOC容器
在IOC容器中,这些<bean标签对应的信息,会转化成BeanDefinition对象,而在BeanDefinition对象中,包含定义的信息(id class)
这意味着,BeanDefinition就和类概念一样,SpringIOC容器可以根据BeanDefinition对象反射创建多个Bean对象实例
具体创建多少个Bean,由Bean作用域Scope决定
b.作用域可选值
取值 | 含义 | 创建对象的时机 | 默认值 |
---|---|---|---|
singleton | 在IOC容器中,bean的对象始终为单实例 | IOC容器初始化的时候 | 是 |
prototype | 这个Bean在IOC容器中有多个实例 | 获取Bean的时候 | 否 |
如果在WebApplicationContext下,会有另外两个作用域(不常用)
取值 | 含义 | 创建对象的时机 | 默认值 |
---|---|---|---|
request | 请求范围内有效的实例 | 每次请求 | 否 |
session | 会话范围内有效的实例 | 每次对话 | 否 |
c.作用域配置
@Scope(ScopeName= ConfigurableBeanFactory.SCOPE_SINGLETON)//单例默认值 @Scope(ScopeName= ConfigurableBeanFactory.SCOPE.PROTOTYPE)//多例 二选一 public class BeanOne{ //周期方法要求:命名随意,但是方法一定是返回值为空的public方法 @PostConstruct public void init(){ //初始化逻辑 } }
4.3.3 实验三: Bean属性赋值: 引用类型自动装配(DI)
@Controller public class UserController { /** * 复习使用xml方式插入注解 注意 property这个成立的前提是name指的那个set方法存在,并且name值为set方法去掉set和余下首字母小写的结果 ref是引用类型 value是值类型,引用类型填的是已经声明的bean的id * <bean id="UserService" class="class地址"></bean> * <bean id="UserController" class="class地址"> * <property name="userService(set方法的名字去掉set和首字母小写)" ref="UserService"></property> * </bean> *我们用@AutoWwired实际上就是替代了上述的操作 简化了xml操作 实现了自动化的di * * */ @Autowired//这里等价于<property userService ->对应类型的bean装配 //所以说 autowired叫做注解的自动装配 private UserService userService; public void show(){ //调用业务层的show方法 } }
@Autowired注解细节:
a.标记位置:
1.成员变量:
这也是主要的使用方式 与xml进行bean ref不同, 使用@Autowired不必有set方法
@Service("smallDog") public class SoldierService{ @Autowired private SoldierDao soldierDao; public void getMessage(){ soldierDao.getMessage(); } }
2.构造器:
@Controller(value="tianDog") public class SoldierController{ private SoldierService soldierService; @Autowired public SoldierController(SoldierService soldierService){ this.soldierService = soldierService; } }
3.setXxx方法
@Controller(value="tiangou") public class SoldierController{ private SoldierService soldierService; @Autowired public void setSoldierService(SoldierService soldierService){ this.soldierService = soldierService; } }
b.工作流程
-
首先根据所需要的组件类型在IOC容器中寻找
-
能找到唯一的bean:直接执行装配
-
如果完全找不到匹配的这个bean:装配失败
-
和所需类型匹配的bean不止一个
-
没有@Qualifier注解: 根据@Autowired标记位置成员变量名作为bean的id进行匹配
-
能找到:执行装配
-
找不到:装配失败
-
-
有@Qualifier注解:根据@Qualifier注解中指定的名称作为bean的id进行匹配
-
能找到:执行装配
-
找不到:装配失败
-
-
-
package com.atguigu.ioc_03; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Controller; @Controller public class UserController { @Autowired @Qualifier("userServiceImpl") /* * 1.Qualifier对Autowired是依赖的 * 在 Spring 中,@Qualifier 注解用于指定具体要注入的 Bean, * 解决自动装配时的歧义性。当一个接口有多个实现类时, * Spring 无法确定要注入哪个 Bean,这 * 时就可以使用 @Qualifier 注解来指定具体要注入的 Bean。 * */ private UserService userService; public void show() { // 调用业务层的show方法 userService.show(); } }
4.3.4 实验四: Bean属性赋值: 基本类型属性赋值(DI)
@Value通常用于注入外部化属性
声明外部配置
application.properties
catalog.name=MovieCatalog
xml引入外部配置
<context:property-placeholder location="application.properties"></context:property-placeholder>
@Value注解读取配置
@Component public class CommonComponent{ @Value(${catalog:hahaha})//这个:之后是默认值 private String name; public String getName(){ return name; } public void setName(String name){ this.name=name; } }
4.3.5 实验五: 基于注解+XML方式 整合三层架构组件
4.4基于 配置类 方式管理Bean
4.4.1 完全注解开发理解
4.4.2 实验一: 配置类和扫描注解
package com.atguigu.config; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; /** * @program: spring-ioc-java-06 * @description: * @author: XiaoYongCai * @Goal: * @create: 2024-03-19 14:25 * 1.包扫描注解配置 * 2.引用外部的配置文件 * 3.声明第三方依赖的bean组件 * 步骤: * 1.添加@Configuration 代表我们是配置类 * 2.实现上述功能注解 **/ @ComponentScan(value="com.atguigu.ioc_01") @PropertySource(value="classpath:jdbc.properties") @Configuration//证明你是配置类 public class JavaConfiguration { }
4.4.3 实验二: @Bean定义组件
/** * @program: spring-ioc-java-06 * @description: * @author: XiaoYongCai * @Goal: * @create: 2024-03-19 14:25 * 1.包扫描注解配置 * 2.引用外部的配置文件 * 3.声明第三方依赖的bean组件 * 步骤: * 1.添加@Configuration 代表我们是配置类 * 2.实现上述功能注解 **/ @ComponentScan(value="com.atguigu.ioc_01") @PropertySource(value="classpath:jdbc.properties") @Configuration//证明你是配置类 public class JavaConfiguration { @Value("$atguigu.url") private String url; @Value("$atguigu.driver") private String driver; @Value("$atguigu.username") private String uesename; @Value("$atguigu.password") private String password; @Bean//证明其是一个添加第三方变量的bean组件 /* * 这里实际上与 * <bean id class => <property name = "" value=; * * </bean> * 效果是一致的,只是避免了Xml的繁琐配置,精简为配置类实现 * * */ public DruidDataSource dataSource(){ DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl(url); dataSource.setDriverClassName(driver); dataSource.setUsername(uesename); dataSource.setPassword(password); return dataSource; } }
4.4.4 实验三: 高级特性: @Bean注解细节
问题1:BeanName的问题->默认是方法名 指定:name/value属性来起名 覆盖方法名
问题2:周期方法如何指定->原有解决方案:PostConstruct+PreDestroy注解指定
->Bean注解指定:initMethod/destoryMethod指定
问题3:作用域:
还有用 @Scope指定,只是默认单例
@Scope(scopeName=ConfigurableBeanFanctory.SCOPE.SINGLETON) @Bean(name="ergouzi1",initMethod = "",destroyMethod = "")
4.4.5 实验四: 高级特性: @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
package com.atguigu.config; import org.springframework.context.annotation.Configuration; @Configuration public class JavaConfigurationB { } /** * @program: spring-ioc-java-06 * @description: * @author: XiaoYongCai * @Goal: * @create: 2024-03-19 16:20 **/
package com.atguigu.config; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @Import( value = JavaConfigurationB.class) @Configuration public class JavaConfigurationA { } /** * @program: spring-ioc-java-06 * @description: * @author: XiaoYongCai * @Goal: * @create: 2024-03-19 16:19 **/
4.4.6 实验五: 基于注解+配置类整合方式 整合三层架构组件
4.5三种配置方式总结
4.5.1 XML方式配置总结
1.所有内容都写到xml格式配置文件中
2.声明bean通过<bean标签
3.<bean标签包含基本信息(id,class)和属性信息<property name value/ref>
4.引入外部的properties文件可以通过<context:property-placeholder
5.IOC具体容器实现选择ClassPathXmlApplicationContext对象
4.5.2 XML+注解方式配置总结
1.注解负责标记IOC的类和进行属性装配
2.xml文件依然重要,需要通过<context:component-scan标签来指定注解范围
3.标记IOC注解 @Component @Service @Controller @Repository
4.标记DI注解 @Autowired @Qualifier @Resource @Value
5.具体容器选择ClassPathXmlApplicationContext对象
4.5.3完全注解方式配置总结
1.完全注解方式是指去掉xml文件,使用配置类+注解来实现
2.xml文件替换成使用Configuration注解标记的类
3.标记IOC注解:@Component @Service @Controller @Repository
4.标记DI注解:@Autowired @Qualifier @Resource @Value
5.<context:component-scan 标记指定注解范围用
@ComponentScan替代
6.<context:property-placeholder 引入外部配置文件
7.<bean标签可以用@bean替代
8.IOC具体容器选择AnnotationApplicationContext对象
4.6整合Spring5-Test5搭建测试 环境
五:SpringAOP面向切面编程
5.1 场景设定和问题复现
package com.atguigu; public interface Calculator { int add(int i,int j); int sub(int i,int j); int mul(int i,int j); int div(int i,int j); } /** * @program: Spring-aop-annotation-09 * @description: * @author: XiaoYongCai * @Goal: * @create: 2024-03-19 21:30 **/
package com.atguigu; public class CalculatirPureImpl implements Calculator{ @Override public int add(int i,int j){ System.out.println("i = " + i + ", j = " + j); int result = i+j; System.out.println("result = " + result); return result; } @Override public int sub(int i,int j){ System.out.println("i = " + i + ", j = " + j); int result = i-j; System.out.println("result = " + result); return j; } @Override public int mul(int i,int j){ System.out.println("i = " + i + ", j = " + j); int result = i*j; System.out.println("result = " + result); return result; } @Override public int div(int i,int j){ System.out.println("i = " + i + ", j = " + j); int result = i/j; System.out.println("result = " + result); return result; } } /** * @program: Spring-aop-annotation-09 * @description: * @author: XiaoYongCai * @Goal: * @create: 2024-03-19 21:31 **/
代码问题分析:
a.代码缺陷:
-
对核心业务功能有干扰,程序员在开发核心人物功能时分散了精力
-
附加功能代码重复,分散在各个业务功能方法中,冗余并且不方便统一维护
b.解决思路:
核心是:解耦:我们需要把附加功能从业务功能代码中抽取出来
将重复的代码统一提取,并且[动态插入]到每个业务方法中
c.技术困难:
解决问题的困难:提取重复的附加功能到一个类中可以实现
但是如何将代码插入到各个方法中,我们不会的,我们需要新的技术->AOP技术
5.2 解决技术代理模式(AOP)
1.代理模式
二十三中设计模式中的一种,属于结构型模式.它的作用是通过提供一个代理类,让我们在调用目标方法时,不再直接对目标方法进行调用,而是通过代理类间接调用.让不属于目标方法核心逻辑的代码从目标方法中抽离出来--解耦,调用目标方法时,先调用代理对象的方法,减少对目标方法的调用和打扰,同时让附加功能能够集中在一起,也有利于统一维护
2.静态代理技术
代理类(中介):
package com.atguigu.statics; import com.atguigu.Calculator; public class StaticProxyCalculator implements Calculator { private Calculator calculator; public StaticProxyCalculator(Calculator calculator){ this.calculator = calculator; } @Override public int add(int i, int j) { //使用构造函数传入的信息 System.out.println("i = " + i + ", j = " + j); //调用目标 int result = calculator.add(1, 1); return result; } @Override public int sub(int i, int j) { return 0; } @Override public int mul(int i, int j) { return 0; } @Override public int div(int i, int j) { return 0; } } /** * @program: Spring-aop-annotation-09 * @description: * @author: XiaoYongCai * @Goal: * @create: 2024-03-19 22:11 **/
核心逻辑(房东):
package com.atguigu; public interface Calculator { int add(int i,int j); int sub(int i,int j); int mul(int i,int j); int div(int i,int j); } /** * @program: Spring-aop-annotation-09 * @description: * @author: XiaoYongCai * @Goal: * @create: 2024-03-19 21:30 **/
package com.atguigu; public class CalculatirPureImpl implements Calculator{ @Override public int add(int i,int j){ int result = i+j; return result; } @Override public int sub(int i,int j){ int result = i-j; return j; } @Override public int mul(int i,int j){ int result = i*j; return result; } @Override public int div(int i,int j){ int result = i/j; return result; } } /** * @program: Spring-aop-annotation-09 * @description: * @author: XiaoYongCai * @Goal: * @create: 2024-03-19 21:31 **/
使用AOP:
package com.atguigu; import com.atguigu.statics.StaticProxyCalculator; public class UseAOP { public static void main(String[] args){ //房东 目标 Calculator target = new CalculatirPureImpl(); //中介 代理 Calculator proxy = new StaticProxyCalculator(target); //调用中介方法 proxy.add(1,2); } } /** * @program: Spring-aop-annotation-09 * @description: * @author: XiaoYongCai * @Goal: * @create: 2024-03-19 22:15 **/
3.动态代理技术(实际开发使用)
动态代理技术分类:
-
JDK动态代理:JDK原生的实现方式,需要被代理的目标类必须要实现接口,他会基于目标类的接口自动生成一个代理对象,代理对象和目标对象有相同的接口(拜把子)
-
cglib:通过继承被代理的目标类来实现代理,所以不需要目标类来实现接口(认干爹)
5.3 面向切面编程思维
5.4 Spring AOP框架介绍和关系梳理
1.AOP是一种区别于OOP编程思维,用来完善和解决OOP的非核心代码冗余和不方便统一维护问题
2.代理技术(动态代理|静态代理)是实现AOP思维编程的具体技术,但是自己用动态代理实现代码比较繁琐
3.Spring AOP框架,基于AOP思维,封装了动态代理技术,简化了动态代理技术实现的框架!Spring AOP内部帮助我们实现动态代理,我们只需要写少量的配置,指定生效范围.即可完成面向切面思维编程实现
5.5 Spring AOP基于注解方式实现和细节
5.5.1 Spring AOP底层技术组成
动态代理(InvocationHandler):JDK原生的实现方式,需要被代理的目标类必须实现接口,因为这个技术要求代理对象和目标对象实现同样的接口(拜把子模式)
cglib:通过继承被代理的目标类,实现代理,不需要目标类的实现类
AspectU:早期的AOP实现的框架,SpringAOP借用了AspectJ中的AOP注解
无非是借用AspectU提供的注解,但是由动态代理和cglib实现,这个是历史问题,因为程序员已经习惯使用AspectJ的注解来开发了,所以SpringAOP直接用了AspectJ的注解
5.5.2 初步实现
1.加入依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>6.1.4</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>6.0.6</version> </dependency>
2.准备接口
3.