开始记录时间:2021年6月16日20:29:02
一、Spring简介
Spring概述
SSM:Spring SpringMVC Mybatis
SSH: Spring Struct Hibernate
百度百科
Spring框架是一个[开放源代码的J2EE应用程序框架,由[Rod Johnson](https://baike.baidu.com/item/Rod Johnson/1423612)发起,是针对bean的生命周期进行管理的轻量级容器(lightweight container)。 Spring解决了开发者在J2EE开发中遇到的许多常见的问题,提供了功能强大IOC、AOP及Web MVC等功能。Spring可以单独应用于构筑应用程序,也可以和Struts、Webwork、Tapestry等众多Web框架组合使用,并且可以与 Swing等桌面应用程序AP组合。因此, Spring不仅仅能应用于J2EE应用程序之中,也可以应用于桌面应用程序以及小应用程序之中。Spring框架主要由七部分组成,分别是 Spring Core、 Spring AOP、 Spring ORM、 Spring DAO、Spring Context、 Spring Web和 Spring Web MVC。
Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。
要点:
1. Spring是针对与J2EE的框架
2. Spring是针对**bean的生命周期进行管理的轻量级容器**
3. 提供了IOC(控制反转)、AOP(面向切面编程)、WebMVC等功能
4. 可以单独应用于构件应用程序
5. 可以和Struts、Webwork、Tapestry等众多Web框架组合使用,并且可以与 Swing等[桌面应用程序](https://baike.baidu.com/item/桌面应用程序/2331979)AP组合。(可以应用于桌面应用程序以及小应用程序之中)
6. Spring框架主要由七部分组成
1. Spring Core
2. Spring AOP
3. Spring ORM
4. Spring DAO
5. Spring Context
6. Spring Web
7. Spring Web MVC
Spring是一个开源容器框架,它集成各类型的工具,通过核心的**Bean factory实现了底层的类的实例化和生命周期的管理。**
在整个框架中,各类型的功能被抽象成一个个的 Bean,这样就可以实现各种功能的管理,包括动态加载和切面编程。
可以从上图看出,Spring支撑所有的是SpringCore
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iwJ7o9rN-1625537948223)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210616165556498.png)]
MVC
MVC框架:M是指业务模型,V是指用户界面,C则是控制器,使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式。最典型的MVC就是JSP + servlet + javabean的模式。
- 视图:用户看到的界面
- 模型:模型表示企业数据和业务规则。在MVC的三个部件中,模型拥有最多的处理任务
- 控制器:控制器接受用户的输入并调用模型和视图去完成用户的需求,所以当单击Web页面中的超链接和发送HTML表单时,控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示返回的数据。
JavaBean
JavaBean:Java语言欠缺属性、事件、多重继承功能。所以,如果要在Java程序中实现一些面向对象编程的常见需求,只能手写大量胶水代码。Java Bean正是编写这套胶水代码的惯用模式或约定。这些约定包括getXxx、setXxx、isXxx、addXxxListener、XxxEvent等。遵守上述约定的类可以用于若干工具或库。
EJB
EJB:把你编写的软件中那些需要执行制定的任务的类,不放到客户端软件上了,而是给他打成包放到一个服务器上了"。是的,没错!EJB 就是将那些"类"放到一个服务器上,用C/S 形式的软件客户端对服务器上的"类"进行调用
EJB 的官方解释:
商务软件的核心部分是它的业务逻辑。业务逻辑抽象了整个商务过程的流程,并使用计算机语言将他们实现。
……
J2EE 对于这个问题的处理方法是将业务逻辑从客户端软件中抽取出来,封装在一个组件中。这个组件运行在一个独立的服务器上,客户端软件通过网络调用组件提供的服务以实现业务逻辑,而客户端软件的功能单纯到只负责发送调用请求和显示处理结果。在J2EE 中,这个运行在一个独立的服务器上,并封装了业务逻辑的组件就是EJB(Enterprise JavaBean)组件。博客文章:EJB到底是什么,真的那么神秘吗??
学习感悟:如果在学习看懂一些官方文档时就去收集一些别人写的博客用白话写的,然后再回去看官方的文章
SpringBoot与Spring Cloud
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nOHzcSzh-1625537948229)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210616170427288.png)]
二、IOC(控制反转)推导
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AD7MPNrc-1625537948231)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210616190539240.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8CUQDxx6-1625537948232)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210616190608177.png)]
代码演示
这是以前写项目的方式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z5ZiAdtW-1625537948233)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210616193119352.png)]
但是现在用户(也就是使用test来测试的)的需求改了它要使用的是MySQDao或Oracle的实现类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ejcbn5Qu-1625537948234)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210616193535724.png)]
此时我们就需要修改业务层的service
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C8wnqRHG-1625537948235)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210616193653741.png)]
如果用户又需要改则会出现很大的修改开销,但是如果此时我们让用户直接自己指定实现那个实现类则是一件很简单解决这个问题的方法
此时我们给service添加一个set方法,让用户直接来new 它需要的dao实行类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ehLw9Bsh-1625537948236)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210616194509500.png)]
测试类
package com.cctv.service;
import com.cctv.dao.impl.MySQLUserDaoImpl;
import com.cctv.service.impl.UserServiceImpl;
import org.junit.jupiter.api.Test;
public class UserServiceTest {
UserService userService = new UserServiceImpl();
@Test
public void testFindUser(){
userService.setUserDao(new MySQLUserDaoImpl());
userService.findUser();
}
}
运行结果
mysqlDao
进程已结束,退出代码为 0
再来查看一下service层(业务层)的代码:
以前的方式
package com.cctv.service.impl;
import com.cctv.dao.UserDao;
import com.cctv.dao.impl.UserDaoImpl;
import com.cctv.service.UserService;
public class UserServiceImpl implements UserService {
UserDao userDao = new UserDaoImpl();
@Override
public void findUser() {
userDao.findUser();
}
}
业务层具有调用dao的主动性
修改过后的方式
package com.cctv.service.impl;
import com.cctv.dao.UserDao;
import com.cctv.service.UserService;
public class UserServiceImpl implements UserService {
UserDao userDao;
@Override
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void findUser() {
userDao.findUser();
}
}
此时业务层则形成了 被动 的来实现业务的调用
就是使用了一个set方法来注入用户想实现的类对象
这就是控制反转,其实可以从字面看出一些端倪控制权反转交给用户,不再由程序的service层来决定
耦合性也大大的降低了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wx5a5W9e-1625537948236)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210616202218225.png)]
耦合性
耦合性(英语:Coupling,dependency,或称耦合力或耦合度)是一种软件度量,是指一程序中,模块及模块之间信息或参数依赖的程度。
内聚性是一个和耦合性相对的概念,一般而言低耦合性代表高内聚性,反之亦然。耦合性和内聚性都是由提出结构化设计概念的赖瑞·康斯坦丁所提出。低耦合性是结构良好程序的特性,低耦合性程序的可读性及可维护性会比较好。
紧密耦合的系统在开发阶段有以下的缺点:
- 一个模块的修改会产生涟漪效应,其他模块也需随之修改。
- 由于模块之间的相依性,模块的组合会需要更多的精力及时间。
- 由于一个模块有许多的相依模块,模块的可复用性低。
改善方法:
机能设计是一种可以降低耦合性的方法,此方法以机能性的角度设法限制各模块需负责的事务。在类别***A***及***B***之间,若有以下任何一种情形,会提高二者的耦合性:
- A***有一个属性是参考类别***B(此属性的形态为类别***B***)
- ***A***调用对象***B***提供的服务
- A***有一个方法会参考类别***B(此方式会传回一形态为类别***B***的物性)
- ***A***是类别***B***的子类。
松散耦合是指二个彼此相关的模块,其中的接口是一个简单而稳定的接口,且其接口和任一模块内部的实现方式无关(参考信息隐藏)。
像CORBA或组件对象模型等系统,允许一对象在不知道另一对象实现方式的情形下和另一对象交互。这类系统甚至允许一对象和用其他语言撰写的对象进行交互。
Spring与IOC
IOC的本质
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。
Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。
理解IoC思想是理解Spring的基础
代码实现
实体类
package com.cctv.pojo; public class Hello { private String str; public String getStr() { return str; } public void setStr(String str) { this.str = str; } @Override public String toString() { return "Hello{" + "str='" + str + '\'' + '}'; } }
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"> <!-- 使用Spring来创建对象(在Spring中这些都叫Bean) --> <bean id="hello" class="com.cctv.pojo.Hello"> <property name="str" value="Spring"/> </bean> </beans>
测试类
package com.cctv; import com.cctv.pojo.Hello; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class HelloTest { @Test public void testHello(){ //获取Spring上下文对象 ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); //此时的context就是一个Spring容器,你现在可以从容器中取出对象 final Hello hello = (Hello) context.getBean("hello"); System.out.println(hello.toString()); System.out.println(hello.getStr()); } }
执行结果
Hello{str='Spring'}Spring进程已结束,退出代码为 0
上下文:自己的理解,就是某段代码在运行过程中跟自己有联系的所有代码。这里还有一层意思,这段代码前面的代码或后面的代码来进行推断
可以发现在此次测试中没有new Hello而是使用了Spring的配置文件来代理(ApplicationContext context = new ClassPathXmlApplicationContext(“beans.xml”);)
下图是该实现的类结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8GoAN4i3-1625537948240)(C:\Users\你好\Desktop\image-20210617093406907.png)]
分析:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xsLHXPZH-1625537948241)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210617091832566.png)]
所以在Spring中的IoC就是对象由Spring来创建、管理和装配。
例子2:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZwxQI8Su-1625537948242)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210617123101897.png)]
测试类:
package com.cctv.service;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserServiceTest {
@Test
public void testFindUser(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//现在使用的是MySQL
final UserService userService1 = (UserService)context.getBean("userService1");
userService1.findUser();
}
}
输出结果:
mysqlDao进程已结束,退出代码为 0
现在想要使用Oracle,则直接修改配置文件即可[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mynTvmnr-1625537948243)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210617123431081.png)]
OracleUserDao进程已结束,退出代码为 0
三、IoC创建对象
1、使用无参创建对象,这个是Spring使用的默认实现方式。(假设现在没有无参构造函数,则需要用到“2”)
<bean id="hello" class="com.cctv.pojo.Hello">
<property name="str" value="Spring"/>
</bean>
2、使用有参创建对象
①、通过下标的方式给其构造函数赋值
<bean id="showUser" class="com.cctv.pojo.User">
<constructor-arg index="0" value="张三"/>
</bean>
②、通过参数的类型来进行赋值
<bean id="showUser" class="com.cctv.pojo.User">
<constructor-arg type="java.lang.String" value="张三"/>
</bean>
③、通过参数名称赋值
<bean id="showUser" class="com.cctv.pojo.User">
<constructor-arg name="name" value="张三"/>
</bean>
Spring容器,为什么叫“容器”是因为你在创建bean时它就已经将实体类的对象创建好了,只是你创建bean时它会从里面拿出相应的对象,然后根据你决定要赋的值进行给对象赋值,注意它的内存中只有一份实例对象,它取的都是一份实例对象
四、Spring的其他配置
别名
在Spring的bean中可以给一个创建的bean创建别名,此时就可以使用此bean的id或者别名调用该对象
第一种
<bean id="showUser" class="com.cctv.pojo.User">
<constructor-arg name="name" value="张三"/>
</bean>
<alias name="showUser" alias="kanUser"/>
第二种
<bean id="showUser" class="com.cctv.pojo.User" name="kanUser,showUU">
<constructor-arg name="name" value="张三"/>
</bean>
使用name就可以创建别名,并且还可以创建多个别名(多个别名使用逗号隔开或者用空格分隔或者分号等)
import
<import resource="beans1.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>
此时如果多个配置文件中有bean的id是一样的则别名就起到了很好的作用
五、依赖注入(DI)
依赖注入
DI 存在两个主要变体:基于 构造函数的依赖注入 和基于 Setter 的依赖注入。
- 依赖:在Spring中所有的bean都依赖于容器
- 注入:bean中所有的属性都依赖于Spring中的容器来注入
构造器注入
在IoC创建对象时已经说过了
通过set方式注入
<bean id="address" class="com.cctv.pojo.Address">
<property name="address" value="贵州"/>
</bean>
<bean id="student" class="com.cctv.pojo.Student">
<!-- 通过属性的方式进行注入(采用的是SetXxx进行的注入),普通值注入 -->
<property name="name" value="秦昊"/>
<!-- 通过属性的方式进行注入(采用的是SetXxx进行的注入),bean(对象)的注入 -->
<property name="address" ref="address"/>
<!-- 通过属性的方式进行注入(采用的是SetXxx进行的注入),数组的注入 -->
<property name="books">
<array>
<value>我是特种兵</value>
<value>我的团长我的团</value>
<value>我们的父辈</value>
</array>
</property>
<!-- 通过属性的方式进行注入(采用的是SetXxx进行的注入),List集合的注入 -->
<property name="hobbys">
<list>
<value>篮球</value>
<value>吉他</value>
<value>自行车</value>
</list>
</property>
<!-- 通过属性的方式进行注入(采用的是SetXxx进行的注入),Map集合的注入 -->
<property name="card">
<map>
<entry key="20190132028" value="秦昊"/>
</map>
</property>
<!-- 通过属性的方式进行注入(采用的是SetXxx进行的注入),Set集合的注入 -->
<property name="games">
<set>
<value>俄罗斯方块</value>
<value>植物大战僵尸</value>
<value>坦克大战</value>
</set>
</property>
<!-- 通过属性的方式进行注入(采用的是SetXxx进行的注入),String的注入 -->
<property name="wife">
<null/>
</property>
<!-- 通过属性的方式进行注入(采用的是SetXxx进行的注入),Properties的注入 -->
<property name="info">
<props>
<prop key="时间">2021年6月19日14:19:13</prop>
</props>
</property>
</bean>
拓展方式注入
p命名空间注入
导入约束
xmlns:p="http://www.springframework.org/schema/p"
<!-- p注入,只能注入普通的属性 -->
<bean id="user" class="com.cctv.pojo.User" p:name="张三" p:age="19"/>
c命名空间注入
注意注入方式是通过的构造方法来进行注入,所以必须要有一个有参构造函数
导入约束
xmlns:c="http://www.springframework.org/schema/c"
<!-- c注入,必须要有有参构造函数 -->
<bean id="user1" class="com.cctv.pojo.User" c:name="李四" c:age="20"/>
Map 键或值的值或设置值也可以是以下任意元素:
bean | ref | idref | list | set | map | props | value | null
六、bean的作用域
Scope | Description |
---|---|
singleton | (默认)将每个 Spring IoC 容器的单个 bean 定义范围限定为单个对象实例。 |
prototype | 将单个 bean 定义的作用域限定为任意数量的对象实例。 |
request | 将单个 bean 定义的范围限定为单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有一个在单个 bean 定义后面创建的 bean 实例。仅在可感知网络的 Spring ApplicationContext 中有效。 |
session | 将单个 bean 定义的范围限定为 HTTP Session 的生命周期。仅在可感知网络的 Spring ApplicationContext 上下文中有效。 |
application | 将单个 bean 定义的范围限定为ServletContext 的生命周期。仅在可感知网络的 Spring ApplicationContext 上下文中有效。 |
websocket | 将单个 bean 定义的范围限定为WebSocket 的生命周期。仅在可感知网络的 Spring ApplicationContext 上下文中有效。 |
单例模式
多个bean对应一个对象
它是Spring的默认模式
<bean id="user" class="com.cctv.pojo.User" p:name="张三" p:age="19" scope="singleton"/>
scope=singleton:单例模式
原型模式
一个bean对应一个对象
<bean id="user" class="com.cctv.pojo.User" p:name="张三" p:age="19" scope="prototype"/>
scope=“prototype”:原型模式
还有很多是在web开发中使用的,后面学MVC时学
七、Bean的自动装配
-
自动装配:它是Spring满足bean依赖的一种方式
-
Spring会在上下文中自动寻找,并自动给bean装配属性
-
在Spring中有三种装配方式:
-
在xml中显示的配置(前面讲过了)
-
在java中显示的配置(后面讲)
-
隐式的自动装配bean
-
例子:
这是原来的显示装配
<bean id="cat" class="com.cctv.pojo.Cat"/>
<bean id="dog" class="com.cctv.pojo.Dog"/>
<bean id="user" class="com.cctv.pojo.User">
<property name="name" value="张三"/>
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
</bean>
byName装配
<bean id="cat" class="com.cctv.pojo.Cat"/>
<bean id="dog" class="com.cctv.pojo.Dog"/>
<bean id="user" class="com.cctv.pojo.User" autowire="byName">
<property name="name" value="张三"/>
</bean>
autowire=“byNme”:它会根据上下文来进行判断进行自动装配
但是需要注意:它的装配方式要满足的条件
此种装配模式是根据该bean的setXxx方法后面的“Xxx”来进行判断的它会变成“xxx”所以需要注入的bean的id必须是符合“xxx”的形式的(根据id来进行判断)
byType装配
<bean id="cat222" class="com.cctv.pojo.Cat"/>
<bean id="dog222" class="com.cctv.pojo.Dog"/>
<bean id="user" class="com.cctv.pojo.User" autowire="byType">
<property name="name" value="张三"/>
</bean>
可以发现该种装配方式不是通过“Xxx”的形式来装配的,它是通过自己属性的类型来进行判断的。
所以需要注意:不能出现两个同样类型的bean,否则报错(根据class来进行判断)
使用注解实现自动装配
注解 在声明中提供了很多上下文,从而使配置更短,更简洁。
注解注入在 XML 注入之前执行。因此,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
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 这个就是需要添加的注解配置 -->
<context:annotation-config/>
</beans>
它的约束:xmlns:context=“http://www.springframework.org/schema/context”
它的注解配置: context:annotation-config/
@Autowired
例子:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 导入注解配置 -->
<context:annotation-config/>
<bean id="cat" class="com.cctv.pojo.Cat"/>
<bean id="dog" class="com.cctv.pojo.Dog"/>
<bean id="user" class="com.cctv.pojo.User"/>
</beans>
public class User {
private String name;
@Autowired
private Cat cat;
@Autowired
private Dog dog;
public String getName() {
return name;
}
………………
也可以@Autowired放在set方法上,如果在属性上使用可以不用写setXxx方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MuSXazqL-1625537948247)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210619170456576.png)]
@Resource
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UMt52NmH-1625537948248)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210619171207003.png)]
小结
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vB2xrgbY-1625537948250)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210619171220579.png)]
八、使用注解开发
使用注解开发需要导入下列标红的包
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-52wnLvox-1625537948251)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210619172557277.png)]
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--
这个标签是用来加载文件的
在这里如果使用注解开发就需要将类的包写在上面
-->
<context:component-scan base-package="com.cctv.pojo"/>
<context:annotation-config/>
</beans>
package com.cctv.pojo;
import org.springframework.stereotype.Component;
@Component
public class User {
public String name = "张希";
}
如上是直接将name的属性值赋值给name的;如果需要动态的使用注解则可以使用@Value来实现,但是它最多用于简单的注入
package com.cctv.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class User {
@Value("张希")
public String name;
}
衍生的注解
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t6KzJxNA-1625537948252)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210619175815475.png)]
基本概念:@Bean 和@Configuration
Spring 的新 Java 配置支持中的主要工件是@Configuration
注解 的类和@Bean
注解 的方法。
@Bean
注解 用于指示方法实例化,配置和初始化要由 Spring IoC 容器 Management 的新对象。对于熟悉 Spring 的<beans/>
XML 配置的人来说,@Bean
注解 与<bean/>
元素具有相同的作用。您可以对任何 Spring @Component
使用@Bean
注解 的方法。但是,它们最常与@Configuration
bean 一起使用。
用@Configuration
注解 类表示该类的主要目的是作为 Bean 定义的来源。此外,@Configuration
类通过调用同一类中的其他@Bean
方法来定义 Bean 间的依赖关系。最简单的@Configuration
类的内容如下:
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
上面的AppConfig
类等效于下面的 Spring <beans/>
XML:
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>
九、代理模式
代理模式:其实在前面学的时候学过,就是你要达到某一目的但是你是通过的“中介”来实现的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8OBpWQXi-1625537948252)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210622202844039.png)]
我的理解就是本来只有两方,现在出现了一个第三方,两方都需要通过第三方来完成相对的目的,因为第三方更加擅长这件事
注意接口思想也是约束这件事一定一定要记住,一般在完成某件事的时候(也有可能是两件有联系的事)就需要用到接口来规范思想,约束你的实体类
后面要学的AOP就是利用了代理模式的思想
狂神说up主提出了一个纵向开发和横向开发很好的解释了这一思想,其实也有点像说的先长高再长胖的思想,就是你先不断的纵向开发,如果在后期需要增加一些功能你能去原有的代码只是在原有的代码上重新添加代码即可
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-44GjmkxU-1625537948253)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210623084924427.png)]
静态代理与动态代理
静态代理其实需要和动态代理同时理解(相对理解)
如果现在去电脑店买电脑,现在这个店只有一种配置的电脑(一体机),这就是静态代理
现在电脑店不进一体机(只有一种配置),它进了电脑的配件,电脑可以随意的组装,这就是动态代理(也就是你要拿到原本的配件),在Java中可以使用反射来拿到“java对象的配件”
现在首要的问题就是如何拿到它的“配件”
动态
动态代理有两种方式:
- 基于接口的动态代理
- 基于类的动态代理
-
基于接口动态的代理实现方式使用JDK源码的形式(这里使用这种)
-
基于类的动态的代理实现方式使用cglib的形式
-
或者基于java的字节码来实现JAVAssist来实现
- Javassist是一个开源的分析、编辑和创建Java字节码的类库
需要了解一个类Proxy和一个接口InvocationHandler
InvocationHandler接口
翻译:InvocationHandler(调用处理程序)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UXZpLlB6-1625537948255)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210623094803021.png)]
逐字分析:
1. 代理实例
2. 关联
3. 调用处理程序
4. invoke()方法(invoke()方法就是通过反射去执行一个方法,它是属于Method类里面的方法,注意Method是反射中一个重要的类)
方法摘要 | |
---|---|
Object | invoke(Object proxy, Method method, Object[] args) 在代理实例上处理方法调用并返回结果。 |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W7maFS2p-1625537948256)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210623095314161.png)]
参数分析:
1. proxy:就是一个代理类
2. method:该代理类的方法(它使用的是Method类的对象)
3. args:就是该代理类的方法的参数
Proxy类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PjZngnfi-1625537948257)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210623103625173.png)]
逐字分析:
1. 创建
2. 动态代理类
3. 该动态代理类的实例的静态方法
4. 所有动态代理类的超类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VemKbKMc-1625537948258)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210623103826103.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P78ZcaY6-1625537948259)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210623103858033.png)]
首先要明白InvocationHandler接口与Proxy类的作用:
Proxy:它的作用是在程序执行的时候生成代理类
invocationHandler:它来执行Proxy生成的代理类里面的方法
反射
反射:通过对象来获取类结构(类的完整包名)
需要注意Java中的Class类。平时我们说的类比如自己写的类,它通常代表的是一类事物。而此时的Class也就是官方定义的一个Class类它是用来代表这世间所有的类的一个类,当然此类在反映所有“事物类”的时候它是被弄成了对象的,因为世间的所有的“事物类”就只有一个类型Class,所以某一个事物类只有一个Class对象
有些人说对象就是类的镜子,通过对象来反映类就叫做反射
反射的作用:
1. java反射,就是在运行状态中
1. 获取任意类的名称,
2. 包名信息,
3. 属性,方法,
4. 注解,
5. 类型,
6. 参数类型,
7. 类加载器,
8. 修饰符,
9. 父类,
10. 实现接口等
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p7BbjfSq-1625537948260)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210623210853068.png)]
在框架中,动态代理推动了配置文件的使用
十、AOP(面向切面编程)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v5fTpiYt-1625537948260)(file://C:\Users%E4%BD%A0%E5%A5%BD\AppData\Roaming\Typora\typora-user-images\image-20210623084924427.png?lastModify=1625219703)]
AOP:通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术
特点:
-
AOP(面向切面)是OOP(面向对象)的延续,
是软件开发中的一个热点 -
是Spring框架中的一个重要内容
-
是函数式编程的一种衍生范型
-
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
主要功能:
日志记录,性能统计,安全控制,事务处理,异常处理等等
主要意图:
将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
spring中使用AOP
第一种方式(使用AOP中的API来实现)
导包
重点:aop织入包aspectjweaver
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3Nfrud2F-1625537948261)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210702190341697.png)]
AOP思想包:就是AOP织入包
写日志类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rzZzaXX7-1625537948262)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210702191459568.png)]
编辑配置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yGMdts2P-1625537948263)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210702194345807.png)]
测试类
package test;
import com.cctv.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test01(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
final UserService userService = (UserService) context.getBean("userService");
userService.add();
}
}
运行结果
F:\java_SE\jdk_13.0.2\bin\java.exe ...
在Log的before方法add,参数为:[Ljava.lang.Object;@5c7933ad,执行对象为:com.cctv.service.impl.UserServiceImpl@57bc27f5
添加一个用户
执行了add方法,返回结果为:null
进程已结束,退出代码为 0
Aspectj切入点语法定义(execution)
在使用spring框架配置AOP的时候,不管是通过XML配置文件还是注解的方式都需要定义pointcut”切入点”
例如定义切入点表达式 execution (* com.sample.service.impl…*. *(…))
execution()是最常用的切点函数,其语法如下所示:
整个表达式可以分为五个部分:
1、execution(): 表达式主体。
2、第一个*号:表示返回类型, *号表示所有的类型。
3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。
4、第二个*号:表示类名,*号表示所有的类。
5、*(…):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数
第二种方式(自定义3、类来实现AOP)
导包和前面一样
自定义类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rxj1Gr46-1625537948264)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210702195557102.png)]
配置文件
其他配置和前面一样
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H3uJmNB4-1625537948265)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210702200242972.png)]
测试类
package test;
import com.cctv.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test01(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
final UserService userService = (UserService) context.getBean("userService");
userService.add();
}
}
运行结果
F:\java_SE\jdk_13.0.2\bin\java.exe ...
----->前,执行了<-----
添加一个用户
----->后,执行了<-----
进程已结束,退出代码为 0
第三种方式(使用注解实现)
导包和前面一样
注解类定义
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MdFUuBoi-1625537948267)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210702202320520.png)]
配置文件
其他配置和前面一样
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n5x5tZdU-1625537948268)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210702202058338.png)]
测试类
package com.cctv.diy;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect // 标注该类是一个切面
public class AnnotationPointCut {
@Before("execution(* com.cctv.service.impl.UserServiceImpl.*(..))")
public void before(){
System.out.println("=====>前,before方法执行了<=====");
}
@After("execution(* com.cctv.service.impl.UserServiceImpl.*(..))")
public void after(){
System.out.println("=====>后,after方法执行了<=====");
}
}
运行结果
F:\java_SE\jdk_13.0.2\bin\java.exe ...
=====>前,before方法执行了<=====
添加一个用户
=====>后,after方法执行了<=====
进程已结束,退出代码为 0
总结AOP:以我现在的理解。在spring中AOP的思想就是,spring掌控了某段“逻辑代码”的执行“时间”它可以让某段代码在“逻辑代码”的前面或后面执行,所以说spring掌控了“逻辑代码”的执行事件以及顺序
十一、Spring与Mybatis整合
导包:
1. aop织入包
2. Mybatis包
3. MySQL包
4. spring相关的包
5. spring-mybatis包(用于Sprig与Mybatis的整合)
6. 单元测试包
<?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">
<parent>
<artifactId>Spring-study</artifactId>
<groupId>com.cctv</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-09-mybatis和spring</artifactId>
<properties>
<maven.compiler.source>13</maven.compiler.source>
<maven.compiler.target>13</maven.compiler.target>
</properties>
<dependencies>
<!-- 单元测试包 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- mybatis包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!-- mysql包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
<!-- spring-webmvc包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.8</version>
</dependency>
<!-- spring操作数据库需要导入一个 spring-jdbc包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.3.RELEASE</version>
</dependency>
<!-- spring的aop的织入包 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<!-- Spring与Mybatis的整合包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
</dependencies>
</project>
Mybatis的配置
前面已经讲过Mybatis这里就不再详细的赘述
在pom.xml中引入Mybatis的包以后
编写Mybatis核心配置文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bfdIoU56-1625537948269)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210703124803083.png)]
编写mapper配置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tyQOusaI-1625537948270)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210703124317430.png)]
spring-mybatis
什么是 MyBatis-Spring?
MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。它将允许 MyBatis 参与到 Spring 的事务管理之中,创建映射器 mapper 和 SqlSession
并注入到 bean 中,以及将 Mybatis 的异常转换为 Spring 的 DataAccessException
。 最终,可以做到应用代码不依赖于 MyBatis,Spring 或 MyBatis-Spring。
mybatis-spring、mybatis、springFramework、SpringBatch、jdk各个版本之间的兼容性问题
MyBatis-Spring | MyBatis | Spring Framework | Spring Batch | Java |
---|---|---|---|---|
2.0 | 3.5+ | 5.0+ | 4.0+ | Java 8+ |
1.3 | 3.4+ | 3.2.2+ | 2.1+ | Java 6+ |
spring整合mybatis的配置
<?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">
<!-- 建立一个与mybatis核心配置一样的bean -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
</bean>
<!-- 创建一个DataSource(数据源) -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 绑定mybatis的核心配置文件(可以不用配置) -->
<property name="configuration" value="classpath:mybatis-config.xml"/>
</bean>
</beans>
spring中整合mybatis后与原有mybatis的配置的对比
关于spring重点导入的包
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8mxsUxgI-1625537948271)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210703133021274.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BUYdT5Cn-1625537948273)(C:\Users\你好\Desktop\01.png)]
练习(第一种方式):
导入jar包
<?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">
<parent>
<artifactId>Spring-study</artifactId>
<groupId>com.cctv</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-09-mybatis和spring</artifactId>
<properties>
<maven.compiler.source>13</maven.compiler.source>
<maven.compiler.target>13</maven.compiler.target>
</properties>
<dependencies>
<!-- 单元测试包 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- mybatis包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!-- mysql包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
<!-- spring-webmvc包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.8</version>
</dependency>
<!-- spring操作数据库需要导入一个 spring-jdbc包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.3.RELEASE</version>
</dependency>
<!-- spring的aop的织入包 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<!-- Spring与Mybatis的整合包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<!-- 导入LomBok包,就是不用写getXxx和setXxx方法的包 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
</dependencies>
</project>
编写实体类
package com.cctv.domain;
public class User {
private Integer id;
private String name;
private String password;
public long getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
编写mapper接口
package com.cctv.mapper;
import com.cctv.domain.User;
import java.util.List;
public interface UserMapper {
/**
* 查询所有用户
* @return
*/
public List<User> findUsers();
/**
* 查询某个用户
* @return
*/
public User findUser(Integer id);
编写mybatis核心配置文件
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- environments(环境)数据库环境配置 -->
<!-- 和Spring整合后environments配置将被废除 -->
<!--<environments default="development">
<environment id="development">
<!– 使用JDBC事务管理 –>
<transactionManager type="JDBC"/>
<!– 数据库连接池 –>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="com.cctv.mapper.UserMapper"/>
</mappers>-->
</configuration>
编写mybatis的mapper配置文件
com/cctv/mapper/UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cctv.mapper.UserMapper">
<select id="findUsers" resultType="com.cctv.domain.User">
select * from user
</select>
<select id="findUser" parameterType="int" resultType="com.cctv.domain.User">
select * from user where id = #{id}
</select>
</mapper>
编写spring整合mybatis的配置文件
spring-dao.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">
<!-- 建立一个与mybatis核心配置一样的bean -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
</bean>
<!-- 创建一个DataSource(数据源) -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 绑定mybatis的核心配置文件(可以不用配置) -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!-- 绑定mybatis的mapper配置文件 -->
<property name="mapperLocations" value="classpath:com/cctv/mapper/UserMapper.xml"/>
</bean>
<!-- 创建一个sqlSession -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!-- 只能使用构造方法注入,因为SqlSessionTemplate没有setXxx方法-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
</beans>
创建一个给Spring与mybatis连接的类
package com.cctv.mapper.impl;
import com.cctv.domain.User;
import com.cctv.mapper.UserMapper;
import org.mybatis.spring.SqlSessionTemplate;
import java.util.List;
public class UserMapperImpl implements UserMapper {
SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public List<User> findUsers() {
//重点理解这个位置,这里的Mapper其实还是属于mybatis但是sqlSession是由spring-mybatis来创建的
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//所以这里的UserMapperImpl类其实还是调用了mybatis创建的mapper
return userMapper.findUsers();
}
@Override
public User findUser(Integer id) {
return null;
}
}
重点注意这里为什么要创建一个实体类,因为Spring它创建对象需要一个实体类,mybatis创建一个Mapper只需要一个接口,所以这里用–实体类去调用mapper-- 这样就可以达到使用Spring与mybatis的整合
spring各个配置文件的整合与创建一个userMapper的bean
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">
<import resource="spring-dao.xml"/>
<!-- 创建一个userMapper -->
<bean id="userMapper" class="com.cctv.mapper.impl.UserMapperImpl">
<!-- 注意这里执行了注入操作 -->
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>
测试类
package test;
import com.cctv.domain.User;
import com.cctv.mapper.UserMapper;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
public class MyTest {
@Test
public void test01(){
final ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
final UserMapper userMapper = context.getBean("userMapper",UserMapper.class);
final List<User> users = userMapper.findUsers();
for (User user : users) {
System.out.println(user);
}
}
}
测试结果:
F:\java_SE\jdk_13.0.2\bin\java.exe ...
7月 03, 2021 2:35:40 下午 org.springframework.jdbc.datasource.DriverManagerDataSource setDriverClassName
信息: Loaded JDBC driver: com.mysql.jdbc.Driver
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.apache.ibatis.reflection.Reflector (file:/D:/Maven_repository/maven_repository/org/mybatis/mybatis/3.4.5/mybatis-3.4.5.jar) to method java.lang.Object.finalize()
WARNING: Please consider reporting this to the maintainers of org.apache.ibatis.reflection.Reflector
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
User{id=1, name='张三', password='123'}
User{id=2, name='李四', password='456'}
User{id=3, name='老李', password='89564'}
User{id=4, name='老李头', password='46513'}
User{id=5, name='李三儿', password='9456'}
User{id=99, name='王eee子', password='789'}
进程已结束,退出代码为 0
练习(第二种方式)
前面我们需要使用spring的注入思想需要自己来实现,来进行一个SqlSessionTemplate的注入。但是现在有第二种方式,就是dao的实体类继承一个SqlSessionTemplate类,它直接帮你写好了注入的方法,直接进行注入即可
其他的和前面一样所以这里就不再赘述
创建一个给Spring与Mybatis“连接”的类
注意这里它继承了SqlSessionDaoSupport这个类
这种方式就是SqlSessionDaoSupport这个类直接帮你把setXxx的方法封装了,然后你可以直接调用就行
package com.cctv.mapper.impl;
import com.cctv.domain.User;
import com.cctv.mapper.UserMapper;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
@Override
public List<User> findUsers() {
return getSqlSession().getMapper(UserMapper.class).findUsers();
}
@Override
public User findUser(Integer id) {
return getSqlSession().getMapper(UserMapper.class).findUser(id);
}
}
编写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">
<import resource="spring-dao.xml"/>
<!-- 创建一个userMapper -->
<bean id="userMapper" class="com.cctv.mapper.impl.UserMapperImpl">
<!-- 注意这里的注入:这里注入是自己写的setXxx方法注入的sqlSessionTemplate(这里为了理解写的sqlSession)注入的 -->
<property name="sqlSession" ref="sqlSessionTemplate"/>
</bean>
<!-- 创建一个userMapper2,它才是这里的重点,前面的userMapper没有删是为了理解 -->
<bean id="userMapper2" class="com.cctv.mapper.impl.UserMapperImpl2">
<!-- 注意这里的注入:这里它不是我们自己写的setXxx方法,而是我们UserMapperImpl2类继承了SqlSessionDaoSupport它写的 -->
<property name="sqlSessionTemplate" ref="sqlSessionTemplate"/>
</bean>
</beans>
测试类
package test;
import com.cctv.domain.User;
import com.cctv.mapper.UserMapper;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
public class MyTest {
@Test
public void test01(){
final ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
final UserMapper userMapper = context.getBean("userMapper2",UserMapper.class);
final List<User> users = userMapper.findUsers();
for (User user : users) {
System.out.println(user);
}
}
}
测试结果:
F:\java_SE\jdk_13.0.2\bin\java.exe ...
7月 03, 2021 3:51:51 下午 org.springframework.jdbc.datasource.DriverManagerDataSource setDriverClassName
信息: Loaded JDBC driver: com.mysql.jdbc.Driver
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.apache.ibatis.reflection.Reflector (file:/D:/Maven_repository/maven_repository/org/mybatis/mybatis/3.4.5/mybatis-3.4.5.jar) to method java.lang.Object.finalize()
WARNING: Please consider reporting this to the maintainers of org.apache.ibatis.reflection.Reflector
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
User{id=1, name='张三', password='123'}
User{id=2, name='李四', password='456'}
User{id=3, name='老李', password='89564'}
User{id=4, name='老李头', password='46513'}
User{id=5, name='李三儿', password='9456'}
User{id=99, name='王eee子', password='789'}
总结步骤:
- 写一个实体类(User.java)
- 写一个实体类的Mapper接口(UserMapper.java)
- 编写mybatis的核心配置文件(mybatis-config.xml,这一步可以省略,但是为了后期可以给mybatis补一些东西还是可以写上),可以省略的是它连接数据库的配置,这些都交给spring的配置文件spring-dao.xml来做了
- 写一个mybatis对于Mapper接口的Mapper配置文件(UserMapper.xml)
- 编写一个Spring整合Mybatis的核心配置文件(spring-dao.xml)
- 写一个实体类它是用于spring和mybatis的“连接”的它实现了UserMapper接口。并且它还可以继承SqlSessionDaoSupport类就不用写setXxx方法来注入sqlSession了。(UserMapperImpl.java)
- UserMapperImpl的继承与实现关系(UserMapperImpl extends SqlSessionDaoSupport implements UserMapper)
- 写一个用于整合所有Spring的配置文件的spring配置文件(applicationContext.xml)
- 编写测试类测试
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uqa0uB4v-1625537948273)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210703163458581.png)]
注意这里的UserMapperImpl它从存在,它可以说就像是衣服的“左拉链”一样,它属于spring的用来创建对象的,然后UserMapperImpl又调用Mybatis创建的mapper里面的方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5MNdtK6P-1625537948274)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210703164242201.png)]
和以前在测试类中获取mapper时差不多只是这里全使用了spring里面的类来获取
十二、声明式事务
事务:多业务之间做交互,要么都成功,要么都失败。
例如:两个人转账,数据库之间的增减货币量必须一起成功或失败
事务的ACID
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cHMikIch-1625537948275)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210703165056335.png)]
spring与mybatis整合的事务
一个使用 MyBatis-Spring 的其中一个主要原因是它允许 MyBatis 参与到 Spring 的事务管理中。而不是给 MyBatis 创建一个新的专用事务管理器,MyBatis-Spring 借助了 Spring 中的 DataSourceTransactionManager
来实现事务管理。
一旦配置好了 Spring 的事务管理器,你就可以在 Spring 中按你平时的方式来配置事务。并且支持 @Transactional
注解和 AOP 风格的配置。在事务处理期间,一个单独的 SqlSession
对象将会被创建和使用。当事务完成时,这个 session 会以合适的方式提交或回滚。
事务配置好了以后,MyBatis-Spring 将会透明地管理事务。这样在你的 DAO 类中就不需要额外的代码了。
Spring中的事务管理有两种:
1. 声明式事务:AOP实现(常用,重点)
2. 编程式事务:需要代码进行事物管理
AOP实现声明式事务代码
写一个实体类
package com.cctv.domain;
public class User {
private Integer id;
private String name;
private String password;
public User() {
}
public User(Integer id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}
public long getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
写一个mapper接口
package com.cctv.mapper;
import com.cctv.domain.User;
import java.util.List;
public interface UserMapper {
/**
* 查询所有用户
* @return
*/
public List<User> findUsers();
/**
* 查询某个用户
* @return
*/
public User findUser(Integer id);
/**
* 修改某个用户
* @param id
*/
public void updateUser(Integer id,User user);
/**
* 增加一个用户
* @param user
*/
public void addUser(User user);
/**
* 删除某个用户
* @param id
*/
public void deleteUser(Integer id);
}
mybatis核心配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 因为没有任何的作用其实可以不写,但是为了习惯后期还是写好 -->
</configuration>
写一个mapper的mybatis配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cctv.mapper.UserMapper">
<select id="findUsers" resultType="com.cctv.domain.User">
select * from user
</select>
<select id="findUser" parameterType="int" resultType="com.cctv.domain.User">
select * from user where id = #{id}
</select>
<insert id="addUser" parameterType="com.cctv.domain.User">
insert into mybatis.user (id, name, password)
values (#{id},#{name},#{password});
</insert>
<delete id="deleteUser" parameterType="int">
<!-- “delt”没有写完整故意让它出错 -->
delet from mybatis.user where id = #{id}
</delete>
</mapper>
编写spring整合mybatis并添加了事务配置的配置文件
<?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"
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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 建立一个与mybatis核心配置一样的bean -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
</bean>
<!-- 创建一个DataSource(数据源) -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 绑定mybatis的核心配置文件(可以不用配置) -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!-- 绑定mybatis的mapper配置文件 -->
<property name="mapperLocations" value="classpath:com/cctv/mapper/UserMapper.xml"/>
</bean>
<!-- 创建一个sqlSession -->
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<!-- 只能使用构造方法注入,因为SqlSessionTemplate没有setXxx方法-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<!-- 配置声明式事务 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 结合AOP实现事务的织入 -->
<!-- 配置事务通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 给方法配置事务性 -->
<tx:attributes>
<!-- 给以add开头的方法添加事务,以下都差不多一个意思 -->
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<!-- read-only="true"表示只读 -->
<tx:method name="find" read-only="true"/>
<!-- *表示给所有方法添加事务 -->
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 配置事务切入 -->
<aop:config>
<!-- 切入点 -->
<aop:pointcut id="txPointCut" expression="execution(* com.cctv.mapper.impl.UserMapperImpl.*(..))"/>
<!-- 切入 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
</beans>
spring各个配置文件的整合与创建一个userMapper的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"
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">
<import resource="spring-dao.xml"/>
<!-- 创建一个userMapper -->
<bean id="userMapper" class="com.cctv.mapper.impl.UserMapperImpl">
<!-- 注意这里的注入:这里它不是我们自己写的setXxx方法,而是我们UserMapperImpl类继承了SqlSessionDaoSupport它写的 -->
<property name="sqlSessionTemplate" ref="sqlSessionTemplate"/>
</bean>
</beans>
测试类
package test;
import com.cctv.domain.User;
import com.cctv.mapper.UserMapper;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
public class MyTest {
@Test
public void test01(){
final ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
final UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
/**
* 注意这里findUsers方法中调用了add方法和delete方法(模拟service),如果delete方法出错则add添加也不能成功
*/
final List<User> users = userMapper.findUsers();
for (User user : users) {
System.out.println(user);
}
}
}
运行结果
没有执行时的结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ac43jd28-1625537948276)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210703193010099.png)]
执行后的结果(因为delete报错了所以add也没能添加进去)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4E5AH432-1625537948277)(C:\Users\你好\AppData\Roaming\Typora\typora-user-images\image-20210703193024076.png)]
七种配置
下面是Spring中Propagation类的事务属性详解:
REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。