需要获取此学习笔记的小伙伴可以+q2760598291获取,记得点赞收藏哦!感谢你的关注!
第一章 初识Spring
1.1 Spring简介
-
Spring是一个为简化企业级开发而生的开源框架。
-
Spring是一个IOC(DI)和AOP容器框架。
-
IOC全称:Inversion of Control【控制反转】
- 将对象【万物皆对象】控制权交给Spring
- 注意:new的对象,不能被JVM垃圾回收机制回收
-
DI全称:(Dependency Injection):依赖注入,可以理解为不仅可以管理对象本身以外,还可以管理其依赖关系,比如管理其属性。
-
AOP全称:Aspect-Oriented Programming,面向切面编程
-
官网:https://spring.io/
1.2 Spring模块
1.3 搭建Spring框架步骤
-
导入jar包,该包中含有spring-jcl包(日志包),也就是说不用向mybatis一样依赖第三方日志 log4j了
<!--导入spring-context,加上其依赖,共6个jar包--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.1</version> </dependency> <!--导入junit4.12--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency>
-
编写核心配置文件
-
配置文件名称:applicationContext.xml【beans.xml或spring.xml】
-
配置文件路径:src/main/resources
-
示例代码
<?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容器中--> <!-- id指明在测试类创建对象中传入的类取名字--> <!-- 例如:Student student = (Student)context.getBean("studentLiu");--> <!-- class说明是指明id所对应的bean类--> <bean id="studentLiu" class="springdemo.Student"> <!-- 通过property设置对象的初始化属性值--> <property name="studentId" value="2"></property> <property name="studentName" value="刘翰林"></property> </bean> </beans>
-
-
使用核心类库
public class TestMySpring { @Test public void test1(){ //使用Spring之前创建对象 // Student student = new Student(); //使用Spring之后创建对象 //1、先创建容器对象 //2、再通过容器对象获得对象 // ("此处路径默认为编译后的根目录,并非源码目录, // IDEA将放在res资源目录中的文件,编译后自动放在项目的根目录,这样就可以直接使用文件名访问了") ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");//获取容器对象 Student student = (Student) context.getBean("studentLiu");//通过容器对象的getbean方法获取对象 System.out.println(student); } }
1.4 Spring特性
- 非侵入式:基于Spring开发的应用中的对象可以不依赖于Spring的API。
- Servlet是侵入式的,自定义的类要实现Servlet接口才能用Servlet的作用
- 容器:Spring是一个容器,因为它包含并且管理应用对象的生命周期。
- 组件化:Spring实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用XML和Java注解组合这些对象。
- 一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(实际上Spring 自身也提供了表述层的SpringMVC和持久层的JDBCTemplate)。
- 控制反转:IOC——Inversion of Control,翻转资源获取方向。把自己创建资源、向环境索取资源 变成环境将资源准备好,我们享受资源注入。
- 面向切面编程:AOP——Aspect Oriented Programming,在不修改源代码的基础上增强代码功 能。
1.5 Spring中getBean()三种方式
-
getBean(String beanId):通过beanId获取对象
- 不足:需要强制类型转换,不灵活
-
getBean(Class clazz):通过Class方式获取对象
-
不足:容器中有多个相同类的bean对象的时候,会报如下错误:
expected single matching bean but found 2: stuZhenzhong,stuZhouxu
-
-
【推荐使用】getBean(String beanId,Clazz clazz):通过beanId和Class获取对象
- 第一个参数决定获取容器中的哪个对象
- 第二个参数决定将容器中获取的对象赋值给什么类型的对象
-
代码:
-
@Test public void test1(){ //使用Spring之前创建对象 // Student student = new Student(); //使用Spring之后创建对象 //1、先创建容器对象 //2、再通过容器对象获得对象 // ("此处路径默认为编译后的根目录,并非源码目录, // IDEA将放在res资源目录中的文件,编译后自动放在项目的根目录,这样就可以直接使用文件名访问了") //getBean使用三种方式 //方式一:使用getBean(String beanId),此处的beanId就是配置文件中指明的id,弊端:每次都需要对其返回值类型进行强转 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");//获取容器对象 Student student = (Student) context.getBean("studentLiu");//通过容器对象的getbean方法获取对象 System.out.println(student); //方式二:使用getBean(class clazz),这种方法的弊端:,当配置文件中的bean类对应多个beanId的时候就会报错 Student student1 = context.getBean(Student.class); System.out.println(student1); //方式三:方法一和方法二的结合getBean(String beanId,class clazz) Student student3 = context.getBean("studentLiu", Student.class); System.out.println(student3); }
注意:框架默认都是通过无参构造器,帮助我们创建对象。
所以:如提供对象的构造器时,一定添加无参构造器
1.6 bean标签详解
- 属性
- id:bean对象的唯一标识
- class:定义bean的类型【class全类名】
- name:bean对象的不唯一标识(所以现在一般不用),getBean也可以通过name的方式获取bean对象
- scope:bean的作用域,详情看 第七章
- init-method:绑定类的初始化方法,详情看 8.1小节
- destroy-method:绑定类的销毁方法,详情看 8.1小节
- Autowire:基于XML的自动装配,详情看 第九章
- 子标签
- property:为对象中属性赋值【set注入(4.1小节)】
- name属性:设置属性名称
- value属性:设置属性数值
- ref属性:引用外部bean,看 3.3 小节
- property:为对象中属性赋值【set注入(4.1小节)】
day06
第二章 SpringIOC底层实现
IOC:将对象的控制反转给Spring
2.1 BeanFactory与ApplicationContexet
ioc的底层原理实现
- BeanFactory:IOC容器的基本实现,是Spring内部的使用接口,是面向Spring本身的,不是提供给开发人员使用的。****
- ApplicationContext:BeanFactory的子接口的子接口,提供了更多高级特性。面向Spring的使用者,几乎所有场合都使用ApplicationContext而不是底层的BeanFactory。
2.2 图解IOC类的结构
- BeanFactory:Spring底层IOC实现【面向Spring框架,内部接口】
- …
- ApplicationContext:面向程序员
- ConfigurableApplicationContext:提供关闭或刷新容器对象方法
- …
- ClassPathXmlApplicationContext:基于类路径检索xml文件
- AnnotationConfigApplicationContext:基于注解创建容器对象
- FileSystemXmlApplicationContext:基于文件系统检索xml文件
- …
- ConfigurableApplicationContext:提供关闭或刷新容器对象方法
- ApplicationContext:面向程序员
- …
第三章 Spring依赖注入数值问题【重点】
3.1 字面量数值
- 从数据类型角度:基本数据类型及包装类、String
- 从语法角度:value属性或value标签
3.2 CDATA区
-
语法:<![CDATA[value值、SQL语句等等]]>,在sql中遇到也可以使用,所有的xml中都可以使用
-
作用:在xml中定义**特殊字符(比如 <>>,>代表标签结束)**时,使用CDATA区
-
代码示例:
<bean id="studentLiu" class="springdemo.Student"> <!-- 通过property设置对象的初始化属性值--> <property name="studentId" value="2"></property> <!-- 当xml中有特殊的符号或者值时候(比如<<刘翰林>>),会报错比如如下所示--> <!-- <property name="studentName" value="<<刘翰林>>"></property>--> <!-- 这时候可以使用<![CDATA[此处放特殊值]]>来处理,如下--> <property name="studentName"> <value><![CDATA[<<刘翰林>>]]]></value> </property> <!-- 注意:不能利用标签的属性来设置cdata不然也会报错,如下所示:--> <!-- <property name="studentName" value="<![CDATA[<<刘翰林>>]]]>"></property>--> </bean>
3.3 外部已声明bean及级联属性赋值
-
语法:ref
-
注意:级联属性更改数值会影响外部声明bean【ref赋值的是引用】
-
示例代码
<bean id="dept1" class="demo.pojo.Dept"> <property name="deptId" value="1"></property> <property name="deptName" value="技术部"></property> </bean> <bean id="studentJieJie" class="demo.pojo.Employee"> <property name="email" value="JieJie@qq.com"></property> <property name="id" value="1"></property> <property name="lastName" value="JieJie"></property> <property name="salary" value="500.0"></property> <!-- 因为这里的属性不是字面量,所以不能使用value的方式去赋值--> <property name="dept" ref="dept1"></property> <!-- 级联属性更改数值会影响外部声明bean【ref赋值的是引用】,如下图--> <!-- 这里会更改dept1的dept.Name的值,因为上面赋值赋的是引用,这里使用级联方式赋值--> <property name="dept.deptName" value="财务部"></property> </bean>
3.4 内部bean
-
概述
- 内部类:在一个类中完整定义另一个类,当前类称之为内部类
- 内部bean:在一个bean中完整定义另一个bean,当前bean称之为内部bean
-
注意:内部bean不会直接装配到IOC容器中,即不可以通过getBean()获取该内部对象
-
示例代码
<!-- 测试内部bean--> <bean id="liu" class="demo.pojo.Employee"> <property name="id" value="2"></property> <property name="email" value="liu@qq.com"></property> <property name="lastName" value="liu"></property> <property name="salary" value="9999.0"></property> <property name="dept"> <!-- 定义内部bean--> <bean class="demo.pojo.Dept"> <property name="deptId" value="1"></property> <property name="deptName" value="技术部"></property> </bean> </property> </bean>
3.5 集合
-
List
<!-- 测试集合--> <bean id="dept6" class="demo.pojo.Dept"> <property name="deptId" value="6"></property> <property name="deptName" value="创新部"></property> <!-- 可以直接在属性中定义集合,也可以把集合提取出来,如下所示--> <property name="empList"> <!-- 该属性的对象是什么类型的集合,就用什么标签,如Map集合,用<map> --> <list> <ref bean="liu"></ref> <ref bean="studentJieJie"></ref> </list> </property> </bean> <!-- 将集合提取出来的示例--> <!-- 测试提取List, util:list需要引入命名空间,将这个标签敲出来,alt+enter即可--> <util:list id="myList"> <ref bean="liu"></ref> <ref bean="studentJieJie"></ref> </util:list> <bean id="dept7" class="demo.pojo.Dept"> <property name="deptId" value="7"></property> <property name="deptName" value="经理部"></property> <property name="empList" ref="myList"></property> </bean>
-
Map
- 方式一
<!-- 测试map集合方式一--> <bean id="dept8" class="demo.pojo.Dept"> <property name="deptId" value="8"></property> <property name="deptName" value="后勤部"></property> <property name="empMap"> <!-- 这里也可以使用两种方式,直接写在里面或者外部引用,这里写在里面,外部引用见下边--> <map> <!-- 这里的<entry>又有两种方式可以用:--> <!-- 第一种:利用entry标签的属性--> <entry key="1" value-ref="studentJieJie"></entry> <!-- 第二种:使用entry标签里面再使用标签--> <entry> <!-- key指的是注入属性集合的键--> <key><value>2</value></key> <ref bean="liu"></ref> </entry> </map> </property> </bean>
方式二:
<!-- 测试map集合方式二-->
<!-- 使用util:map标签-->
<util:map id="myMap">
<entry key="1" value-ref="studentJieJie"></entry>
<entry>
<key><value>2</value></key>
<ref bean="liu"></ref>
</entry>
</util:map>
<bean id="dept9" class="demo.pojo.Dept">
<property name="deptId" value="9"></property>
<property name="deptName" value="九号部门"></property>
<property name="empMap" ref="myMap"></property>
</bean>
第四章 Spring依赖注入方式【基于XML】
在ApplicationContext对象创建的时候,就会执行注入,
为属性赋值方式
- 通过setXxx()方法
- 通过构造器
- 反射
4.1 set方法注入(常用)
- 语法:<property name=“” value=“”>
- 要求:有相应的setXxx()方法
4.2 构造器注入
- 语法:<constructor-arg name=“” value=“”>
- 要求:有相应参数个数的构造器
4.3 p名称空间注入
导入名称空间:xmlns:p=“http://www.springframework.org/schema/p”
-
语法:<bean p:xxx>
-
底层:也是set方法注入
-
示例代码
<bean id="studentJie" class="springdemo.Student"> <!-- 这种是set注入,也就是设置属性的值是通过对象的set方法--> <property name="studentId" value="3"></property> <property name="studentName" value="洁洁"></property> </bean> <bean id="studentLe" class="springdemo.Student"> <!-- 这种是构造器注入,也就是设置属性是通过构造器--> <constructor-arg name="studentId" value="4"></constructor-arg> <constructor-arg name="studentName" value="乐乐"></constructor-arg> </bean> <!-- 这种使用的是p名称空间注入,其底层也是调用的set方法,需要导入名称空间:xmlns:p="http://www.springframework.org/schema/p"--> <bean id="studentLi" class="springdemo.Student" p:studentId="5" p:studentName="力"></bean>
4.4 //TO DO 基于注解注入
第五章 Spring管理第三方bean
5.1 加载外部属性文件标签
<context:property-placeholder location=“classpath: xxxx” />,用法看下面
5.2 Spring管理druid步骤
-
导入jar包
<!--导入druid的jar包--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency> <!--导入mysql的jar包--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <!-- <version>5.1.37</version> --> <version>8.0.26</version> </dependency>
-
编写db.properties配置文件
db.driver=com.mysql.cj.jdbc.Driver db.url=jdbc:mysql://localhost:3306/test_ssm db.username=root db.password=lhl20020704
-
编写applicationContext_druid.xml相关代码
<!-- 加载外部属性文件db.properties,mybatis用的<properties>--> <!-- 加入context的命名空间 --> <context:property-placeholder location="db.properties"></context:property-placeholder> <!-- 装配数据源 注意:druid数据源,既可以mybatis管理(mybatis-config.xml的<environments>的子标签<datasource>中的<property>), 也可以Spring管理(推荐,如下) --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${db.driver}"></property> <property name="url" value="${db.url}"></property> <property name="username" value="${db.username}"></property> <property name="password" value="${db.password}"></property> </bean>
-
测试
@Test public void test5() throws SQLException { ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext_druid.xml"); DruidDataSource dataSource = ioc.getBean("dataSource", DruidDataSource.class); DruidPooledConnection connection = dataSource.getConnection(); System.out.println(dataSource); System.out.println("=========================="); System.out.println(connection); }
第六章 Spring中FactoryBean(BeanFactory是spring内部创建对象使用的接口)
6.1 Spring中两种bean
- 一种是普通bean:设置的类型就是返回的类型
- FactoryBean是Spring提供的一种整合第三方框架的常用机制。和普通的bean不同,配置一个 FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是 getObject()方法的返回值。通过这种机制,Spring可以帮我们把复杂组件创建的详细过程和繁琐细节都 屏蔽起来,只把最简洁的使用界面展示给我们。
- 另一种是工厂bean【FactoryBean】:返回类型通过该工厂bean的getObject方法指定
- 作用:如需我们程序员参与到bean的创建时,使用FactoryBean。即bean的控制权不完全交给Spring
6.2 FactoryBean使用步骤
-
实现FactoryBean接口
-
重写方法【三个】
package com.atguigu.factory; import com.atguigu.pojo.Dept; import org.springframework.beans.factory.FactoryBean; public class MyFactoryBean implements FactoryBean<Dept> { /** * getObject():参数对象创建的方法 * @return * @throws Exception */ @Override public Dept getObject() throws Exception { Dept dept = new Dept(101,"研发部门"); //..... return dept; } /** * 设置参数对象Class * @return */ @Override public Class<?> getObjectType() { return Dept.class; } /** * 设置当前对象是否为单例,默认为false * @return */ @Override public boolean isSingleton() { return true; } }
-
装配工厂bean,配置applicationContext_factorybean.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"> <bean id="myFactoryBean" class="com.atguigu.factory.MyFactoryBean"></bean> </beans>
-
测试
@Test public void testFactoryBean(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext_factorybean.xml"); Dept dept = context.getBean("myFactoryBean", Dept.class);// 注意是Dept.class --> System.out.println("dept = " + dept); }
第七章 Spring中bean的作用域
7.1 语法
- 在bean标签中添加属性:scope属性即可,scope的值为下面四种
7.2 四个作用域
- singleton【默认值】:单例【在容器中只有一个对象】,<bean>不加scope属性时,默认单例
- 对象创建时机:创建容器对象时,创建对象执行
- prototype:多例【在容器中有多个对象】
- 对象创建时机:getBean()方法被调用时,创建对象执行
- request:请求域
- 当前请求有效,离开请求域失效
- 当前请求:URL不变即为当前请求
- session:会话域
- 当前会话有效,离开当前会话失效
- 当前会话:当前浏览不关闭不更换即为当前会话(情况默认),默认存活30分钟
7.3 测试prototype多例
-
applicationContext-actionScope.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"> <!-- 设置成单例模式singleton,容器中只有一个对象,在容器创建时调用--> <!-- 设置成多例模式prototype,容器中会创建多个对象,在容器调用getBean方法时创建--> <bean id="dept" class="demo.pojo.Dept" scope="prototype"> <property name="deptId" value="1"></property> <property name="deptName" value="技术部"></property> </bean> </beans>
-
testActionScope方法
@Test public void test7(){ ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext-beanScope.xml"); Dept dept = ioc.getBean("dept", Dept.class); Dept dept1 = ioc.getBean("dept", Dept.class); System.out.println("是否相等:" + (dept == dept1)); // 运行结果: // 调用了构造器... // 调用了构造器... // 是否相等:false }
第八章 Spring中bean的生命周期
8.1 bean的生命周期
① 通过构造器或工厂方法创建bean实例
② 为bean的属性设置值和对其他bean的引用(调用set方法)
③ 调用bean的初始化方法
④ bean可以使用了
⑤ 当容器关闭时,调用bean的销毁方法
-
Student类
public class Student { private Integer studentId; private String studentName; /** * 初始化方法 * @date * @author * @return * @throws */ public void initStudent(){ System.out.println("初始化对象..."); } /** * 销毁方法 * @date * @author * @return * @throws */ public void destroyStudent(){ System.out.println("对象被销毁了..."); } public Integer getStudentId() { return studentId; } public void setStudentId(Integer studentId) { this.studentId = studentId; } public String getStudentName() { return studentName; } public void setStudentName(String studentName) { this.studentName = studentName; } @Override public String toString() { System.out.println("对象被使用了..."); return "Student{" + "studentId=" + studentId + ", studentName='" + studentName + '\'' + '}'; } public Student(Integer studentId, String studentName) { this.studentId = studentId; this.studentName = studentName; } public Student() { System.out.println("对象被创建了..."); } }
-
applicationContext-lifeCycle.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"> <!-- 分别设置对象的初始化方法(init-method)和销毁方法(destroy-method)--> <bean id="student" class="demo.pojo.Student" init-method="initStudent" destroy-method="destroyStudent" scope="prototype"> <property name="studentId" value="1"></property> <property name="studentName" value="刘翰林"></property> </bean> </beans>
-
测试示例
/** *bean声明周期的测试 * @date * @author * @return * @throws */ @Test public void test8(){ //如果是单例,则对象的初始化方法和创建都随着ioc的创建而调用 //如果是多例,则对象的初始化方法和创建都随着ioc.getBean方法的调用而调用 ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext_lifeCycle.xml"); Student student = ioc.getBean("student", Student.class); System.out.println(student);//对象的使用 ioc.close();//对象的销毁 // 值得注意的是此处ioc对象的销毁得使用ConfigurableApplicationContext类 }
8.2 bean的后置处理器
-
作用:在调用初始化方法前后对bean进行额外的处理。
-
实现:
-
自定义一个类实现BeanPostProcessor接口,该类一般放processor包下
-
重写方法
- postProcessBeforeInitialization(Object, String):在bean的初始化之前执行
-
postProcessAfterInitialization(Object, String):在bean的初始化之后执行
-
在容器中装配后置处理器
<?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属性可以省略,class属性为后置处理器的全类名--> <!-- 装配之后,对这个容器(也就是这个配置文件中)所有的bean都有效--> <bean id="processor" class="demo.processor.MyProcessor"></bean> </beans>
-
-
注意:装配后置处理器会为当前容器(xml)中每个bean均装配(即每个bean都有这个后置处理器了),不能为局部bean装配后置处理器
8.3 添加后置处理器后bean的生命周期
① 通过构造器或工厂方法创建bean实例
② 为bean的属性设置值和对其他bean的引用
3.1 postProcessBeforeInitialization(Object, String):在bean的初始化之前执行
③ 调用bean的初始化方法
3.2 postProcessAfterInitialization(Object, String):在bean的初始化之后执行
④ bean可以使用了
⑤ 当容器关闭时,调用bean的销毁方法
第九章 Spring中自动装配【基于XML】
代码看day06的P17-20,该部分建议看视频复习,总时长20分钟左右,代码示例部分看明白了也行
9.1 Spring中提供两种装配方式,所谓的装配可以理解成就是spring帮助我们new对象
- 手动装配:本章节以上的的,手动的给属性赋值了
- 自动装配:本章节讨论基于XML的自动装配,还有一种基于注解的自动装配,看10.2小节
9.2 Spring自动装配语法及规则(注意要重写set方法,因为自动装配是基于set注入的)
-
在bean标签中添加属性:Autowire即可
-
byName:类中要装配对象的属性名称与容器中的bean标签的Id进行匹配,如果属性名与beanId数值一致,则自动装配成功
代码示例
<?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="daoImpl" class="demo.dao.daoImpl"></bean> <!-- 将bean标签中的autowire属性设置成byName,就是byName的自动装配方式--> <!-- byName的自动装配方式,意思匹配该类的属性与此容器中是否有其他bean标签的id名相等的,有则自动装配--> <!-- 比如这里就是自动装配了daoImpl,因为在serviceImpl类中有个属性叫daoImpl--> <bean id="serviceImpl" class="demo.service.serviceImpl" autowire="byName"></bean> </beans>
serviceImpl类:
public class serviceImpl implements service { daoImpl daoImpl = new daoImpl();//注意这里可以不用new对象,不然会报空指针异常 //此处记得要重写set方法,因为自动装配是基于set方法注入的!!! @Override public void saveObject() { daoImpl.insertObject(); } }
-
byType:类中要装配对象的属性类型与容器中class进行匹配,属性的类型是一个接口,class对应的类的这个接口的实现类,如果唯一匹配则自动装配成功,装配成class对应的类【如第二张图中DeptServiceImpl类,第十七行配置了自动装配,其类中成员 deptDao 会被第二张图中第七行的<bean>装配】
-
匹配0个:未装配
-
匹配多个,会报错
expected single matching bean but found 2: deptDao,deptDao2
-
-
-
注意:基于XML方式的自动装配,只能装配非字面量数值
-
代码示例:注意第二张图的第七行的<bean>必须有,deptService这个<bean>配置为自动给
9.3 总结
-
基于xml自动装配,底层使用set注入,也就意味着应提供属性相应的set方法
-
最终:不建议使用byName、byType,建议使用注解方式自动装配
day07
第十章 Spring中注解【非常重要】
10.1 使用注解将对象装配到IOC容器中
约定:约束>配置【注解>XML】>代码
位置:在类上面标识
注意:
- Spring本身不区分四个注解【四个注解本质是一样的@Component】,提供四个注解的目的只有一个:提高代码的可读性
- 如:在类上加上注解:@Repository ,就不用配置XML的自动装配的bean了(也就是第九章代码示例第二张图中的第七行代码)
- 使用注解装配对象,默认将类名首字母小写作为bean的Id。
- 也可以使用value属性,设置beanId;当注解中只使用一个value属性时,value关键字可省略。如:使用@Repository(String value() default “” )来指定beanId。@Repository(value = “deptDapImpl**”)**
使用注解其实就是代替了标签将对象装配到容器中
-
注意1:使用注解自动装配后,注解的形参就是bean的Id,具体看上面详解
-
注意2:@Scope(“prototype”)此注解是设置装配对象是单例还是多例,getBean时,若beanId相同,则获取的对象为同一个(相同地址),也就是相当于;若beanId不同,对获取的是不同的对象(地址不同),也就是类似。scope属性的值看 7.2小节
-
注意3:若注解只有一个属性value,则编写参数时可以省略value=,多个属性则必须加上
-
装配对象四个注解
- @Component:装配普通组件到IOC容器
- @Repository:装配持久化层组件到IOC容器
- @Service:装配业务逻辑层组件到IOC容器
- @Controller:装配表述层|控制层组件到IOC容器。JavaWeb叫Servlet
值得注意的是,当使用这四个注解不指明value的值时,他们会默认使用该类首字母小写的类名
-
使用注解步骤
-
导入相关jar包【已导入,即导入spring-context依赖即可】
-
开启组件扫描,就是扫描器扫描所有的注解
<!-- 开启组件扫描 base-package:设置能扫描注解的包名【当前包及其子包】, 若有多个包,中间逗号隔开【不推荐,若包太多,太繁琐】,应采用第十一章的方法 未开启或者context的命名空间会报错:NoSuchBeanDefinitionException 异常 --> <context:component-scan base-package="demo"></context:component-scan>
-
使用注解标识组件
-
-
代码示例,为简化步骤,Controller的注解自动装配与Dao和Service方法类似,用@Controller即可
-
DeptDaoImpl类,持久化层组件使用@Repository注解
@Repository(value = "deptDao") public class DeptDaoImpl implements DeptDao { @Override public void insertData() { System.out.println("插入数据成功!!"); } }
-
DeptServiceImpl类,业务逻辑层组件使用@Service注解
@Service(value = "deptService") public class DeptServiceImpl implements DeptService { DeptDaoImpl daoImpl; @Override public void addData() { daoImpl.insertData(); } }
-
DeptController类,控制层组件使用@Controller注解
-
@Controller("deptController") @Scope("prototype")//此注解是设置装配对象是单例还是多例 public class DeptController { DeptService deptService; /** * 模拟处理数据 * @date * @author * @return * @throws */ public void saveDept(){ deptService.addData(); } }
-
applicationContext.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" 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"> <!-- 注意:命名空间是第4、7、9这三行的,别弄错了 --> <!-- 开启组件扫描 base-package:设置能扫描注解的包名【当前包及其子包】, 若有多个包,中间逗号隔开【不推荐,若包太多,太繁琐】,应采用第十一章的方法 未开启或者context的命名空间会报错:NoSuchBeanDefinitionException 异常 --> <context:component-scan base-package="com.atguigu"></context:component-scan> </beans>
-
-
测试代码
/** * 测试四个注解【将对象装配到容器中】 */ public class MyTest { @Test public void test1(){ ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml"); DeptController deptController = ioc.getBean("deptController", DeptController.class); DeptServiceImpl deptService = ioc.getBean("deptService", DeptServiceImpl.class); DeptDaoImpl deptDao = ioc.getBean("deptDao", DeptDaoImpl.class); Dept dept = ioc.getBean("dept", Dept.class); System.out.println("deptController" + deptController); System.out.println("deptService" + deptService); System.out.println("deptDao" + deptDao); System.out.println("dept" + dept); // 运行结果: // deptControllerdemo.controller.DeptController@7a52f2a2 // deptServicedemo.service.impl.DeptServiceImpl@78047b92 // deptDaodemo.dao.impl.DeptDaoImpl@8909f18 // deptDept{deptId=null, deptName='null', empList=null, empMap=null} } }
10.2 使用注解装配对象中属性【自动装配】
之前是xml自动装配,对比9.2小节
-
未给属性装配时,deptDao**.insertDept**(dept); 会报空指针异常
-
@Autowired注解
-
作用:自动装配对象中属性
-
装配原理:反射机制
-
装配方式
-
先按照byType进行匹配
-
匹配1个:匹配成功,正常使用
-
匹配0个:
-
默认【@Autowired(required=true)】报错
/*expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} */
-
@Autowired(required=false),不会报错
-
-
匹配多个
-
再按照byName进行唯一筛选
-
筛选成功【对象中属性名称==beanId】,正常使用
-
筛选失败,报如下错误:
【对象中属性名称!=beanId】 //expected single matching bean but found 2: deptDao,deptDao2 【又有两个 beanId 相同】 //最上面:BeanDefinitionStoreException //Caused by ... : Annotation-specified bean name 'deptDao' for bean class [com.atguigu.dao.impl.DeptDaoImpl2] conflicts with existing, non-compatible bean definition of same name and class [com.atguigu.dao.impl.DeptDaoImpl]
-
-
-
-
-
@Autowired中required属性
- true:表示被标识的属性必须装配数值,如未装配,会报错。
- false:表示被标识的属性不必须装配数值,如未装配,不会报错。
-
-
@Qualifier注解
-
作用:配合@Autowired一起使用,通过装配对象注解的参数,设置的beanId名称装配到@Qualifier
注解的属性中,此时也就不需要beanId与属性名称一致了
-
注意:不能单独使用,需要与@Autowired一起使用。一般用于有多个同类型的bean时,一个的时候直
接通过byType的装配好
-
用法:@Qualifier(“beanId”)
要装配的属性
@Service(value = "deptService") public class DeptServiceImpl implements DeptService { @Autowired //该注解是自动装配属性,按照先使用byType后byName的方式 @Qualifier("deptDaoImpl1") //配合Autowired使用,当容器中有多个该类对象时指定id使用哪个 DeptDao deptDaoImpl; @Override public void addData() { deptDaoImpl.insertData(); } }
-
-
@Value注解
- @Autowired和@Qualifier都是用来装配非字面量值的
- 作用:装配对象中的属性【字面量数值】,也就是初始值。如:@Value(“le”) String name;
-
@Component("student") public class Student { @Value("1") private Integer studentId; @Value("刘") private String studentName; } //在创建该类对象时就会z
第十一章 Spring中组件扫描
11.1 默认使用情况
<!-- 开启组件扫描
base-package:设置能扫描注解的包名【当前包及其子包】,
若有多个包,中间逗号隔开【不推荐,若包太多,太繁琐】,应采用第十一章的方法
未开启或者context的命名空间会报错:NoSuchBeanDefinitionException 异常
-->
<context:component-scan base-package="com.atguigu"></context:component-scan>
11.2 包含扫描
- 注意:
- 使用包含扫描之前,必须设置use-default-filters=“false”【关闭当前包及其子包的扫描】,关闭默认的组件扫描
- type
- annotation:设置被扫描注解的全类名
- assignable:设置被扫描**[实现]类**的全类名
<!-- 包含扫描,指定需要扫描的组件-->
<context:component-scan base-package="demo" use-default-filters="false"><!--关闭当前包及其子包的扫描-->
<!-- 这种是扫描指定的注解-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<!-- 这种是扫描指定的类-->
<context:include-filter type="assignable" expression="demo.service.impl.DeptServiceImpl"/>
<context:include-filter type="assignable" expression="demo.pojo.Student"/>
</context:component-scan>
11.3 排除扫描
<!-- 排除扫描-->
<!--就是不扫描指定包下的指定类或者声明的注解-->
<context:component-scan base-package="demo">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="assignable" expression="demo.controller.DeptController"/>
</context:component-scan>
第十三章 Spring完全注解开发【0配置】
13.1 完全注解开发步骤
- 创建配置类
- 在class上面添加注解
- @Configuration:标识当前类是一个配置类,作用:代替XML配置文件
- @ComponentScan:设置组件扫描当前包及其子包
- 使用AnnotationConfigApplicationContext容器对象
13.2 示例代码
@Configuration //该注解表明此类是作为配置的类
@ComponentScan(basePackages = "demo") //此注解表明组件扫描的包名
public class SpringConfig {
}
@Test
public void test1(){
//创建容器对象,基于XML配置文件,该处未使用xml配置了,所以不能使用该构造器
// ApplicationContext context =
// new ClassPathXmlApplicationContext("applicationContext.xml");
//使用AnnotationConfigApplicationContext容器对象
ApplicationContext ioc =
new AnnotationConfigApplicationContext(SpringConfig.class);
// 此处记得注意,getBean的名字是该类上标明注解的名
// 比如在DeptServiceImpl中就声明了注解:@Service(value = "deptService"),
// 所以下面传入的bean名应该是deptService
DeptServiceImpl deptServiceImpl = ioc.getBean("deptService", DeptServiceImpl.class);
deptServiceImpl.addData();
}
第十四章 Spring集成Junit4
了解,后面会讲Junit5【看20.3 小节】,现在都用Junit5了
14.1 为什么要集成?
-
现象:每写一个测试方法都得重新获取一个容器对象,且获取的步骤都大致相同,只有参数不同。
-
好处:一旦注解写好之后,将不再需要创建容器对象。只需在测试类定义一个属性,通过注解的方式给属性装
配即可。
14.2 集成步骤
-
导入jar包
-
spring-test-5.3.1.jar 【版本需与spring-context版本一致】
<!-- junit4 的jar包--> <!-- https://mvnrepository.com/artifact/org.springframework/spring-test --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.3.1</version> <scope>test</scope> </dependency>
-
-
指定Spring的配置文件的路径
- 【@ContextConfiguration】
-
指定Spring环境下运行Junit4的运行器
- @RunWith(Junit的版本)。如:@RunWith(SpringJUnit4ClassRunner.class)
14.3 示例代码
@ContextConfiguration(locations = "classpath:applicationContext.xml")//指明配置文件的路径
@RunWith(SpringJUnit4ClassRunner.class)//指定Spring环境下运行Junit4的运行器
public class JunitTest {
@Autowired //此处记得自动装配属性
private DeptServiceImpl deptService;
@Test
public void test1(){
// ApplicationContext ioc =
// new AnnotationConfigApplicationContext(SpringConfig.class);
// deptService = ioc.getBean("deptService", DeptServiceImpl.class);
// 这种做法需要每次此时都要new一个容器对象
// 这种做法由于属性自动装配了可以直接使用
deptService.addData();
}
}
第十五章 AOP前奏
本章主要讲解代理模式
可回顾javase的静态代理和动态代理
控制台输出对象,类名以$开头的就代表是代理对象
15.1 代理模式
-
代理模式:我们需要做一件事情,又不期望自己亲力亲为,此时,可以找一个代理【中介】
-
我们【目标对象】与中介【代理对象】不能相互转换,因为是“兄弟”关系
15.2 为什么需要代理【程序中】
-
需求:实现【加减乘除】计算器类
- 在加减乘除方法中,添加日志功能【在计算之前,记录日志。在计算之后,显示结果。】
-
实现后发现问题如下
- 日志代码比较分散,可以提取日志类
- 日志代码比较混乱,日志代码【非核心业务代码】与加减乘除方法【核心业务代码】书写一处
-
总结:在核心业务代码中,需要添加日志功能,但不期望在核心业务代码中书写日志代码。
- 此时:使用代理模式解决问题【先将日志代码横向提取到日志类中,再动态织入回到业务代码中】
15.3 手动实现动态代理环境搭建
-
实现方式
- 基于接口实现动态代理: JDK动态代理
- 基于继承实现动态代理: Cglib、Javassist动态代理
-
实现动态代理关键步骤
- 一个类:Proxy
- 概述:Proxy代理类的基类【类似Object】
- 作用:newProxyInstance():创建代理对象
- 一个接口:InvocationHandler
- 概述:实现【动态织入效果】关键接口
- 作用:invoke(),执行invoke()实现动态织入效果
- 一个类:Proxy
15.4 手动实现动态代理关键步骤
注意:代理对象与实现类【目标对象】是“兄弟”关系,不能相互转换
-
创建类【为了实现创建代理对象工具类】
-
提供属性【目标对象:实现类】
-
提供方法【创建代理对象】
-
提供有参构造器【避免目标对为空】
-
代码示例:
-
Calc接口
public interface Calc { public int add(int a, int b); public int sub(int a, int b); public int mul(int a, int b); public int div(int a, int b); }
-
CalcImpl类
public class CalcImpl implements Calc { @Override public int add(int a, int b) { return a + b; } @Override public int sub(int a, int b) { return a - b; } @Override public int mul(int a, int b) { return a * b; } @Override public int div(int a, int b) { return a / b; } }
-
MyLogging类
public class MyLogging { /** * 方法之前 * @param methodName * @param args */ public static void beforeMethod(String methodName, Object[] args){ System.out.println("==>Calc中"+ methodName +"方法(),参数:" + Arrays.toString(args)); } /** * 方法之后 * @param methodName * @param rs */ public static void afterMethod(String methodName, Object rs){ System.out.println("==>Calc中"+ methodName +"方法(),结果:" + rs); } }
-
MyProxy类(代理类)
package com.atguigu.beforeaop; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class MyProxy { /** * 目标对象【目标客户】 */ private Object target; public MyProxy(Object target){ this.target = target; } /** * 获取目标对象的,代理对象 * @return */ public Object getProxyObject(){ Object proxyObj = null; /** 类加载器【ClassLoader loader】,目标对象类加载器 目标对象实现接口:Class<?>[] interfaces,目标对象实现所有接口 InvocationHandler h */ ClassLoader classLoader = target.getClass().getClassLoader(); Class<?>[] interfaces = target.getClass().getInterfaces(); //创建代理对象 proxyObj = Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() { //执行invoke()实现动态织入效果 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //获取方法名【目标对象】 String methodName = method.getName(); //执行目标方法之前,添加日志 MyLogging.beforeMethod(methodName,args); //触发目标对象目标方法 Object rs = method.invoke(target, args); //执行目标方法之后,添加日志 MyLogging.afterMethod(methodName,rs); return rs; } }); return proxyObj; } // class invocationImpl implements InvocationHandler{ // } }
-
测试方法
@Test public void testBeforeAop(){ // int add = calc.add(1, 2); // System.out.println("add = " + add); //目标对象 Calc calc = new CalcImpl(); //代理工具类 MyProxy myProxy = new MyProxy(calc); //获取代理对象 Calc calcProxy = (Calc)myProxy.getProxyObject(); //测试 // int add = calcProxy.add(1, 2); int div = calcProxy.div(2, 1); }
-
day08
第十六章 Spring中AOP【重点】
16.1 AspectJ框架【AOP框架】
- AspectJ是Java社区里最完整最流行的AOP框架。不是Spring家族的,Spring只是提供支持。
- 在Spring2.0以上版本中,可以使用基于AspectJ注解或基于XML配置的AOP。
- 使用了这个框架后,动态代理将不需要我们手动编写
16.2 使用AspectJ步骤
-
添加jar包支持
<!-- 添加AspectJ--> <!--spirng-aspects的jar包--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.3.1</version> </dependency>
-
配置文件
- 开启组件扫描
- 开启AspectJ注解支持
<!-- 开启组件扫描--> <context:component-scan base-package="com.atguigu"></context:component-scan> <!-- 开启AspectJ注解支持,需引入名称空间--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
-
CalcImpl类上面添加注解:@Component(“calc”)
-
将MyLogging类上面添加注解
- @Component:将当前类标识为一个组件,使该类装配到容器中
- @Aspect:将当前类标识为切面类【非核心业务提取类】
-
将MyLogging中的方法中添加通知注解(一共五个)
- @Before。参数为value,值为切入点表达式(看17.3小节)
@Component //将当前类标识为一个组件 @Aspect //将当前类标识为【切面类】【非核心业务提取类】 public class MyLogging { /** * 方法之前,方法里的好多东西暂时未讲 */ //此注解可以理解为就是在value里面这个方法执行之前,先执行下面这个方法beforeMethod @Before(value = "execution(public int demo.CalcImpl.add(int, int))") public void beforeMethod(JoinPoint joinPoint){ //通过joinPoint获取参数和方法名 String methodName = joinPoint.getSignature().getName(); Object[] args = joinPoint.getArgs(); System.out.println("==>Calc中"+ methodName +"方法(),参数:" + Arrays.toString(args)); } }
-
测试
@Test public void testAop(){ //创建容器对象 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext_aop.xml"); Calc calc = context.getBean("calc", Calc.class); //错误的,代理对象不能转换为目标对象【代理对象与目标对象是兄弟关系】 //getBean方法,第一个参数决定获取容器中的哪个对象, // 第二个参数决定将容器中获取的对象赋值给什么类型的对象 //CalcImpl calc = context.getBean("calc", CalcImpl.class); int add = calc.add(1, 2); }
//TO DO 通知注解
16.3 Spring中AOP概述
- AOP:Aspect-Oriented Programming,面向切面编程【面向对象一种补充】,对对象的横向补充
- 优势:
- 解决代码分散问题
- 解决代码混乱问题
- 优势:
- OOP:Object-Oriented Programming,面向对象编程,对对象的纵向补充
16.4 Spring中AOP相关术语
-
横切关注点:非核心业务代码【日志】,称之为横切关注点
-
切面(Aspect):将横切关注点提取到类中,这个类称之为切面类
-
通知(Advice):将横切关注点提取到类中之后,横切关注点更名为:通知也叫通知方法
-
目标(Target):目标对象,指的是需要被代理的对象【实现类(CalcImpl)】
-
代理(Proxy):代理对象可以理解为:中介
-
连接点(Joinpoint):通知方法需要指定通知位置(也就是@before的value的值),
这个位置称之为:连接点【通知之前,没执行通知注解之前】
可以理解为就是非核心业务代码【日志】执行的位置(个人理解)
-
切入点(pointcut):通知方法需要指定通知位置,
这个位置称之为:切入点【通知之后,执行了通知注解之后,更名为切入点】
如何区分连接点和切入点,简单理解以下:可以理解为,可以插入通知的地方为连接点,在连接点插入通知之后,该连接点就被称为切入点。(个人理解)连接点是在应用执行过程中能够插入切面(Aspect)的一个点
第十七章 AspectJ详解【重点】
17.1 AspectJ中切入点表达式
-
语法:@Before(value=“execution(权限修饰符 返回值类型 包名.类名.方法名(参数类型))”)
-
通配符
【*】:
【*】:可以代表任意权限修饰符&返回值类型
【*】:可以代表任意包名、任意类名、任意方法名
【…】:
【…】:代表任意参数类型及参数个数
-
重用切入点表达式
-
使用**@PointCut注解**,提取可重用的切入点表达式
@Pointcut("execution(* com.atguigu.aop.CalcImpl.*(..) )") public void myPointCut(){}
-
使用**方法名()**引入切入点表达式,支持引用其他类的重用切入点表达式,在方法名加上其他类的全类名即可(两个类属于同包可直接写类名.方法名),如引用MyLogging的重用切入表达式:@Before(value = “MyLogging.myPointCut()”)
//之前: @Before(value = "execution(public int com.atguigu.aop.CalcImpl.add(int, int) )") public void beforeMethod(JoinPoint joinPoint){} //使用重用切入点表达式之后 @Before(value = "myPointCut()") public void beforeMethod(JoinPoint joinPoint){} @After("myPointCut()") public void afterMethod(){...}
-
17.2 AspectJ中JoinPoint对象
-
JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象(通俗点来说就是JoinPoint对象能够获取到核心业务代码方法的参数和方法名等)
-
JoinPoint【切入点对象】,通知方法的参数不能随便写,要是JoinPoint类的对象
-
作用:
-
获取方法名称
//获取方法签名【方法签名=方法名+参数列表】 joinPoint.getSignature(); //获取方法名称 String methodName = joinPoint.getSignature().getName();
-
获取参数
Object[] args = joinPoint.getArgs();
-
17.3 AspectJ中通知
-
前置通知
-
语法:@Before
-
执行时机:指定方法执行之前执行【如目标方法中有异常,会执行】
- 指定方法:切入点表达式设置位置
-
示例代码
//重用的切入表达式 @Pointcut("execution(public int demo.CalcImpl.add(int, int))") public void myPointCut(){} /** * 前置通知,在方法执行之前通知 * @date * @author * @return * @throws */ //此注解可以理解为就是在value里面这个方法执行之前,先执行下面这个方法beforeMethod @Before(value = "myPointCut()") public void beforeMethod(JoinPoint joinPoint){ //通过joinPoint获取参数和方法名 String methodName = joinPoint.getSignature().getName(); Object[] args = joinPoint.getArgs(); System.out.println("【前置通知】==>Calc中"+ methodName +"方法(),参数:" + Arrays.toString(args)); }
-
-
后置通知
-
语法:@After
-
执行时机:指定方法所有通知执行之后执行【如目标方法中有异常,会执行】
-
示例代码
/** * 后置通知,在方法执行之后通知 * @date * @author * @return * @throws */ @After("execution(* *.*.add(..))") public void afterMethod(JoinPoint joinPoint){ String methodName = joinPoint.getSignature().getName(); System.out.println("【后置通知】==>Calc中"+ methodName +"方法(),执行之后:"); }
-
-
返回通知
-
语法:@AfterReturnning
-
执行时机:指定方法返回结果时执行,比后置通知早【如目标方法中有异常,不执行】
-
注意事项:@AfterReturnning中returning属性与入参中参数名一致,即将返回值封装到哪个形参中
-
拓展:属性value可以用pointcut代替,底层同时提供了value和pointcut,建议用value,风格更统一
-
示例代码
/** * 返回通知,在方法返回结果之后执行 * @date * @author * @return * @throws */ // 获得的返回结果应该作为参数声明在方法中如:Object result,且在注解中指明returning的值和参数名一致,如:returning = "result" @AfterReturning(value = "execution(public int demo.CalcImpl.add(int,int))",returning = "result") public void afterReturn(JoinPoint joinPoint,Object result){ String methodName = joinPoint.getSignature().getName(); System.out.println("【返回通知】==>Calc中"+ methodName +"方法(),返回结果之后,结果为:" + result); }
-
-
异常通知
-
语法:@AfterThrowing
-
执行时机:指定方法出现异常时执行,比后置通知早,与返回通知两者只会执行其一
【如目标方法中无异常,不执行】
-
注意事项:@AfterThrowing中的throwing属性值与入参参数名一致
-
示例代码:
/** * 异常通知 * @date * @author * @return * @throws */ @AfterThrowing(value = "myPointCut()",throwing = "ex") public void afterException(JoinPoint joinPoint,Exception ex){ //第二个形参写 Throwable ex 也可,Throwable是Exception的父类 String methodName = joinPoint.getSignature().getName(); System.out.println("【异常通知】==>Calc中"+ methodName +"方法(),出现异常执行,异常为:" + ex); }
-
总结
- 有异常:前置通知=》异常通知=》后置通知
- 无异常:前置通知=》返回通知=》后置通知
-
-
环绕通知【前四个通知整合】
-
语法:@Around
-
作用:整合前四个通知
-
注意:
- 参数中必须使用ProceedingJoinPoint(是JoinPoint的一个子类),而不能使用JoinPoint,因为JoinPoint没有proceed方法
- proceed方法是用来调用(执行)目标方法的,这样就可以在执行proceed方法前后等等位置,使用try-catch-finally,加上另外四个通知,起到整合四个通知的作用
-
环绕通知必须将返回结果,作为环绕通知方法的返回值
-
-
示例代码
/** * 环绕通知 * @date * @author * @return * @throws */ @Around(value = "myPointCut()") public Object around(ProceedingJoinPoint joinPoint){ String methodName = joinPoint.getSignature().getName(); Object[] args = joinPoint.getArgs(); Object result = null; try { //这里写前置通知 System.out.println("【前置通知】==>Calc中"+ methodName +"方法(),参数:" + Arrays.toString(args)); result = joinPoint.proceed(); //这里写返回通知 System.out.println("【返回通知】==>Calc中"+ methodName +"方法(),返回结果之后,结果为:" + result); } catch (Throwable ex) { ex.printStackTrace(); //这里写异常通知 System.out.println("【异常通知】==>Calc中"+ methodName +"方法(),出现异常执行,异常为:" + ex); }finally { //这里写后置通知 System.out.println("【后置通知】==>Calc中"+ methodName +"方法(),执行之后:"); } return result; }
17.4 定义切面优先级
-
现象:可能在被代理类方法之前,添加日志功能、验证功能等多个切面类,而这些类又有相同类型的通知,但此时却不确定他们的执行顺序(假设都有前置通知,此时将不确定谁的前置通知先执行)。
-
语法:@Order(value=index)
- index是int类型,默认值是int可存储的最大值
- 数值越小,优先级越高【一般建议使用正整数】
-
示例代码
@Component //将该类表示为一个组件,可以装入到ioc容器中 @Aspect //将该类标识为一个切面类 @Order(value = 5)//指明该切面类的优先级,value越小,优先级越高,通知在其他切面类通知之前执行 public class MyValidate {} @Component //将此类标识为一个组件,可以装配到ioc容器中 @Aspect //将此类表示为切面列【非核心业务提取类】 @Order(value = 2) //指明该切面类的优先级,value越小,优先级越高,通知在其他切面类通知之前执行 public class MyLogging {}
17.5 基于XML方式配置AOP(了解)
<?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: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/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置计算器实现类-->
<bean id="calculator" class="com.atguigu.spring.aop.xml.CalculatorImpl"></bean>
<!--配置切面类-->
<bean id="loggingAspect" class="com.atguigu.spring.aop.xml.LoggingAspect"></bean>
<!--AOP配置-->
<aop:config>
<!--配置切入点表达式-->
<aop:pointcut id="pointCut"
expression="execution(* com.atguigu.spring.aop.xml.Calculator.*(..))"/>
<!--配置切面-->
<aop:aspect ref="loggingAspect">
<!--前置通知-->
<aop:before method="beforeAdvice" pointcut-ref="pointCut"></aop:before>
<!--返回通知-->
<aop:after-returning method="returningAdvice" pointcut-ref="pointCut" returning="result"></aop:after-returning>
<!--异常通知-->
<aop:after-throwing method="throwingAdvice" pointcut-ref="pointCut" throwing="e"></aop:after-throwing>
<!--后置通知-->
<aop:after method="afterAdvice" pointcut-ref="pointCut"></aop:after>
<!--环绕通知-->
<aop:around method="aroundAdvice" pointcut-ref="pointCut"></aop:around>
</aop:aspect>
</aop:config>
</beans>
第十八章 Spring中JdbcTemplate
18.1 JdbcTemplate简介
- Spring提供的JdbcTemplate是一个小型持久化层框架,简化Jdbc代码。
- Mybatis是一个半自动化的ORM持久化层框架
18.2 JdbcTemplate基本使用
-
导入jar包
<!--spring-context--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.1</version> </dependency> <!--junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!--导入mysql的jar包--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.37</version> </dependency> <!--导入druid的jar包--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency> <!-- spring-orm:jdbcTemplate的jar包【前提有:spring的jar,MySQL的jar包,druid的jar包】--> <!-- Spring 在执行持久化层操作、与持久化层技术进行整合过程中,需要使用orm、jdbc、tx三个jar包 --> <!-- 导入 orm 包就可以通过 Maven 的依赖传递性把其他两个也导入 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>5.3.1</version> </dependency>
-
编写配置文件
-
db.properties:设置连接数据库属性
-
applicationContext.xml【spring配置文件】
- 加载外部属性文件
- 装配数据源【DataSources】
- 装配JdbcTemplate
-
示例代码
<!-- 加载外部属性文件--> <context:property-placeholder location="classpath:db.properties"></context:property-placeholder> <!-- 将datasource装配进容器中--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="password" value="${db_password}"></property> <property name="username" value="${db_username}"></property> <property name="url" value="${db_url}"></property> <property name="driverClassName" value="${db_driver}"></property> </bean> <!-- 装配JdbcTemplate--> <!-- 自定义类可用注解来装配bean,但第三方得用xml方式装配bean(毕竟咱也不可能修改对方的源代码吧) --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean>
-
-
使用核心类库【JdbcTemplate】
- 通过getBean获取JdbcTemplate的对象
18.3 JdbcTemplate的常用API
JdbcTemplate默认:自动提交事务
-
jdbcTemplate.update(String sql,Object… args):通用的增删改方法
// ***********通用的增删改方法**************** // jdbcTemplate.update(String sql,Object... args):通用的增删改方法 // 增 // String sql = "insert into tbl_dept (dept_name) values (?)"; // jdbcTemplate.update(sql,"财贸部"); // 改 // String sql = "update tbl_dept set dept_name = ? where dept_id = ?"; // jdbcTemplate.update(sql,"销售部",2); // 删 // String sql = "delete from tbl_dept where dept_id = ?"; // jdbcTemplate.update(sql,2);
-
jdbcTemplate.batchUpdate(String sql,List<Object[]> args):通用批处理增删改方法
// ***********通用批处理增删改方法**************** // 其实批处理操作所传的参数可以理解为一个object[]对象为处理一条数据所需要的参数 // jdbcTemplate.batchUpdate(String sql,List<Object[]> args):通用批处理增删改方法 // 删 // String sql = "delete from tbl_dept where dept_id = ?"; // List<Object[]> list = new ArrayList(); // Object[] o1 = {"3"}; // Object[] o2 = {"4"}; // list.add(o1); // list.add(o2); // jdbcTemplate.batchUpdate(sql,list); // 增 // String sql = "insert into tbl_employee (last_name,email,salary,dept_id) values(?,?,?,?)"; // List<Object[]> list = new ArrayList<>(); // Object[] objects1 = new Object[]{"洁洁","jiejie@qq.com",500.0,1}; // Object[] objects2 = new Object[]{"洁洁","jiejie@qq.com",500.0,1}; // Object[] objects3 = new Object[]{"洁洁","jiejie@qq.com",500.0,1}; // list.add(objects1); // list.add(objects2); // list.add(objects3); // jdbcTemplate.batchUpdate(sql,list); // 改 String sql = "update tbl_employee set last_name = ?,email = ? where id = ?"; List<Object[]> list = new ArrayList<>(); Object[] o1 = {"测试","ceshi@qq.com",6}; Object[] o2 = {"测试","ceshi@qq.com",7}; Object[] o3 = {"测试","ceshi@qq.com",8}; Object[] o4 = {"测试","ceshi@qq.com",9}; list.add(o1); list.add(o2); list.add(o3); list.add(o4); jdbcTemplate.batchUpdate(sql,list);
-
jdbcTemplate.queryForObject(String sql,Class clazz,Object… args):查询单个数值
-
第二个参数为返回值类型
-
String sql = “select count(1) from tbl_xxx”;
// *******************查询单个数值******************** // jdbcTemplate.queryForObject(String sql,Class clazz,Object... args):查询单个数值 // String sql = "select count(*) from tbl_employee"; // Integer integer = jdbcTemplate.queryForObject(sql, Integer.class); // System.out.println(integer);
-
-
jdbcTemplate.queryForObject(String sql,RowMapper rm,Object… args):查询单个对象
-
RowMapper跟DBUtils的XxxHandler用法类似
-
String sql = “select col1,col2… from tbl_xxx”;
-
sping中的RowMapper可以将数据中的每一行数据封装成用户定义的类。我们在数据库查询中,如果返回的类型是用户自定义的类型(其实我们在数据库查询中大部分返回的都是自定义的类)则需要包装(通俗点来说其实就是指定查询出来的多条数据封装成哪个类的对象。)
// jdbcTemplate.queryForObject(String sql,RowMapper<T> rm,Object... args):查询单个对象 String sql = "select * from tbl_employee where id = ?"; RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class); // Employee employee = jdbcTemplate.queryForObject(sql, rowMapper, 1); // 这里的参数不能直接写employee.class Employee employee = jdbcTemplate.queryForObject(sql, rowMapper, 1); System.out.println(employee);
-
-
jdbcTemplate.query(String sql,RowMapper rm,Obejct… args):查询多个对象
// jdbcTemplate.query(String sql,RowMapper<T> rm,Obejct... args):查询多个对象 String sql = "select * from tbl_employee where id = ? or id = ?"; RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class); List<Employee> employeeList = jdbcTemplate.query(sql, rowMapper, 1, 3); System.out.println(employeeList);
18.4 使用JdbcTemplate搭建Service&Dao层
-
Service层依赖Dao层
-
Dao层依赖JdbcTemplate
-
示例代码
@Repository public class DeptDaoImpl implements DeptDao { @Autowired @Qualifier("jdbcTemplate") private JdbcTemplate jdbcTemplate; @Override public List<Dept> selectAllDepts() { String sql = "select dept_id,dept_name from tbl_dept"; RowMapper<Dept> rowMapper = new BeanPropertyRowMapper<>(Dept.class); List<Dept> list = jdbcTemplate.query(sql, rowMapper); return list; } } /** * @author Chunsheng Zhang 尚硅谷 * @create 2022/3/29 16:30 */ @Service("deptService") public class DeptServiceImpl implements DeptService { @Autowired @Qualifier("deptDaoImpl") private DeptDao deptDao; @Override public List<Dept> getAllDepts() { return deptDao.selectAllDepts(); } }
第十九章 Spring声明式事务管理
回顾事务
- 事务四大特征【ACID】
- 原子性:一气呵成的,不可分割的
- 一致性:事务必须使数据库从一个一致性状态变换到另外一个一致性状态。比如:如果从A账户转账到B账户,不可能因为A账户扣了钱,而B账户没有加钱
- 隔离性:事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
- 持久性:持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。
- 事务三种行为
- 开启事务:connection.setAutoCommit(false)
- 提交事务:connection.commit()
- 回滚事务:connection.rollback()
19.1 Spring中支持事务管理
-
编程式事务管理【传统事务管理】
-
获取数据库连接Connection对象
-
取消事务的自动提交【开启事务】
-
执行操作
-
正常完成操作时手动提交事务
-
执行失败时回滚事务
-
关闭相关资源
- 不足:
- 事务管理代码【非核心业务】与核心业务代码相耦合
- 事务管理代码分散
- 事务管理代码混乱
- 事务管理代码【非核心业务】与核心业务代码相耦合
-
-
声明式事务管理【使用AOP管理事务】
- 先横向提取【事务管理代码】,再动态织入
19.2 使用声明式事务管理
不用事务管理代码,发现:同一个业务中,会出现局部成功及局部失败的现象【不正常】
-
添加支持【AspectJ的jar包】
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.3.1</version> </dependency>
-
创建数据库表
CREATE TABLE `t_book` ( `book_id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '主键', `book_name` VARCHAR(20) DEFAULT NULL COMMENT '图书名称', `price` INT(11) DEFAULT NULL COMMENT '价格', `stock` INT(10) UNSIGNED DEFAULT NULL COMMENT '库存(无符号)', PRIMARY KEY (`book_id`) ) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; INSERT INTO `t_book`(`book_id`,`book_name`,`price`,`stock`) VALUES (1,'斗破苍 穹',80,100),(2,'斗罗大陆',50,100); CREATE TABLE `t_user` ( `user_id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '主键', `username` VARCHAR(20) DEFAULT NULL COMMENT '用户名', `balance` INT(10) UNSIGNED DEFAULT NULL COMMENT '余额(无符号)', PRIMARY KEY (`user_id`) ) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; INSERT INTO `t_user`(`user_id`,`username`,`balance`) VALUES (1,'admin',50);
-
编写配置文件
- 配置事务管理器 <bean>
- 开启事务注解支持
<?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:tx="http://www.springframework.org/schema/tx" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 需引入外部配置文件,不然ref="dataSource"无效 --> <!-- 配置事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 开启事务注解支持 transaction-manager默认值:transactionManager。 该属性的值为事务管理器的beanId,若事务管理器的beanId是该默认值,则该属性可以不配置--> <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven> </beans>
-
在需要事务管理的业务方法上,添加注解**@Transactional**,该注解可以声明在方法上也可以声明在类上
//添加事务后,如果是通过getbean方式获取,则第二个参数不能写接口的实现类.class, //因为获取的是代理对象,代理对象不能转换为实现类,他们是兄弟关系 @Transactional @Override public void buyBook(Integer bookId, Integer userId) { //查询图书的价格 Integer price = bookDao.getPriceByBookId(bookId); //更新图书的库存 bookDao.updateStock(bookId); //更新用户的余额 bookDao.updateBalance(userId, price); }
-
关于@Transactional注解的属性:
@Transactional( // readOnly = true //该属性(默认为false)设置成true说明此事务只可读,如果出现增删改会报错 // timeout = 3 //该属性(默认为-1)设置成3表示如果该事务三分钟没有执行完成,就会回滚 // noRollbackFor = {ArithmeticException.class}//该属性表示当出现以下异常时不回滚,注意这里是数组,指明异常的类对象 // noRollbackForClassName = {"java.lang.ArithmeticException"}//该属性表示当出现以下异常时不回滚,指明异常的全类名 )
-
总结:
- 添加声明式事务管理之后,获取是代理对象,代理对象不能转换为目标对象【实现类】
- 原因看16.3小节上面的代码块
19.3 Spring声明式事务管理属性
@Transactional注解的属性propagation、isolation、timeout、readOnly、rollbackFor[noRollbackFor]
-
事务传播行为【Propagation】
-
通俗理解就是当一个事务调用中调用的方法也是一个事务,这时候我们就可以设置事务的传播行为属性,有如下几种
-
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。
- 如:执行事务方法method()1【事务x】之后,调用事务方法method2()【事务y】,此时需要设置method()2方法的事务传播行为,即设置按照事务x执行还是按照事务y执行
-
Spring的7种传播行为,均是Propagation枚举类的属性:
传播属性 描述 REQUIRED **默认属性。**如果有事务在运行,当前的方法就在这个事务内运行;否则就启动一个新的事务,并在自己的事务内运行。即执行事务x REQUIRES_NEW 当前的方法必须启动新事务,并在自己的事务内运行;如果有事务正在运行,应该将它挂起。即执行事务y SUPPORTS 如果有事务在运行,当前的方法就在这个事务内运行,否则可以不运行在事务中。 NOT_SUPPORTED 当前的方法不应该运行在事务中,如果有运行的事务将它挂起 MANDATORY 当前的方法必须运行在事务中,如果没有正在运行的事务就抛出异常。 NEVER 当前的方法不应该运行在事务中,如果有正在运行的事务就抛出异常。 NESTED 如果有事务正在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则就启动一个新的事务,并在它自己的事务内运行。 -
代码示例
-
结账方法 checkout()
@Transactional @Override public void checkout(Integer[] bookIds, Integer userId) { for(Integer bookId : bookIds){ try { bookService.buyBook(bookId,userId); } catch (InterruptedException e) { e.printStackTrace(); } } }
-
购书方法 buyBook() 注意:该方法规定每次只能买一本
@Transactional( // propagation = Propagation.REQUIRED //这样使用的是调用该事务的事务,也就是当调用他的事务失败时, 此事务也会回滚 propagation = Propagation.REQUIRES_NEW //这样使用的是该事务本身的事务,就算调用他的父事务执行失 败,该事务也不会回滚 ) @Override public void buyBook(Integer bookId, Integer userId) { //查询图书的价格 Integer price = bookDao.getPriceByBookId(bookId); //更新图书的库存 bookDao.updateStock(bookId); //更新用户的余额 bookDao.updateBalance(userId, price); // int x = 1/0; }
-
-
-
事务隔离级别【Isolation】(详情请看jdbc关于事务的隔离级别的md笔记,数据库高级会讲)
-
隔离级别概述:一个事务与其他事务之间的隔离等级【1,2,4,8】
-
问题:脏读、不可重复读、幻读
-
隔离等级,Isolation枚举类的属性
- 读未提交【1】:READ_UNCOMMITTED
- 读已提交【2】:READ_COMMITTED
- 可重复读【4】:REPEATABLE_READ
- 串行化【8】:SERIALIZABLE
-
示例
@Transactional(isolation = Isolation.DEFAULT)//使用数据库默认的隔离级别 @Transactional(isolation = Isolation.READ_UNCOMMITTED)//读未提交 @Transactional(isolation = Isolation.READ_COMMITTED)//读已提交 @Transactional(isolation = Isolation.REPEATABLE_READ)//可重复读 @Transactional(isolation = Isolation.SERIALIZABLE)//串行化
-
-
事务超时【timeout】
- 设置事务的超时时间,达到指定时间后会强制事务回滚
- 类型:int;单位:秒
- 默认值:-1秒【未设置强制回滚时间】,也就是说,此时,Mybatis是不会去检测Spring事务是否超时的
-
事务只读【readOnly】
- 事务只有查询操作时,才能设置成只读
- 默认值:false
-
事务回滚【不回滚】:
- 默认所有异常都会回滚
- rollbackFor:设置需要回滚的异常;值为:异常类类名.class
- noRollbackFor:设置不需要回滚的异常;值为:异常类类名.class
-
示例
@Transactional( // readOnly = true, //该属性(默认为false)设置成true说明此事务只可读,如果出现增删改会报错 // timeout = 3 , //该属性(默认为-1)设置成3表示如果该事务三分钟没有执行完成,就会回滚 // noRollbackFor = {ArithmeticException.class},//该属性表示当出现以下异常时不回滚,注意这里是数组, //指明异常的类对象 // noRollbackForClassName = {"java.lang.ArithmeticException"}//该属性表示当出现以下异常时不回滚,指 明异常的全类名 ) @Override public void buyBook(Integer bookId, Integer userId) { // 让该事务休眠,TimeUnit.SECONDS说明失眠的时间单位是分钟 // try { // TimeUnit.SECONDS.sleep(5); // } catch (InterruptedException e) { // e.printStackTrace(); // } //查询图书的价格 Integer price = bookDao.getPriceByBookId(bookId); //更新图书的库存 bookDao.updateStock(bookId); //更新用户的余额 bookDao.updateBalance(userId, price); // int x = 1/0; }
19.4 基于XML方式,配置声明式事务管理(了解)
该部分可看b站新版课件
第二十章 Spring5新特性
Spring5框架基于java8,运行时兼容JDK9
20.1 新注解
20.2 Spring5整合Log4j2
-
导入jar包【并将Log4j的jar包删除】
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.11.2</version> <scope>test</scope> </dependency>
-
编写配置文件【log4j2.xml】
不需要自己配置,复制即可(网上找类似的)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P8FrMF1G-1690105233709)(03_Spring.assets\14.jpg)]
20.3 Spring5整合Junit5
-
导入jar包【并将Junit4的jar包删除】
<dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.7.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.3.1</version> <scope>test</scope> </dependency>
-
@ContextConfiguration(locations = “classpath:applicationContext.xml”) 还是用来指定 Spring 配置文件位置,和整合 junit4 一样。
-
@ExtendWith(SpringExtension.class) 表示使用 Spring 提供的扩展功能。
-
复合注解(将上面两个注解整合) :@SpringJUnitConfig(locations = “classpath:applicationContext.xml”)
-
测试示例
/** * @author Chunsheng Zhang 尚硅谷 * @create 2022/3/28 14:12 */ @ContextConfiguration(locations = "classpath:applicationContext.xml") @ExtendWith(SpringExtension.class) //方式二:@SpringJUnitConfig(locations = "classpath:applicationContext.xml") public class TestSpringJunit4 { @Autowired private DeptService deptService; @Test public void testService(){ //创建容器对象 // ApplicationContext context = // new ClassPathXmlApplicationContext("applicationContext.xml"); // DeptService deptService = context.getBean("deptService", DeptServiceImpl.class); deptService.saveDept(new Dept()); } }
第二十一章 Jar包总结
21.1 Spring
导入jar包,该包中含有spring-jcl包(日志包),也就是说不用向mybatis一样依赖第三方日志 log4j了
<!--导入spring-context,加上其依赖,共6个jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.1</version>
</dependency>
21.2 Junit4
-
maven工程下test目录就用到了到jar包
-
如何根据方法快速生成单元测试方法?用法观看 idea快捷键.docx 文档
<!--导入junit4.12 :一般情况要进行单元测试,只需加该包即可,下面那个包有其需求再加--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency>
-
spring-test的使用:观看第十四章
<!-- spring-test : 加入该依赖后,可实现 1> 不需要new容器对象,而通过注解来获取容器对象 2> 不需要getBean,而直接使用 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.3.1</version> <scope>test</scope> </dependency>
21.3 MySQL8.0.26
<!--导入mysql的jar包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!-- <version>5.1.37</version> -->
<version>8.0.26</version>
</dependency>
21.4 Druid
详细讲解:第五章
<!-- 前提:需加入MySQL的jar包-->
<!--导入mysql的jar包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!-- <version>5.1.37</version> -->
<version>8.0.26</version>
</dependency>
<!--导入druid的jar包-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
21.5 ApectJ
详细讲解:第十六、十七章
<!--spirng-aspects的jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.1</version>
</dependency>
21.6 JdbcTemplate
详细讲解:第十八章
<!--spring-context-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.1</version>
</dependency>
<!--导入mysql的jar包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version>
</dependency>
<!--导入druid的jar包-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!-- spring-orm:jdbcTemplate的jar包【前提有:spring的jar,MySQL的jar包,druid的jar包】-->
<!-- Spring 在执行持久化层操作、与持久化层技术进行整合过程中,需要使用orm、jdbc、tx三个jar包 -->
<!-- 导入 orm 包就可以通过 Maven 的依赖传递性把其他两个也导入 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.3.1</version>
</dependency>
21.7 Log4j2
详细讲解:20.2小节
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.11.2</version>
<scope>test</scope>
</dependency>
21.8 Junit5
详细讲解:20.3小节
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.1</version>
<scope>test</scope>
</dependency>
<artifactId>spring-test</artifactId>
<version>5.3.1</version>
<scope>test</scope>
```
21.3 MySQL8.0.26
<!--导入mysql的jar包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!-- <version>5.1.37</version> -->
<version>8.0.26</version>
</dependency>
21.4 Druid
详细讲解:第五章
<!-- 前提:需加入MySQL的jar包-->
<!--导入mysql的jar包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!-- <version>5.1.37</version> -->
<version>8.0.26</version>
</dependency>
<!--导入druid的jar包-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
21.5 ApectJ
详细讲解:第十六、十七章
<!--spirng-aspects的jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.1</version>
</dependency>
21.6 JdbcTemplate
详细讲解:第十八章
<!--spring-context-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.1</version>
</dependency>
<!--导入mysql的jar包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version>
</dependency>
<!--导入druid的jar包-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!-- spring-orm:jdbcTemplate的jar包【前提有:spring的jar,MySQL的jar包,druid的jar包】-->
<!-- Spring 在执行持久化层操作、与持久化层技术进行整合过程中,需要使用orm、jdbc、tx三个jar包 -->
<!-- 导入 orm 包就可以通过 Maven 的依赖传递性把其他两个也导入 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.3.1</version>
</dependency>
21.7 Log4j2
详细讲解:20.2小节
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.11.2</version>
<scope>test</scope>
</dependency>
21.8 Junit5
详细讲解:20.3小节
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.1</version>
<scope>test</scope>
</dependency>