文章目录
- SpringIoc---Spring的核心容器:
-
- [SpringIoc 容器接口:](#SpringIoc 容器接口:)
- [ApplicationContext 容器的实现类:](#ApplicationContext 容器的实现类:)
- [SpringIoc 概念总结:](#SpringIoc 概念总结:)
- [Spring IOC/DI实现的基本步骤](#Spring IOC/DI实现的基本步骤)
- [基于 XML 配置方式组件管理](#基于 XML 配置方式组件管理)
- 组件(Bean)依赖注入配置(DI)
- [IOC 容器创建和使用](#IOC 容器创建和使用)
- 高级特性:组件(Bean)作用域和同期方法配置
- [高级特性:FactoryBean 特性和使用](#高级特性:FactoryBean 特性和使用)
- [基于**注解**方式管理 Bean](#基于注解方式管理 Bean)
- 组件(Bean)作用域和周期方法注解
- [Bean 属性赋值:引用类型自动装配(DI)](#Bean 属性赋值:引用类型自动装配(DI))
- [Bean 属性赋值:基本类型属性赋值(DI)](#Bean 属性赋值:基本类型属性赋值(DI))
- [基于 配置类 方式管理 Bean](#基于 配置类 方式管理 Bean)
- 配置类和扫描注解
- [@Bean 方式实现](#@Bean 方式实现)
- [高级特性:@Bean 注解细节](#高级特性:@Bean 注解细节)
- [整合 Spring5-Test5 搭建测试环境](#整合 Spring5-Test5 搭建测试环境)
- [Spring AOP 面向切面编程](#Spring AOP 面向切面编程)
-
- [Spring AOP 框架介绍和关系梳理](#Spring AOP 框架介绍和关系梳理)
- [Spring AOP 基于注解方式实现和细节(常用)](#Spring AOP 基于注解方式实现和细节(常用))
-
- 初步实现
- 获取通知细节信息
- 切点表达式语法
- 重用(提取)切点表达式
- 环绕通知
- 切面优先级设置---当有多个切面时
- CGLib动态代理生效
- [Spring AOP 基于XML 方式实现(了解)](#Spring AOP 基于XML 方式实现(了解))
- [Spring 声明式事务---基于 注解的声明式事务](#Spring 声明式事务---基于 注解的声明式事务)
- [Spring 核心掌握总结](#Spring 核心掌握总结)
SpringFramework的功能模块:
- SpringIoc:核心容器
- AOP:面向切面编程
- TX:声明式事务管理
- Spring MVC 提供了面向Web应用程序的集成功能。
SpringIoc---Spring的核心容器:
SpringIoc 容器接口:
- BeanFactory:接口提供了一种高级配置机制,是SpringIoc容器标准化超接口。
- ApplicationContext是BeanFactory的子接口,它扩展了以下功能:
- 更容易与Spring的AOP功能集成。
- 消息资源处理(用于国际化)
- 特定于应用程序给予此接口实现。
ApplicationContext 容器的实现类:
- ClassPathXmlApplicationContext:通过读取类路径下的XML格式的配置文件创建IOC容器对象。
- FileSystemXmlApplicationContext:通过文件系统路径读取XML格式的配置文件创建IOC容器对象。
- AnnotationConfigApplicationContext:通过读取java配置类创建IOC容器对象。
- WebApplicationContext:专门为Web应用准备,基于Web环境创建IOC容器对象,并将对象引入存入ServletContext域中。
SpringIoc 概念总结:
-
IoC(Inversion of Control) 控制反转
当应用程序需要使用一个对象时,不再是应用程序直接创建该对象,而是由IoC容器来创建和管理,即控制权由应用程序转移到了IoC容器中,也就是"反转"了控制权。
-
DI(Dependency Injection) 依赖注入:
DI是指在组件之间传递依赖关系的过程中,将依赖关系在容器内部进行处理。在Spring中,DI是通过XML配置文件或注解的方式实现的。它提供了三种形式的依赖注入:构造函数注入、Setter方法注入和接口注入。
-
Spring IOC/DI实现的基本步骤
-
配置元数据(配置):
配置元数据,既是编写,交给Spring IOC容器,管理组件的信息,配置方式有三种(基于XML的配置,基于注解的配置,基于类的配置)。
基于XML的配置元数据的基本结构:
spring<bean id="..." class="..."> </bean>Spring IOC容器管理一个或多个组件。这些组件是使用你提供给容器的配置元数据创建的
一个标签代表一个组件信息声明。
- id 属性是标识Bean定义的字符串,也就是Bean起的一个名字。
- class 属性定义Bean的类型并使用完全限定的类名
spring<?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="..."/> <bean id="..." class="..."/> </beans>该XML配置文件基本格式可以通过右键点击**
resources资源包通过new**
可得
-
实例化IOC容器
提供给
ApplicationContext构造函数的位置路径是资源字符串地址,允许容器从各种外部资源 (如本地文件系统,javaCLASSPATH等) 加载配置元数据。首先,我们应该选择一个合适的容器实现类,进行IOC容器的实例化工作:
spring//实例化IOC容器,读取外部配置文件,最终会在容器内进行ioc和di动作 ApplicationContext context1=new ClassPathXmlApplicationContext("service.xml","dao.xml") ClassPathXmlApplicationContext context2=new ClassPathXmlApplicationContext("service.xml","dao.xml") -
获取Bean(组件)
ApplicationContext是一个高级工厂的接口,能够维护不同bean及其所依赖项的注册表。通过使用方法T getBean(String name, Class<T>requiredType),您可以检索bean的实例。允许读取Bean定义并访问它们,如以下救命所示:
spring//创建ioc容器对象,指定配置文件,ioc也开始实例组件对象 ClassPathXmlApplicatonContext applicationcontext = new ClassPathXmlApplicationContext("services.xml","daos.xml"); //获取ioc容器的组件对象 PetStoreService service = applicationcontext.getBean("petstore",PetStoreService.class); //使用组件对象 List<String> userList = service.getUsernameList();
基于 XML 配置方式组件管理
-
基于无参数构造函数:
-
准备类似组件类:
javapackage com.jxnu.ioc public class HappyComponent{ //默认包含无参数构造函数 public void doWork(){ System.out.println("HappyComponent.doWork"); } } -
编写配置文件:
- 文件放置地址:resources/spring-ioc-01.xml
spring<bean id="happyComponent" class="com.jxnu.ioc.HappyComponent"/>- bean 标签: 通过配置bean标签告诉IOC容器需要创建对象的组件信息。
- id属性:bean 的唯一标识,方便后期获取Bean!
- class 属性 : 组件类的全限定符
- 注意:要求当前组件类必须包含无参数构造函数!
-
-
基于静态工厂方法实例化
除了使用构造函数实例化对象,还有一类是通过工厂模式实例化对象。
-
准备类似组件类:
javapackage com.jxnu.ioc public class ClientService{ private static ClientService clientService = new ClientService(); private ClientService(){} public static ClientService createInstance(){ return clientService; } } -
编写配置文件:
-
文件放置地址:resources/spring-ioc-01.xml
spring<bean id="clientService" class="com.jxnu.ioc.ClientService" factory-method="createInstance"/>- bean 标签: 通过配置bean标签告诉IOC容器需要创建对象的组件信息。
- id属性:bean 的唯一标识,方便后期获取Bean!
- class 属性 : 组件类的全限定符
- factory-method:指定静态工厂方法,注意,在静态工厂中,该方法必须是static方法。
-
-
-
基于实例工厂方法实例化
-
准备组建类
springpackage com.jxnu.ioc public class DefaultServiceLocator{ private static clientService = new ClientService(); public ClientService createClientSERviceInstance(){ return clientService; } } -
编写配置文件:
-
文件放置地址:resources/spring-ioc-01.xml
spring//先将工厂类进行ioc配置 <bean id="serviceLocator" class="com.jxnu.DefaultServiceLocator"></bean> //根据工厂对象的实例工厂方法进行实例组件对象 <bean id="clientService" factory-bean="serviceLocator" factory-method="createClientServiceInstance"/>- bean 标签: 通过配置bean标签告诉IOC容器需要创建对象的组件信息。
- id属性:bean 的唯一标识,方便后期获取Bean!
- factory-bean属性:指定当前容器中工厂 Bean 的名称。
- factory-method:指定实例工厂方法名。注意在实例工厂中,实例方法必须是非static的!
-
-
组件(Bean)依赖注入配置(DI)
-
目标
通过配置文件,实现IOC容器中Bean之间的引用(依赖注入DI配置)。
主要涉及注入场景:基于构造函数的依赖注入和基于Setter的依赖注入。
-
基于构造函数的依赖注入(单个构造参数)
-
介绍
基于构造函数的 DI 是通过容器调用具有多个参数的构造函数来完成的,每个参数表示一个依赖项在。
下面的救命演示一个只能通过构造函数注入进行依赖项注入的类!
-
准备组件类
javapackage com.junx.ioc1 public class UserDao{} public class UserService{ private UserDao userDao; public UserService(UserDao userDao){ this.userDao = userDao; } } -
编写配置文件
文件:resources/spring-ioc-02.xml
spring<beans> <bean id="userDao" class="com.junx.ioc1.UserDao"/> <bean id="userService" class="com.junx.ioc1.UserService"> <constructor-arg ref="userDao"/> </bean> </beans>- constructor-arg 标签:可以引用构造参数 ref 引用其他 bean 的标识,用 value 引用一个的参数,例如数字或字符串。ref 和 value 这两个属性只能二选一。
-
-
基于构造函数的依赖注入(多个构造参数)
-
介绍
下面的示例演示通过构造函数注入多个参数,参数包含其他 bean 和基本数据类型!
-
准备组件类
springpackage com.junx.ioc1 public class UserDao{} public class UserService{ private UserDao userDao; private int age; private String name; public UserService(UserDao userDao,String name,UserDao userDao){ this.userDao = userDao; this.age=age; this.name=name; } } -
编写配置文件
spring//多个参数,可以按照相应构造函数参数的顺序注入数据 <beans> //被引用类的声明 <bean id="userDao" class="com.jxnu.ioc1.UserDao"/> <bean id="userService" class="com.jxnu.ioc1.UserService"> //value或ref直接注入基本类型值(按顺序来) <constructor-arg ref="uerDao"/> <constructor-arg value="张三"/> <constructor-arg value="18"/> </bean> </beans> //多个参数,可以按照相应构造函数参数的名称注入数据 <beans> //被引用类的声明 <bean id="userDao" class="com.jxnu.ioc1.UserDao"/> <bean id="userService" class="com.jxnu.ioc1.UserService"> //value或ref按照参数名称来 <constructor-arg name="name" value="张三"/> <constructor-arg name="userDao" ref="userDao"/> <constructor-arg value="18"/> </bean> </beans>- constructor-arg 标签:指定构造参数和对应的值
- constructor-arg 标签属性:name属性指定参数名、ref 属性指定其它bean的标识、value 属性指定普通属性值。
-
-
基于Setter方法依赖注入
-
介绍
开发中,除了构造函数注入(DI)更多的使用的Setter方法进行注入!
下面的示例演示一个只能使用纯Setter注入进行依赖项注入的类。
-
准备组件类
javapackage com.jxnu.ioc1 public class MovieFinder{} public class SimpleMovieLister{ private MovieFinder movieFinder; private String movieName; public void setMovieFinder(MovieFinder movieFinder){ this.movieFinder=movieFinder; } public void setMoiveName(String movieName){ this.movieName=movieName; } } -
编写配置文件
spring//被引用的类 <bean id="movieFinder" class="com.jxnu.ioc1.MovieFinder"/> <bean id ="SimpleMovieLister" class="com.jxnu.ioc1.SimpleMovieLister"> //name属性:是指被引用函数除去set后,将开头字母小写后的函数名,一般为参数名。 //ref属性:是指引用bean的id值。 //value属性:是指基本类型值。 <property name="movieFinder" ref="movieFinder"/> <property name="movieName" value="hello world"/> </bean>- property 标签:可以给setter方法对应的属性赋值。
- property 标签:name 属性代表 set 方法标识、ref 代表引用 bean 的标识 id 、value 属性代表基本属性值。
-
-
总结:
-
依赖注入(DI)包含引用类型和基本数据类型,同时注入的方式也有多种!主流的注入方式为setter方法注入的构造函数注入。
-
需要特别注意:引用其他 bean ,使用 ref 属性。直接注入基本类型值,使用 value 属性。
-
IOC 容器创建和使用
-
容器实例化
spring//方法1:实例化并指定配置文件,推荐 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("services.xml","a.xml"); //方法2:先实例化,再指定配置文件,最后刷新容器触发Bean实例化动作。 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext() context.setConfigLocations("services.xml","a.xml"); context.refresh(); -
Bean 对象读取
spring//方法1:根据id获取 //因为没有指定类型,返回为Object,所以需要类型转化 HappyComponent happyComponent = (HappyComponent) context.getBean("bean的id标识")//context是容器实例化对象。 //使用组件对象 happyComponent.doWork() //方法2:根据类型获取,但是这种方法要求:在XML配置中只有一个bean对应着该类,不能有多个bean对应该类。 HappyComponent happyComponent1 = context.getBean(HappyComponent.class); happyComponent.doWork(); //方法3:根据id和类型获取 HappyComponent happyComponent2 = context.getBean("bean的id标识",HappyComponent.class);
高级特性:组件(Bean)作用域和同期方法配置
-
组件周期方法配置
-
周期方法概念:
我们可以在组件类中定义方法,然后当IOC容器实例货物销毁组件对象的时候进行调用!这两个方法我们称为生命周期方法!
类似于Servlet的inin/destroy方法和Vue中的mouted和beforeDestroy方法,我们可以在周期方法中完成初始化和释放资源等工作。
-
周期方法声明
javapackage com.jxnu.ioc3 public class BeanOne{ public voie init(){//初始化逻辑} } public class BeanOne{ public voie cleanup(){//释放资源逻辑} } -
周期方法配置
spring<beans> <bean id="beanOne" class="com.jxnu.ioc3.BeanOne" init-method="init" /> <bean id="beanTwo" class="com.jxnu.ioc3.BeanTwo" destroy-method="cleanup" </beans>
-
-
组件作用域配置
-
Bean 作用域概念
<bean>标签声明 Bean ,只是将 Bean 的信息配置给 IOC 容器中在 IOC 容器中,这些
<bean>标签将对应的信息转成 Spring内部BeanDefinition对象,BeanDefinition对象内,包含定义的信息(id,class,属性等等)!这意味着,
BeanDefinition与类概念一样,IOC容器可以根据BeanDefinition对象反射创建多个Bean对象实例。具体创建多少个 Bean 的实例对象,由 Bean 的作用域 scope 属性来决定。
-
scope作用域可以选值
取值 取值 创建对象的时机 默认值 singleton 在IOC容器中,这个bean的对象始终为单实例 IOC容器初始化时 是 prototype 这个bean在IOC容器中有多个实例 获取bean时 否 如果是在WebApplicationContext 环境下还会有另外两个作用域(但是不常用):
取值 含义 创建对象的时机 默认值 request 请求范围内有效的实例 每次请求 否 session 会话范围内有效的实例 每次会话 否 -
配置 scope 范围
spring//scope属性:取值为singleton(默认),bean在IOC容器中只有一个实例,IOC容器初始化时创建对象。 //scope属性:取值为prototype, bean 在IOC容器中可以有多个实例,getBean()时创建对象 <bean id="happyMachine" scope="prototype" class="com.jxnu.ioc3.HappyMachine"/> <bean id="happyComponent" class="com.jxnu.ioc3.HappyComponent"/>
-
高级特性:FactoryBean 特性和使用
-
FactoryBean 简介
FactoryBean接口 是用于配置复杂的 Bean 对象,可以将创建过程存储在FactoryBean的 getObject 方法中。FactoryBean<T>接口提供三种方法:-
T getObject<T>:返回此工厂创建的对象的实例。该返回值会被存储到IOC容器中!
-
boolean isSingleton():默认是返回 true ,如果是返回单例,则是 true,否则是 false。
-
Class<?> getObjectType():返回
getObject()方法返回的对象类型,如果事先不知道类型,则返回 null。
-
-
FactoryBean 使用场景
- 代理类的创建
- 第三方框架整合
- 复杂对象实例化等
-
FactoryBean 的应用
-
准备 FactroyBean 实现类
javapackage com.jxnu.ioc4 public class HappyMachineFactoryBean implements FactoryBean<HappyMachine>{ private String machineName; public String getMachineName(){ return machineName; } public void setMachineName(String machineName){ this.machineName=machineName; } @Override public HappyMachine getObject() throws Exception{ //方法内部模拟创建,设置一个对象的复杂过程。 HappyMachine happyMachine = new HappyMachine(); happyMachine.setMachineName(this.machineName); return happyMachine; } @Override public Class<?> getObjectType(){ //返回要生产的对象的类型 return HappyMachine.class; } } -
配置 FactoryBean 实现类
spring<bean id="happyMachine" class="com.jxnu.ioc4.HappyMachineFactoryBean"> <property name="machineName" value="张三"/> </bean> -
FactoryBean 和 BeanFactory 的区别
-
FactoryBean 是用来实现复杂实例化bean对象的,它是一个特殊的bean。可自定义逻辑创建的bean。
-
BeanFactory 是 Spring 框架的基础,是ApplicaionContext 的父接口。
-
总的来说,FactoryBean 和 BeanFactory 的区别主要是前者是用来创建Bean的接口,后者是IOC的实例化接口,是用来管理 Bean 的。
-
-
基于注解方式管理 Bean
Bean 注解标记和扫描(IOC)
-
步骤:
- 标记,可以用@Component、@Repository、@Controller、@Service。@Component是基础命名,另外三个是基础命名的别名。
- 扫描,在XML上用<context:来指定注解标记在哪些包上,加快扫描速度。
-
准备Spring 项目和组件:
-
准备项目 pom.xml


-
准备组件类:
-
普通组件:
java@Component(value="hello world") public class CommonComponent{}在这里 value 的作用是在IOC容器中给CommonComponent 实例对象起一个id 也就是在XML上中的id。
这里它的id 名是hello world。
-
Controller组件:
java@Controller public class XxxController{} -
Service组件:
java@Service public class XxxService{} -
Dao组件:
java@Repository public class XxxDao{}
-
-
配置文件确定扫描范围
-
情况1:基本扫描配置(常用)
spring<?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 http://www.springframework.org/schema/context/spring-context.xsd "> <context:component-scan base-package="com.jxnu.ioc01"/> </beans> -
情况2:指定排除组件
spring<?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 http://www.springframework.org/schema/context/spring-context.xsd "> <context:component-scan base-package="com.jxnu.ioc01"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> </beans>
-
-
-
组件BeanName问题:
-
默认情况下:
类名首字母小写就是 bean 的 id。例如: SolidierController 类对应的 bean 的id 就是 soldierController。
-
使用 value 属性指定:
java@Controller(value="hello world")//或者@Controller("hello world") public class SolidierController{}
-
组件(Bean)作用域和周期方法注解
-
组件周期方法配置
它需要

作为依赖。-
周期方法概念
我们可以在组件类中定义方法,然后当IOC容器实例化和销毁组件对象的时候进行调用!这两个方法我们叫作生命周期方法!
类似于Servlet 的init/destroy 方法,我们可以在周期方法完成初始化和释放资源等工作。
-
周期方法声明
javapackage com.jxnu.ioc2 public class BeanOne{ @PostConstruct //要求方法必须是 public void 无形参列表 public void init(){} } public class BeanTwo{ @PreDestroy public void cleanup(){} }
-
-
组件作用域配置
如果你想在test/java中测试的话,你需要引入依赖
java@Scope(scopeName = ConfigurableBeanFactory.SCOPE_SINGLETON)//单例,默认值 @Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)//多例,【单例,多例二选一】 public class BeanOne{ @PostConstruct public void init(){} }
Bean 属性赋值:引用类型自动装配(DI)
-
自动装配实现
-
@Autowired 注解
在成员变量上直接标记 @Autowired 注解即可,不需要提供 setXxx()方法。
-
给 Controller 装配 Service
java@Controller public class SoldierController{ @Autowired private SoldierService soldierService; public void getMessage(){ soldierService.getMessage(); } }
-
-
@Autowired 注解细节
-
标记位置:
- 在成员变量上。
- 在构造函数上。
- 在setter方法上。
-
工作流程:
-
首先根据所需要的组件类型到IOC 容器中查找
-
能够找到唯一的 bean :直接执行装配
-
如果完全找不到匹配这个类型的 bean :装配失败
-
和所需类型匹配的 bean 不只一个时
-
没有@Qualifier 注解:根据 @Autowired标记位置的成员变量的变量名作为 bean 的 id 进行匹配
java@Controller public class SoldierController{ @Autowired private SoldierService soldierService; }在这里根据
SoldierService soldierService中的soldierService作为 bean 的 id- 能够找到:执行装配
- 找不到:装配失败
-
使用@Qualifier 注解:根据@Qualifier 注解中指定的名称作为 bean 的 id 进行匹配
- 能够找到:执行装配
- 找不到:装配失败
-
java@Controller public class SoldierController{ @Autowired @Qualifier(value="hello_world")//指定查找id为hello world的IOC实例对象。 private SoldierService soldierService; }也可以用@Resource(name="hello world") == @Autowired + @Qualifier(value="hello world")
-
-
-
Bean 属性赋值:基本类型属性赋值(DI)
@Value通常用于注入外部化属性
-
声明外部配置
/resources/application.properties
name=hello_world -
xml 引入外部配置
spring<context:property-placeholder location="application.properties"/> -
@Value 注解读取配置
java@Component public class CommonComponent{ @Value("${name:nokey}")//{key:defaultValue}在外部配置中如果有key对应的值,则用对应的值,如果没有则用defaultValue自己写的值 。 private String name; }
-
一般直接在类中赋值作为DI
java@Component public class CommonComponet{ private String name="hello_world"; }
基于 配置类 方式管理 Bean
-
完全注解开发理解
Spring 完全注解配置是通过 java 配置类代码来配置 Spring 应用程序,即:使用注解来替代原本在XML 配置文件中的配置。
两种方式思维转化:

配置类和扫描注解
-
配置类:
用配置类 代替 之前用的 XML
写在config/JavaConfiguration中
使用 @Configuration 注解将一个普通的类标记为 Spring 的配置类
java@Configuration //是表明它是一个配置类 @ComponentScan(basePackage={"com.jxnu.ioc"}) //是表明它的扫描范围 @PropertySource(value={"classpath:jdbc.properties"}) //是表明注解读取外部配置 public class JavaConfiguration{} -
创建IOC 容器:
javaAnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JavaConfiguration.class); -
总结:
- @Configuration 指定一个类为配置类,在配置类中可以
- 表明注解扫描范围,用ComponentScan(basePackages={""})
- 引入外部配置,比如:jdbc.properties,用PropertySource(value={""})
- 引入第三方包,例如:DruidDataSource,jdbcTemplate
- @Configuration 指定一个类为配置类,在配置类中可以
@Bean 方式实现
-
需要分析:第三方jar 包的类,要添加到IOC 容器中,无法使用 @Component 等相关注解,如jdbcTemplate,Druid等等包,所以只能在配置类中用@Bean 来配置第三方包。
jdbcTemplate 有upate 方法可以用来增删改,有queryForObject,query用来查。其中查时,当数据库属性名和java后端属性名一致时,或可以用 select class as classes 的方法让其属性名一致,当一致时,可以用 List studentList = jdbcTemplate.query(sql,BeanPropertyRowMapper<>(Student.class));
-
用配置类的方式实现
java@Configuration //用来定义配置类 @ComponentScan(basePackages={"com.jxnu.ioc"}) //用来确定扫描范围 @PropertySource(value={"classpath:jdbc.properties"}) //用来引入外部文件 public class JavaConfiguration{ @Bean("hello world") //在这里Druid组件的id 为"hello world",默认情况为函数名也就是druidDataSource public DruidDataSource druidDataSource(@Value(${jdbc.url}) String url @Value(${jdbc.driver}) String driver @Value(${jdbc.username}) String username @Value(${jdbc.password}) String password){ //在配置类通过@Bean标签可以自动在IOC容器中创建对应的第三方包实例对象。 DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setUrl(url); druidDataSource.setDriver(driver); druidDataSource.setUsername(username); druidDataSource.setPassword(password); return druidDataSource } }
高级特性:@Bean 注解细节
-
指定@Bean 的名称
java@Configuration public class JavaConfiguration{ @Bean("hello world") //在这里id的名称是"hello world",但是默认情况是函数名称,也就是jdbcTemplate。 public JdbcTemplate jdbcTemplate(DruidDataSource druidDataSource){ JdbcTemplate jdbcTemplate = new JdbcTemplate(); jdbcTemplate.setDataSource(druidDataSource); return jdbcTemplate } } -
@Bean 初始化和销毁方法
@Bean注解支持指定任意初始化和销毁回调方法,非常类似于 Spring XML 在bean标签上的init-method和destroy-method属性javapublic class BeanOne{ pubilc void init(){} } public class BeanTwo{ public void cleanUp(){} } @Configuration public class JavaConfiguration{ @Bean(initMethod = "init") public BeanOne beanOne{ return new BeanOne(); } @Bean(destroyMethod = "cleanUp") public BeanTwo beanTwo{ return new BeanTwo(); } } -
@Bean Scope 作用域
可以指定使用
@Bean注释定义的 bean 应具有的特定范围,默认是单例。java@Configuration public class JavaConfiguration{ @Bean @Scope("prototype") public A a(){ return new A(); } } -
@Bean 方法之间的依赖
-
案例:JdbcTemplate 的实例化需要引入DruidDataSource连接池。
java@Configuration public class JavaConfiguration{ @Bean public DataSource druidDataSource(){ ...... return druidDataSource; } @Bean public JdbcTemplate jdbcTemplate(DataSource druidDataSource){//可以直接在参数中,引入需要在IOC中的对象 JdbcTemplate jdbcTemplate = new JdbcTemplate(); jdbcTemplaet.setDataSource(druidDataSource); return jdbcTemplate } }通过方法参数传递 Bean实例的引用 来解决 Bean 实例之间的依赖关系
-
-
@Import 扩展
-
@Import 注释允许从另一个配置加载 @Bean 定义
java@Configuration pubilc class ConfigA{ @Bean public A a{ return new A(); } } @Configuration @Import(ConfigA.class) pubilc class ConfigB{ @Bean public B b{ return new B(); } }这样在实例化对象时,就不用指定两个配置类了,只需要一个ConfigB,就可以代表ConfigB和ConfigA。
javaAnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConfigB.class);
-
整合 Spring5-Test5 搭建测试环境
-
整合测试环境作用
- 不需要自己创建IOC容器对象了
- 任何需要的 bean 都可以在测试类中直接享受自动装配
-
导入相关依赖
maven<!--junit5测试--> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.3.1</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>6.0.6</version> <scope>test</scope> </dependency> -
整合测试注解使用
java//@SpringJUnitConfig(location = {"classpath:spring-xml.xml"}) //指定基于XML的配置文件xml @SpringJUnitConfig(value = {JavaConfiguration.class}) //指定配置类 public class Test{ @Autowired //自动装配 private JdbcTemplate jdbcTemplate; @Test public void HaveATry(){ System.out.Println(jdbcTemplate); } }
Spring AOP 面向切面编程
Spring AOP 框架介绍和关系梳理
-
AOP(面向切面编程) 是一种区别于 OOP(面向对象编程)的编程思维,用来完善和解决OOP 的非核心代码冗余和不方便统一维护问题!
-
代理技术(动态代理|静态代理)是实现AOP 思维编程的具体技术,但是自己使用动态代理实现代码比较繁琐!
-
Spring AOP 框架,基于 AOP编程思维,封装了动态代理技术,简化了动态代理技术实现的框架!Spring AOP 内部帮助我们实现动态代理,我们只需要写少量的配置,指定生效范围即可。
spirng中有ioc, 有四种,aop有两种,tx,mybaits,spring MSC
Spring AOP 基于注解方式实现和细节(常用)
初步实现
-
加入依赖
spring<!-- spring-aspects会帮我们传递过来aspectjweaver --> <dependency>//在这里我们可以用spring-context,因为spring-context传递了spring-aop <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>6.0.6</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>6.0.6</version> </dependency> -
准备接口
javapublic 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); } -
实现纯净类
javapackage com.atguigu.proxy; /** * 实现计算接口,单纯添加 + - * / 实现! 掺杂其他功能! */ @Component public class CalculatorPureImpl 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 result; } @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; } } -
声明切面类
有:前置:@Before,后置:@AfterReturning,异常:@AfterThrowing,最后:@After,环绕:@Around
放在/aspect/LogAspect下package com.atguigu.advice; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; // @Aspect表示这个类是一个切面类 @Aspect // @Component注解保证这个切面类能够放入IOC容器 @Component public class LogAspect { // @Before注解:声明当前方法是前置通知方法 // value属性:指定切入点表达式,由切入点表达式控制当前通知方法要作用在哪一个目标方法上 @Before(value = "execution(public int com.atguigu.proxy.CalculatorPureImpl.add(int,int))") public void printLogBeforeCore() { System.out.println("[AOP前置通知] 方法开始了"); } @AfterReturning(value = "execution(public int com.atguigu.proxy.CalculatorPureImpl.add(int,int))") public void printLogAfterSuccess() { System.out.println("[AOP返回通知] 方法成功返回了"); } @AfterThrowing(value = "execution(public int com.atguigu.proxy.CalculatorPureImpl.add(int,int))") public void printLogAfterException() { System.out.println("[AOP异常通知] 方法抛异常了"); } @After(value = "execution(public int com.atguigu.proxy.CalculatorPureImpl.add(int,int))") public void printLogFinallyEnd() { System.out.println("[AOP后置通知] 方法最终结束了"); } } -
开启 aspectj 注解支持
-
xml方式
spring<?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" xmlns:aop="http://www.springframework.org/schema/aop" 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 http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 进行包扫描--> <context:component-scan base-package="com.jxnu" /> <!-- 开启aspectj框架注解支持--> <aop:aspectj-autoproxy /> </beans> -
配置类方式
spring@Configuration @ComponentScan(basePackages = "com.atguigu") //作用等于 <aop:aspectj-autoproxy /> 配置类上开启 Aspectj注解支持! @EnableAspectJAutoProxy public class MyConfig { }
-
获取通知细节信息
-
JoinPoint 接口
需要获取方法签名,传入的实参等信息时,可以在通知方法声明JoinPoint类型的形参。
- 要点1 :JoinPoint 接口通过 getSignature() 方法获取目标方法的签名(方法声明时的完整信息)
- 要点2 :通过目标方法签名对象获取方法名。
- 要点3 :通过 JoinPoint 对象获取外界调用目标方法时传入的实参列表组成的数组。
java@Before(value="execution(* com..impl.*.*(..))") public void before(JoinPoint joinPoint){ //获取方法的签名对象 Signature signature = joinPoint.getSignature(); //通过方法的签名对象,获取目标方法的详细信息 String methodName = signature.getName(); //返回的是 pubilc abstract int modifiers = signature.getModifier(); //返回的是类的路径 String declaringTypeName = signature.getDeclaringTypeName(); //通过JoinPoint,获取参数数组。 Object[] args = joinPoint.getArgs(); //由于数组直接打印看不到具体的数据,所以转换为List集合 List<Object> argList = Arrays.asList(args); } -
方法返回值
在返回通知中,通过@AfterReturning 注解的returning 属性获取目标方法的返回值!
java@AfterReturning(value="execution(* com..impl.*.*(..))",returning = "result") public void afterReturning(JoinPoint joinPoint,Object result){ } -
异常对象捕捉
在异常通知中,通过@AfterThrowing 注解的 throwing 属性获取目标方法抛出的异常对象
java@AfterThrowing(value="execution=("* com..impl.*.*(..)")",throwing="throwable") public void afterThrowing(JoinPoint joinPoint,Throwable throwable){ }
切点表达式语法
-
切点表达式作用
AOP 切点表达式是一种用于指定切点的语言,它可以通过定义匹配规则 ,来选择需要被切入的目标对象。
-
切点表达式语法

重用(提取)切点表达式
我们可以将切点提取,在增强上进行引用即可!
-
切点统一管理
建议:将切点表达式统一存储到一个类中进行集中管理和维护
放在/pointCut/中
javapackage com.jxnu.pointCut @Component public class XxxPointCut{ @Pointcut(value="execution(* com..Impl.*.*(..))") public void pc1(){} @Pointcut(value="execution(public int com.jxnu.Entity.Impl.CalculatorImpl.*(..))") public void pc2(){} } -
在不同类中引用
java@Before(value="com.jxnu.pointCut.XxxPointCut.pc1()") public void before(JoinPoint joinPoint){}
环绕通知
环绕能对应整个 try...catch...finally 结构,包括前面四种通知的所有功能。
放在/aspect/中
java
package com.jxnu.aspect
@Component
public class XxxAspect{
@Around(value="com.jxnu.pointCut.PointCut.XxxAspect()")
public Object around(ProceedingJoinPoint joinPoint){
//用@Around时,方法参数要用ProceedingJoinPoint
Object[] args = joinPoint.getArgs();
Object result;
try{
//写前置任务
........
//目标方法返回值
result = joinPoint.proceed(args);
//写后置任务
........
}catch(Throwable e){
System.out.Println(e.getClass().getName());
}finally{
........
}
return result;
}
}
切面优先级设置---当有多个切面时
使用 @Order 注解可以控制切面的优先级:
- @Order(较小的数):优先级高
- @Order(较大的数):优先级低
要导入包
import org.springframework.core.annotation.Order;
CGLib动态代理生效
在目标类没有实现任何接口的情况下,Spring 会自动使用 cglib 技术实现代理。
如果目标类有接口的情况下,Spring 会自动使用 jdk 技术实现代理。
若目标类有接口,则将其继承接口的增强类放入IOC中(其接口的实现类,目标类,不会放入IOC中)
若目标类没有接口,则将其目标类的子类,也是增强类放入IOC中(目标类是不会放入IOC中的)
Spring AOP 基于XML 方式实现(了解)
-
配置Spring 配置文件
java<!-- 配置目标类的bean --> <bean id="calculatorPure" class="com.atguigu.aop.imp.CalculatorPureImpl"/> <!-- 配置切面类的bean --> <bean id="logAspect" class="com.atguigu.aop.aspect.LogAspect"/> <!-- 配置AOP --> <aop:config> <!-- 配置切入点表达式 --> <aop:pointcut id="logPointCut" expression="execution(* *..*.*(..))"/> <!-- aop:aspect标签:配置切面 --> <!-- ref属性:关联切面类的bean --> <aop:aspect ref="logAspect"> <!-- aop:before标签:配置前置通知 --> <!-- method属性:指定前置通知的方法名 --> <!-- pointcut-ref属性:引用切入点表达式 --> <aop:before method="printLogBeforeCore" pointcut-ref="logPointCut"/> <!-- aop:after-returning标签:配置返回通知 --> <!-- returning属性:指定通知方法中用来接收目标方法返回值的参数名 --> <aop:after-returning method="printLogAfterCoreSuccess" pointcut-ref="logPointCut" returning="targetMethodReturnValue"/> <!-- aop:after-throwing标签:配置异常通知 --> <!-- throwing属性:指定通知方法中用来接收目标方法抛出异常的异常对象的参数名 --> <aop:after-throwing method="printLogAfterCoreException" pointcut-ref="logPointCut" throwing="targetMethodException"/> <!-- aop:after标签:配置后置通知 --> <aop:after method="printLogCoreFinallyEnd" pointcut-ref="logPointCut"/> <!-- aop:around标签:配置环绕通知 --> <!--<aop:around method="......" pointcut-ref="logPointCut"/>--> </aop:aspect> </aop:config> -
测试
java@SpringJUnitConfig(locations = "classpath:spring-aop.xml") public class AopTest { @Autowired private Calculator calculator; @Test public void testCalculator(){ System.out.println(calculator); calculator.add(1,1); } }
Spring 声明式事务---基于 注解的声明式事务
事务的作用:事务是用来保证一系列数据库操作要么全部成功,要么全部失败的一种机制。
也就是添加了一套try...catch...finally...
作用步骤 1. 装配事务管理实现对象 mybaits和jdbcTemplate 用DataSourceTransactionManager
- 用 @Transactional 标签放在方法或类上即可
基本事务控制
-
准备依赖
spring<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> <!--用来@SpringJUnitConfig,不用手动创建IOC和组件实例化的--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>6.0.6</version> <scope>test</scope> </dependency> <!--注解的依赖--> <dependency> <groupId>jakarta.annotation</groupId> <artifactId>jakarta.annotation-api</artifactId> <version>2.1.1</version> </dependency> <!-- 数据库驱动 和 连接池--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.25</version> </dependency> <!--用来DruidDataSource连接池的--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.8</version> </dependency> <!-- spring-jdbc,里面有 DataSourceTransactionManager 接口 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>6.0.6</version> </dependency> <!-- 声明式事务依赖,用来一致性的--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>6.0.6</version> </dependency> // <!--用来Spring AOP的面向切面的编程,但是Spring-context会传递这个--> // <dependency> // <groupId>org.springframework</groupId> // <artifactId>spring-aop</artifactId> // <version>6.0.6</version> // </dependency> <!--用来Spring AOP的面向切面的编程--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>6.0.6</version> </dependency> </dependencies> -
外部配置文件
jdbc.properties
javajxnu.url=jdbc:mysql://localhost:3306/studb //或者 jxnu.url= jdbc:mysql:///studb jxnu.driver=com.mysql.cj.jdbc.Driver jxnu.username=root jxnu.password=password -
Spring 配置文件
spring@Configuration @ComponentScan(value="com.jxnu") @PropertySource("classpath:jdbc.properties") @EnableTransactionManager //在配置文件中允许事务 public class JavaConfiguration{ @Bean public DataSource dataSource(@Value("${jxnu.url}") String url,@Value("${jxnu.driver}" String driver) @Value("${jxnu.username}") String username,@Value("${jxnu.password}") String password) { DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setUrl(url); druidDataSource.setDriver(driver); druidDataSource.setUsername(username); druidDataSource.setPassword(password); return druidDataSource; } @Bean public JdbcTemplate jdbcTemplate(DataSource dataSource){ JdbcTemplate jdbcTemplate = new JdbcTemplate(); jdbcTemplate.setDataSource(dataSource); return jdbcTemplate; } @Bean //装配事务管理实现对象 public TransactionManager transactionManager(DataSource dataSource){ DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); dataSourceTransactionManager.setDataSource(dataSource); return dataSourceTransactionManager; //或者 return new DataSourceTransactionManager(dataSource)就可以了。 } } -
准备 dao/service 层
- dao
java@Repository public class StudentDao{ @Autowired private JdbcTemplate jdbcTemplate; public void updateNameById(String name,Integer id){ String sql = "update students set name = ? where id= ?"; int rows = jdbcTemplate.update(sql,name,id); } public void updateAgeById(Integer age,Integer id){ String sql = "update students set age = ? where id= ?"; int rows = jdbcTemplate.update(sql,age,id); } }- service
java@Service public class StudentService{ @Autowired private StudentDao studentDao; @Transactional //也可以放在类上,代表类中每一个方法都有事务。 public void changeInfo(){ studentDao.updateAgeById(100,1); studentDao.updateNameById("test1",1); } }
事务属性:只读
-
只读介绍:
对一个查询操作来说,如果我们把它设置成只读,更快。
一般情况下,都是通过类添加注解,添加事务
类下的所有方法都有事务
查询方法可以通过再次添加只读事务,提高效率!
查询方法不需要事务,因为它不会改变数据库中的数据!
-
设置方式
jaav@Transactional(readOnly="true")默认值是false
超时时间
-
需求:
事务在执行过程中,有可能因为遇到某些问题,导致程序卡住,从而长时间占用数据库资源。而长时间占用资源。
-
设置超时时间
java@Transactional(reanOnly=true,timeout = 3) // 超时3秒就回滚,释放资源 。
事务属性:事务异常
-
设置回滚异常
rollbackFor 属性:指定哪些异常类才会回滚,默认是 RuntimeException 和 Error 异常方可回滚。
java@Transactional(timeout=3,readOnly=true,rollbackFor=Expection.class)
事务传播行为
即当一个函数调用一个,或多个具有事务的子函数,子函数是独立,还是与父函数具有相同的事务。就是与子函数之间,是否一起成功和失败,还是子函数之间相互不影响。
-
propagation 属性:
名称 含义 REQUIRED 如果父方法有事务,就加入,如果没有就自己独立 REQYUERS_NEW 不管父方法是否有事务,都独立 -
代码
java@Transactional(propagation = Propogation.REQUIRES_NEW)如果你想要把哪个子函数的事务独立出来,就在哪个子函数上加REQUIERS_NEW
-
注意:
- propagation属性是加在子函数上的,默认是REQUIRED
- 父函数与子函数不能在同一个类上。因为无法代理
Spring 核心掌握总结
| 核心点 | 掌握目标 |
|---|---|
| spring框架理解 | spring 家族和 spring framework 框架 |
| spring核心功能 | ioc/di ,aop,tx |
| spring ioc/di | 组件管理、ioc 容器、ioc/di、三种配置方式 |
| spring aop | aop 和 aop 框架和代理技术、基于注解的aop 配置 |
| spring tx | 声明式和编程式事务、动态事务管理器、事务注解、属性 |