Spring 是什么:
Spring是分层的javaSE/EE应用full-stack轻量型的开源框架,
以IoC(Inverse Of Control : 反转控制) 和 AOP (Aspect Oriented Programming : 面向切面编程) 为内核
提供了展现 SpringMVC 和持久层Spring JDBCTemplate 以及业务层事务管理等众多的企业级应用技术 , 还能整合开源事件众多著名的第三方框架和类库 , 逐渐成为实用最多的javaEE企业应用开发开源框架
Spring的优势 :
(1) 方便解耦 , 简化开发
通过Spring 提供的Ioc容器 , 可以将对象间的依赖关系交由Spring进行控制 , 避免硬编码造成的过度耦合 , 用户也不必再为单例模式类 , 属性文件解析 , 等这些很底层的需求编写代码 , 可以更专注于上层的应用
(2) AOP的编程支持 :
通过Spring 的AOP功能 , 方便进行面向切面编程 , 许多不容易用传统OOP实现的功能可以通过 AOP轻松实现
(3) 声明式事务的支持
可以将我们从单调烦闷的事务管理代码中解脱出来 , 通过声明式灵活的进行事务管理 , 提高开发效率和质量
(4) 方便程序的测试
可以使用非容器依赖的编程方式进行几乎所有的测试工作 , 测试不再是昂贵的工作 , 而是随手可做的事情
(5) 方便集成各种优秀的框架
Spring对各种优秀框架的支持
(6) 降低javaEE API 的使用难度
Spring对javaEE API (如jdbc , javaMail , 远程调用等 ) 进行了薄薄的分装层 , 是这些API的使用难度大大降低
(7) java源码经典学习范例
Spring的源代码设计精妙 , 结构清晰 , 匠心独用 , 处处体现着大师对java设计模式灵活运用 , 以及对java技术的高深造诣 , 它的源代码无疑是java技术最佳实践的范例
Spring的体系结构 :
- Core Container : 核心容器
Spring的快速入门:
(1) 导入Spring开发的基本包坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.14</version>
</dependency>
这个是java编译的插件 (可加可不加)
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
(2) 编写Dao接口和实现类
(3) 创建Spring核心配置文件 (一般使用 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
告诉spring的创建对象
声明bean , 就是告诉Spring要创建某个类的对象
id : 对象的自定义名称 , 唯一值 , Spring通过这个名称找到对象
class : 类的全限定名称 (不能是接口 , spring是反射机制创建对象 , 必须使用类 )
Spring就完成了 SomeService someService = new SomeServiceImpl();
Spring是把创建好的对象放到map中 , Spring框架有一个map存放对象 ,
springMap.put(id的值 (someService) , new SomeServiceImpl() (对象));
一个bean标签声明一个对象
-->
<bean id="someService" class="com.sichen.service.impl.SomeServiceImpl"></bean>
</beans>
<!--
spring的配置文件
1.Beans : 是根标签 , spring把java对象称为bean
2.Spring-bean.xsd : 是约束文件 , 和MyBatis指定 ,did是一样的
-->
(4) 在Spring配置文件中配置UserDaoImpl
<!--
id是自己命名的 , 是Bean实例字Spring容器中的唯一标识
class : Bean的全限定名称
-->
<bean id="someService" class="com.sichen.service.impl.SomeServiceImpl"></bean>
(5) 使用Spring的API获得Bean实例
Spring 默认创建对象的时间 :
在创建Spring的容器时 (就是读取配置文件的时候 ) , 他会创建配置文件中所有的对象
//使用spring容器创建的对象
//1.指定spring配置文件的名称
String config = "applicationContext.xml";
//2.创建表示spring容器的对象 , ApplicationContext
//ApplicationContext 就是表示spring容器 , 通过容器获取对象了
//ClassPathXmlApplicationContext : 表示从类路径中加载配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(config);
//getBean("配置文件中bean的id值") , 通过
SomeService someService = (SomeService)applicationContext.getBean("someService");
someService.doSome();
//FileSystemXmlApplicationContext 这个是从磁盘中读取配置文件 (不常用)
<!--
spring能创建一个非自定义的类的对象吗 , 创建一个存在的某个类的对象
spring创建对象 : 默认调用的是无参构造方法
-->
<bean id="myDate" class="java.util.Date"></bean>
两个常用方法 获取容器内创建对象的信息:
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
//使用spring提供的方法 , 获取容器中定义的对象的数量
int count = app.getBeanDefinitionCount();
System.out.println(count);
//容器中所有对象的名称
String[] names = app.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
scope :
scope : 指对象的作用范围 , 取值如下
使用singleton , 会在扫描主配置文件的时候就创建对象
使用prototype : 会在使用getBean()的时候再创建对象 , 每次getBean()的时候 , 会创建一次
Spring的生命周期方法:
加在bean标签的属性中 , 指定对象的生命周期方法的
init-method: 指定对象的初始化方法
destroy-method:指定对象的销毁方法
Ioc概念: (Spring的灵魂)
Ioc : 控制反转 ( loc , Inversion of Control) 是一个概念 , 是一种思想 , 指将传统上由程序代码直接操纵的对象调用权交给容器 , 通过容器来实现对象的装配和管理 , 控制反转就是对对象控制权的转移 , 从程序代码本身反转到了外部容器 , 通过外部容器实现对象的创建 . 属性赋值 , 依赖的管理
控制 : 创建对象 , 对象属性的赋值 , 对象之间的关系管理
反转 : 把原来的开发人员管理 , 创建对象的权限转移给代码之外的容器实现 , 由容器代替开发人员管 理对象 , 创建对象 , 给属性赋值
正转 : 由开发人员在代码中 , 使用new 构造方法 创建对象 , 开发人员主动管理对象
使用ioc的原因 :
目的就是减少对代码的改动 . 也能实现不同的功能 , 实现解耦合
java中创建对象的方法有 :
-
(1) 构造方法 :
-
(2) 反射
-
(3) 序列化
-
(4) 克隆
-
(5) 动态代理
-
(6) ioc : 容器创建对象
IoC的技术实现 : D I
DI : 依赖注入 (Dependency Injection) : 只需要在程序中提供要使用的对象名称即可 , 至于对象如何在容器中创建 , 赋值 , 查找 , 都有容器内部实现
Spring 是使用的di实现了ioc的功能 , Spring底层创建对象 , 使用的是反射机制
DI : 创建对象,给对象的属性赋值
基于xml的DI实现
在spring的配置文件中 , 使用标签和属性 , 叫做基于xml的DI实现
(1) set 注入 : (设值注入) : spring调用类的set方法, 在set方法可以实现属性的赋值
<!--
声明Student对象
注入 : 就是赋值的意思
di : 给属性赋值
1. set注入(设值注入) : Spring调用类的set方法 , 你可以在set方法中完成属性的赋值
语法 :
简单类型的set注入 :
<bean id="myStudent" class="com.sichen.DI01.Student">
<property name="属性名字" value="此属性的值">
一个property只能给一个属性赋值
</bean>
-->
<!--这个类中必须有set方法-->
<bean id="myStudent" class="com.sichen.DI01.Student">
<property name="name" value="思尘"/><!--setName("思尘")-->
<property name="age" value="21"/><!--setAge(21)-->
</bean>
<bean id="myStudent" class="com.sichen.DI02.Student">
<property name="name" value="思尘"/>
<property name="age" value="21"/>
<property name="school" ref="mySchool"/>
<!--使用ref 值为对象的名字 -->
</bean>
<bean id="mySchool" class="com.sichen.DI02.School">
<property name="name" value="南阳理工学院"/>
<property name="address" value="长江路八十号"/>
</bean>
(2) 构造注入 : Spring调用类的有参构造方法 , 创建对象 , 在构造方法中完成赋值
<!--
构造注入 :
spring调用类有参构造方法 , 在创建对象的同时 , 在构造方法中给属性赋值
要使用的标签是 :
<constructor-arg> 标签 :
一个constructor-arg : 表示构造方法中的一个参数
name : 表示构造方法的形参名
index :表示构造方法的参数的位置 , 参数从左往右是 0 , 1 ,2..
value : 构造方法的形参类型是简单类型的 , 用value
ref : 构造方法的形参类型是引用类型的 , 使用ref
-->
<!--
name 和 index 写一个就可以了(或者是两个都不写,但是要严格按照参数顺序来书写)
value 和 ref 也是只用写一个
-->
<bean id="myStudent" class="com.sichen.DI03_有参构造.Student">
<constructor-arg name="name" index="0" value="闻熙灏"/>
<constructor-arg name="age" index="1" value="21"/>
<constructor-arg name="school" index="2" ref="mySchool"/>
</bean>
<bean id="mySchool" class="com.sichen.DI03_有参构造.School">
<constructor-arg name="name" index="0" value="南阳理工"/>
<constructor-arg name="address" index="1" value="80号"/>
</bean>
<!--也可以读取系统中的文件-->
<bean id="file" class="java.io.File" >
<!--parent 和 child 是File构造方法中的两个参数
parent : 文件所在的路径
child : 文件的名称
-->
<constructor-arg name="parent" type="java.lang.String" value="D:\riji"/>
<constructor-arg name="child" type="java.lang.String" value="json2.txt"/>
</bean>
使用多个配置文件:
为什么要使用多个配置文件 :
如果项目比较大的时候 , 一个配置文件中写太多的bean的话 , 不仅修改的时候比较麻烦 , 而且在读取文件的时候 , 耗时比较大 , 在多人开发的时候 , 也会造成影响 , 所以要使用多个配置文件来进行开发
多文件的分配方式 :
- (1)按功能模块分
- 如果你的项目中有多个模块 , (相关的功能在一起) , 一个模块一个配置文件
- (2)按类的功能
- 数据库相关的配置一个文件 , 做事务的配置一个文件 , 做业务相关的一个配置文件
<!--
包含关系的配置文件 :
其中有一个主配置文件 :
包含其他的配置文件
主配置文件一般不是定义对象的
语法 :
<import resource="其他配置文件的路径"/>
关键字 : "classpath:" 表示类路径的(class文件所在的目录)(也就是target路径下的class) ,
在Spring的配置文件中要指定其他文件的位置 ,
需要使用classpath , 告诉spring到哪去加载读取文件
-->
<import resource="classpath:DI05/Spring-Student.xml"/>
<import resource="classpath:DI05/Spring-School.xml"/>
<!--
在包含关系的配置文件中 , 可以使用通配符(* : 表示任意字符)
注意 : 主配置文件名称不能包含在通配符的范围内 ,
-->
<import resource="classpath:DI05/Spring-*.xml"/>
基于注解的DI实现:
使用spring中的注解 , 完成属性的赋值 , 叫做基于注解的DI实现
<!--
引用类型的自动注入 :
spring框架根据某些规则 , 可以给引用类型赋值不用你再给引用类型赋值
使用的规则是 :
byName (按名称注入):
java类中引用类型的属性名和spring容器中的bean的id名称一样
且数据类型是一致的 , 这样的容器中的bean , spring能够赋值给引用类型
语法规则 :
<bean id="xxx" class="yyy" autowire="byName">
简单类型属性赋值
</bean>
byType (按类型注入):
java类中引用的类型的数据类型是和Spring容器中的bean的class属性是同源关系的 ,
这样的bean能够赋值给引用类型
同源关系 : 就是一类的意思 ,
1.java类中引用类型的数据类型和bean的class的值是一样的
2.java类中引用类型的数据类型(父类)是和bean的class的值(子类)是父子类关系的
3.java类中引用类型的数据类型(接口)和bean的class的值(实现类)是接口和实现类的关系
注意: 符合上边的条件 , 但是只能有一个bean的引用类型 , 不然spring就找不到对应的是那一个 , 就会报错
-->
<bean id="myStudent" class="com.sichen.DI04_使用注解的方式注入.Student" autowire="byName">
<property name="name" value="思尘"/>
<property name="age" value="21"/>
</bean>
<bean id="school" class="com.sichen.DI04_使用注解的方式注入.School">
<property name="name" value="南阳理工"/>
<property name="address" value="背景"/>
</bean>
使用注解配置文件:
使用步骤 :
-
1.加入maven的依赖 : spring-context
- 在你加入spring-context依赖的同时 , 间接的加入了spring-aop的依赖 , 想要使用注解 , 就必须要有aop的依赖
-
2.在类中加入spring的注解 (多个不同功能的注解)
-
3.在spring的配置文件中 , 加入一个组件扫描器的标签 , 说明注解在你项目中的位置
-
<!-- <context:component-scan base-package="com.sichen.DI01"/> 声明组件扫描器(component-scan),组件就是java对象 base-package : 指定注解在你的项目中的包名 component-scan工作方式 : spring会扫描遍历 base-package指定的包 , 包中和子包中的所以类 , 找到类中的注解 , 按照注解的功能 去创建对象 , 或给属性赋值 加入了component-scan标签 , 配置文件的变化: 1.加入了一个新的约束文件 , spring-context.xsd 2.给这个新的约束文件起个命名空间的名称 context 前缀: 是用来区分标签是来自于那一个文件的 --> <context:component-scan base-package="com.sichen.DI01"/>
指定多个包的三种方式 :
<!--
指定多个包的方式 :
第一种方式 : 使用多次组件扫描器 , 指定不同的包
第二种方式 : 使用分隔符 ( ; 或 ,)来分隔包名
<context:component-scan base-package="com.sichen.DI01;com.sichen.DI02"/>
第三种方式 : 指定一个父包 ,
<context:component-scan base-package="com.sichen"/>
但是不建议 , 把范围写的太大 , 不然会影响效率
-->
spring的原始注解详解 :
创建对象的注解 (四个):
(1)@Component:创建Bean的对象 , bean是用来封装数据的
-
/** * @Component(value = "myStudent") : * 创建对象的 , 等同于<bean>的功能 * 属性 : value 对象的名称 ,也就是bean的id值 * value的值是唯一的 , 创建的对象在整个容器中就一个 * 位置是在类的上边 , * <bean id="myStudent" class="com.sichen.DI01"></bean> * 所以我们还需要在配置文件中指明注解的位置 */ 还有其他两种写法 @Component("myStudent") //这种是省略value , 开发中常用的方法 @Component //这种是使用spring默认的命名 : 类名的首字母小写的格式
(2)@Respotory(用在持久层的上边) :
放在dao的实现类的上边, 表示创建dao对象,dao对象是能访问数据库的
(3)@Service(用在Service的上边) :
创建service对象的 , service对象是做业务处理的
- 可以有事务等功能
(4)@Controller(用在控制器Servlet上边的) :
放在控制器(处理器)类 , 创建控制器对象的 , 控制器对象,能够接收用户提交的参数 , 显示请求的处理结果
@Component :
如果你想要创建对象 ,
但是这个类并不属于其他三个层次的类 (Controller (Servlet) , Service , Respotory (Dao)), 那么就使用@Component这个注解
给类型赋值的
@Value: 简单类型赋值 :
-
/** * @Value 简单类型的属性赋值 * 属性 : value 是String类型的 , 表示简单类型的属性值 * 位置 : 1.在属性定义的上边 , 无需set方法 , 推荐使用 * 2.在set方法的上边 */ @Value("思尘") private String name;
@Autowired : 引用类型的赋值 :
/**
* 引用类型
* @Autowired Spring框架提供的注解 , 实现引用类型的赋值
* Spring 中通过注解给引用类型赋值 , 使用的是自动注入原理 , 支持byName , byType
*
* @Autowired : 默认使用的是byType自动注入
* 位置 : (1):在属性定义的上边 , 无需set方法 ,推荐使用 , 按类型的话 , 不管School类型上的名字是什么, Spring都会按照类型来进行读取
* (2):在set方法的上边
*/
@Autowired
private Teacher teacher;
@Qualifier :配合 @Autowired:
/* @Autowired : 使用byName注入的方式 :
* 需要做的事情是 :
* 1.在属性的上边加入 @Autowired 注解
* 2.还要在属性上边加上 @Qualifier(value="要注入的bean的id") 注解
* 表示使用指定名称的bean赋值
*/
@Autowired
@Qualifier(value = "mySchool")
private School school;
/* @Autowired :
* 有一个属性 : required , 是一个boolean类型的 , 默认为true
* required=true : 表示引用类型赋值失败(比如说 ,你的id名字写错了导致的不能正常赋值) , 程序报错 , 并且终止执行
* required=false : 表示引用类型赋值失败 , 程序正常运行 , 引用类型是一个null
* 建议没有特殊情况 , 就使用默认为true的值 ,
*/
@Resource :
/**
* 引用类型 :
* @Resource :
* 这个是来自jdk中的注解 ,
* spring框架提供了对这个注解的功能的支持
* 可以使用这个注解给引用类型赋值 , 使用的也是自动注入的原理
* 支持 byName , buType */
默认的属性是byName
/* byName使用的时候 , 就是说 , School school 中的school(自定义的名称) ,要和对应类上的value的值一致 , 不然就会找不到对应的类 ,然后使用byType的属性
*
* 位置 : 1.在属性定义的上面 , 无需set方法 , 推荐使用
*/
@Resource
private School school;
/*
* 默认是byName自动注入 , 如果byName注入失败的话 , 在使用byType的方式
* 只使用byName的属性 : 增加一个属性 name
* name的值是bean的id(名称)
*/
@Resource(name = "mySchool")
private School school;
<!--
如果使用这个注解没有的话 , 看看jdk的版本是不是1.8的 ,
切换到1.8还不行的话 , 在maven中添加一个依赖即可 -->
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
spring的新注解:
使用上边的注解还是不能完全替代xml配置文件,还需要使用注解替代的配置如下
- 非自定义的Bean的配置
- 加载properties文件的配置 context:property-placeholder
- 组件扫描的配置 context:component-scan
主配置类:SpringConfiguration.java
package com.sichen.DI06_新注解_Configuration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
//标志该类是spring的核心配置类
@Configuration
//<context:component-scan base-package="com.sichen.DI04"/>
@ComponentScan("com.sichen.DI04")//组件扫描器 , 值就是要扫描的包名
//<import resource=""/>
@Import({DataSourceConfiguration.class,})
//加载配置类 , 值是一个数组 , 可以同时加载多个配置类
public class SpringConfiguration {
}
配置类: (这个是加载数据源的配置类)
package com.sichen.DI06_新注解_Configuration;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import javax.sql.DataSource;
//<context:property-placeholder location="classpath:jdbc.properties">
@PropertySource("classpath:jdbc.properties")//指定配置文件
public class DataSourceConfiguration {
//配置文件虽然加载到容器当中了 , 但是要使用@Value的方式赋值
@Value("${driverClassName}")
private String Driver;
@Value("${url}")
private String url;
@Value("${username}")
private String username;
@Value("${password}")
private String password;
@Bean("database")//spring会将当前方法的返回值,以指定名称放入到容器当中
public DataSource getDataSource(){
DruidDataSource druid = new DruidDataSource();
druid.setDriverClassName(Driver);
druid.setUrl(url);
druid.setName(username);
druid.setPassword(password);
return druid;
}
}
读取主配置类 :
package com.sichen;
import com.sichen.DI06_新注解_Configuration.SpringConfiguration;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Test06 {
@Test
public void test06(){
//使用配置类后 , 就要使用这种方式加载配置类了 ,
ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfiguration.class);
}
}
注解和配置文件的使用选择 :
-
注解的使用时非常方便的 , 快捷 , 方便记忆
-
浏览代码的时候 , 可以直观的了解到详细的信息 , 代码可读性高
缺点 :
-
注解是嵌入到你的源代码中 , 耦合度较高 (侵入性较高)
-
所以对经常改动的类和属性 , 使用配置文件的方式 , 比较利于维护
使用属性配置文件 (配合注解):
myname=思尘
myage=21
需要在主配置文件中 , 配置一条
<context:property-placeholder location="classpath:属性配置文件的名字.properties"/>
<!--
如果属性配置文件中 , 带有中文的值 , 要增加一条属性 :
file-encoding="utf-8"
-->
<添加这个之后 , 在注解中就可以使用 @Value("${myname}") , 来使用对应的值了>
使用依赖注入的核心是为了在容器内部完成依赖的注入 (例如将dao注入到service中)
set方法注入:
在service中创建一个私有成员Dao , 设置一个set方法 ,
在主配置文件中 , 声明dao , 声明service , 在service声明的内部 , 设置dao的值为声明的dao
<!--声明userDao-->
<bean id="userDao" class="com.sichen.dao.impl.UserDaoImpl"></bean>
<!-- 声明service , 并将userDao注入到service中 ,
注意 , name中的值是service中提供set方法后边的内容的首字母小写 setUserDao()-->
<bean id="userService" class="com.sichen.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
简便形式 : 使用p命名空间注入 :
<!--在最开始的Beans属性中加入p命名空间-->
xmlns:p="http://www.springframework.org/schema/p"
<!--然后直接在service的bean中加属性即可 , 使用的是带ref的属性 , 值就是dao的id-->
<bean id="userService"
class="com.sichen.service.impl.UserServiceImpl" p:userDao-ref="userDao">
使用构造方法完成注入 :
在service中设置一个私有成员dao
设置带参构造
xml中只声明 , 但是不写内容 , 默认使用的是无参构造方法
<!--声明userDao-->
<bean id="userDao" class="com.sichen.dao.impl.UserDaoImpl"></bean>
<!-- 声明service , 并将userDao注入到service中-->
<bean id="userService" class="com.sichen.service.impl.UserServiceImpl">
<constructor-arg name="使用的是构造方法中的形参名" ref="dao对象的id"/>
</bean>
注入集合之类的内容:
<bean id="userService" class="com.sichen.service.impl.UserServiceImpl">
<property name="定义的集合名称">
<!--这里边定义的有对应的标签 ,定义的是什么类型 , 就使用什么类型的标签-->
<list>
<!--这里边如果是普通类型的值 , 就使用value , 如果是引用类型的值 , 就要使用ref标签-->
<value>要设置的值</value>
<ref></ref>
</list>
<map>
<entry key="设置key" value-ref="设置值 , 如果是普通类型就用value"></entry>
</map>
</property>
</bean>
Spring集成Mybatis开发:
把mybatis和spring集成在一起 , 像一个框架一样使用
用的技术是 IOC
为什么ioc能把mybatis和spring集成在一起呢 , 是因为ioc能创建对象 , 可以把mybatis框架中的对象交给spring同一管理 , 开发人员从spring中获取对象
开发人员就不用同时面对两个或多个框架了, 就面对一个spring即可
Spring集成Mybatis需要知道的内容 :
- 1.定义一个bean类 , 用来存储数据
- 2.定义dao接口 , StudentDao
- 3.定义mapper文件 , StudentDao.xml
- 4.定义MyBatis主配置文件 , mybatis.xml
- 4.创建Dao的代理对象 ,
- StudentDao dao = SqlSession.getMapper(StudentDao.class)
- 5.执行方法 :
- List students = dao.selectStudents();
要使用dao对象 , 需要使用getMapper()方法
怎么能使用getMapper()方法 , 需要哪些条件
- 1.获取SqlSession对象 , 需要使用SqlSessionFactory的OpenSession()方法
- 2.创建SqlSessionFactory对象
- 3.创建SqlSessionFactory对象需要读取主配置文件
主配置文件的配置信息 :
- 1.数据库信息
- 我们会使用独立的链接池类来替换MyBatis默认自带的 , 把连接池类也交给Spring创建 , 这样就不要在主配置文件中配置数据库信息了
- 2.mapper文件位置
综上所述 :
我们需要让Spring创建以下对象
- 1.独立的数据库连接池类的对象 , 使用阿里的druid连接池
- 2.SqlSessionFactory对象 ,
- 3.创建出dao对象 ,
需要学习就是上面三个对象的创建语法 , 使用xml的bean标签
Spring集成MyBatis具体步骤 :
- 1.新建maven项目
- 2.加入maven的依赖
- (1)spring依赖
- (2)Mybatis依赖
- (3)mysql驱动
- (4)Spring的事务依赖
- (5)mybatis和spring集成的依赖 : mybatis官方提供的 , 用来在spring项目中创建mybatis的SqlSessionFactory , dao对象的
<dependencies>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!--spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.14</version>
</dependency>
<!--做spring事务的-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.14</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.14</version>
</dependency>
<!--mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.20</version>
</dependency>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<!--mybatis-spring集成的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
</dependencies>
<build>
<!--编译时保留配置文件的插件
目的是把src/main/java目录中的xml文件和properties文件包含到输出结果中
输出到class目录中
-->
<resources>
<resource>
<directory>src/main/java</directory><!--所在的目录-->
<includes><!--包括目录下的.properties , .xml文件都会被扫描到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
- 3.创建实体类
- 4.创建Dao的接口 和 mapper文件
- 5.创建mybatis主配置文件
<!--输出日志的-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--设置实体类的别名 , 使用package ,包中的类名就是别名-->
<typeAliases>
<package name="com.sichen.domain"/>
</typeAliases>
<mappers>
<!--name 是包名 , 这个包中的所有的mapper.xml文件一次都能加载-->
<package name="com.sichen.dao"/>
</mappers>
- 6.创建Service接口和实现类 , 属性是dao
- 7.创建spring的配置文件 , 声明mybatis的对象交给spring创建
- (1)数据源
- (2)SqlSessionFactory
- (3)Dao对象
- (4)声明自定义的Service
<!--
把数据库的配置信息 , 写在一个单独的文件中 ,
需要让spring知道这个文件的位置
-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--声明数据源 DataBase , 作用是连接数据库的-->
<bean id="myDataBase" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<!--set注入 , 给DruidDataBase提供连接数据库信息-->
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="${jdbc.maxActive}"/>
</bean>
<!--声明的是mybatis中提供的SqlSessionFactoryBean类 , 这个类内部是创建SqlSessionFactory的-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--set注入 , 把数据库的链接池赋给了dataSource属性-->
<property name="dataSource" ref="myDataBase"/>
<!--声明mybatis的主配置文件-->
<property name="configLocation" value="classpath:mybatis.xml"/>
</bean>
<!--
创建dao对象 , 使用SqlSession的getMapper (StudentDao.class)
MapperScannerConfigurer: 在内部调用getMapper()生成每个dao接口的代理对象
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定sqlSessionFactory对象的id-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!--
指定一个包名 , 包名是dao接口所在的包名
MapperScannerConfigurer会扫描这个包中的所有接口 , 把每个接口都执行一次getMapper()方法
得到每个接口的dao对象 ,
创建好的dao对象放入到spring的容器中的 ,dao对象的默认名称是 : 接口名首字母小写
-->
<property name="basePackage" value="com.sichen.dao"/>
</bean>
<!--声明service-->
<bean id="studentService" class="com.sichen.service.impl.StudentServiceImpl">
<!--这里的ref就是上边创建的dao对象 , 这个类中要使用几个dao , 就要声明几个-->
<property name="dao" ref="studentDao"/>
</bean>
- 8.创建测试类 , 获取Service对象 , 通过service调用dao完成数据库的访问
Spring集成Junit
-
导入SpringJunit的集成坐标
-
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.3.15</version> </dependency>
@RunWith(SpringJUnit4ClassRunner.class)
//使用spring集成junit的注解 , 内容是固定的
//@ContextConfiguration("classpath:applicationContext.xml")
//声明配置文件的位置
@ContextConfiguration(classes = SpringConfiguration.class)
//声明配置类的位置
public class SpringJunitTest {
@Autowired
//注入要测试的对象
private DataSource dataSource;
@Test
public void test(){
System.out.println(dataSource);
}
}
Spring集成Web:
在Web项目中 , 可以使用ServletContextListener监听Web应用的启动 , 我们可以在Web应用启动时 , 就加载Spring的配置文件 , 创建应用上下文对象ApplicationContext , 在将其存储到最大的域ServletContext域中 , 这样就可以在任意位置从域中获得应用上下文ApplicationContext对象了
原理 :
- 设置监听器类:
public class ContextLoaderListener implements ServletContextListener {
@Override//监听ServletContext初始化的方法
public void contextInitialized(ServletContextEvent sce) {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
//将Spring的应用上下文对象存储到ServletContext域中
ServletContext servletContext = sce.getServletContext();
servletContext.setAttribute("app",app);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
}
}
- 在xml中配置监听器
<!--配置监听器-->
<listener>
<listener-class>listener.ContextLoaderListener</listener-class>
</listener>
对上边代码的优化:
- 在web.xml文件中声明全局初始化参数 ,指定主配置文件的位置
<!--全局初始化参数 , 指定主配置文件的位置-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--配置监听器-->
<listener>
<listener-class>listener.ContextLoaderListener</listener-class>
</listener>
- 使用context的getInitParameter方法加载初始化参数(也就是主配置文件的位置)
public class ContextLoaderListener implements ServletContextListener {
@Override//监听ServletContext初始化的方法
public void contextInitialized(ServletContextEvent sce) {
ServletContext context = sce.getServletContext();
String initParameter = context.getInitParameter("contextConfigLocation");
ApplicationContext app = new ClassPathXmlApplicationContext(initParameter);
context.setAttribute("app",app);
}
}
- 设置一个工具类 , 接收参数为ServletContext , 返回值为ApplicationContext
public static ApplicationContext getWebApplicationContext(ServletContext servletContext){
return (ApplicationContext) servletContext.getAttribute("app");
}
- 在之后的servlet中获取servletContext , 即可 , 然后调用工具类即可获取ApplicationContext容器对象
Spring提供获取应用上下文的工具
使用Spring提供的监听器 , 这个监听器就是对上边功能的封装
我们需要做的只有两件事
- 在web.xml文件中配置ContextLoaderListener监听器 (需要导入spring-web坐标)
<!--全局初始化参数 , 指定主配置文件的位置-->
<context-param>
<!--这个名称必须是这个 , 监听器底层就是使用这个名称来获取主配置文件的-->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--使用Spring提供的监听器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
- 使用WebApplicationContextUtils获得应用上下文对象ApplicationContext
ServletContext context = this.getServletContext();
WebApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(context);
Spring集成JDBCTemplate:
在applicationContext.xml中声明JDBCTemplate的对象
<!--声明JDBCTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="MyDataBase"/>
</bean>
Spring的事务处理 :
事务的简介:
什么是事务 :
事务就是指一组sql语句的集合 , 集合中有多条sql语句
我们希望这些sql语句都能成功 , 或者都失败 , 这些sql语句的执行是一致的 , 作为一个整体使用
什么时候使用事务 :
当我的操作 , 涉及到多个表 , 或者是多个sql语句的insert , update , delete , 需要保证这些语句都是成功才能完成我的功能 , 或者失败 , 保证操作时符合要求的
在java代码中使用 :
service类的业务方法上 , 因为业务方法会调用多个dao方法 , 执行多个sql语句
上边事务的处理方法 , 有什么不足:
(1) 不同的数据库访问技术, 处理事务的对象 , 方法不同 , 需要了解不同数据库访问技术使用事务的原理
(2) 掌握多种数据库中事务的处理逻辑 , 什么时候提交事务 , 什么时候回滚事务
(3) 处理事务的多种方法
总结 : 多种数据库的访问技术 , 有不同的事务处理机制 , 对象和方法
怎么解决不足:
spring它提供了一种处理事务的统一模型 , 能够使用统一的步骤 , 方式 , 来完成多种数据库访问技术的事务处理 ,
声明式事务 : 把事务相关的资源和内容都提供给spring , spring就能处理事务的提交和回滚了 , 基本上不用写代码
spring处理事务 :
spring处理事务的模型 , 使用的步骤都是固定的 , 把事务使用的信息提供给spring就可以了
1.事务内部处理 , 回滚事务 , 使用的是事务管理器对象 , 代替你完成commit , roolback
事务管理器是一个接口和他众多的实现类
- 接口 :
- PlatformTransactionManager : 定义了事务的重要方法 , 一个提交 , 一个回滚
- 实现类 :
- Spring把每一种数据库的访问技术对应的事务处理类都创建好了
- 怎么使用 :
- 你需要告诉Spring , 你用的是那种数据库的访问技术 , 怎么告诉Spring声明数据库访问技术对应的事务管理器实现类 , 在Spring的配置文件中使用声明就可以了
2.你的业务方法需要什么样的事务 , 说明需要事务的类型
(1) 事务的隔离级别 :
- 这些常量均是以 ISOLATION_ 开头的 ,
- DEFAULT : 默认值 : 采用DB默认的事务隔离级别 ,
- MySql的默认是 REPEATABLE_READ 可重复读
- Oracle默认的是 : READ_COMMITTED 读已提交
- READ_UNCOMMITTED : 读未提交 , 未解决任何并发问题
- READ_COMMITTED : 读已提交 , 解决脏读 , 存在不可重复读与幻读
- REPEATABLE_READ : 可重复读 , 解决脏读 , 不可重复读 , 存在幻读
- SERIALIZABLE : 串行化 , 不存在并发问题
(2) 事务的超时时间 :
表示一个方法的最长的执行时间 , 如果方法执行时 , 超过了时间 , 事务就回滚 ,
单位是秒 :
整数值 :默认是-1 , 表示没有最长时间 , 影响时间的因素很多 , 往往收到网络和数据库的处理时间影响 , 一般项目中不设置这个时间
(3) 事务的传播行为 :
控制业务方法是不是有事务的 , 是什么样的事务的
7个传播行为: 表示你的业务方法调用时 , 事务在方法之间是如何使用的
- PROPAGATION_REQUIRED
- 指定的方法必须在事务内执行 , 若当前存在事务 , 就加入到当前事务中 , 若当前没有事务 , 则创建一个新事务 , 这种传播行为是最常见的选择 , 也是spring默认的事务传播行为
- PROPAGATION_REQUIRES_NEW
- 总是新建一个事务 , 若当前存在事务 , 就将当前事务挂起 , 直到新事务执行完毕
- PROPAGATION_SUPPORTS
- 指定的方法支持当前事务 , 但若当前没有事务 , 但是也可以以非事务方式执行(也就是说有没有事务都不影响)
- 前三个是重要的
- PROPAGATION_MANDATORY
- PROPAGATION_NESTED
- PROPAGATION_NEVER
- PROPAGATION_NOT_SUPPORTED
3.事务提交事务 , 回滚事务的时机
- (1)当你的业务方法 , 执行成功 , 没有异常抛出 , 当方法执行完毕 ,spring在方法执行后提交事务 , 事务管理器commit
- (2)当你的业务方法抛出运行时异常或ERROR , spring执行回滚 , 调用事务管理器的rollback
- 运行时异常的定义 : RuntimeException 和他的子类都是运行时异常 ,
- 例如: NullPointException , NumberFormatException
- (3)当你的业务方法抛出非运行时异常 , 主要是受查异常和ERROR时 , 提交事务
- 受查异常 , 在你写代码时必须处理的异常
- 例如 : IOException , SQLException
总结Spring的事务 :
1.管理事务的是 , 事务管理和他的实现类
2.Spring的事务是一个统一模型
- 指定要使用的事务管理器实现类 , 使用
- 指定哪些类 , 那些方法需要加入事务的功能
- 指定方法需要的隔离级别 , 传播行为 , 超时
你需要告诉spring , 你的项目中类信息 , 方法的名称 , 方法的事务传播行为
Spring框架中提供的事务处理方法:
1.@Transactional注解(中小项目)
Spring框架自己用AOP来实现给业务增加事务的功能 , 使用 @Transactional注解增加事务 ,
@Transactional注解 : 是Spring框架自己的注解 , 是放在方法的上面 , 表示当前方法具有事务 , 我们可以给注解的属性赋值 , 来表示具体的隔离级别 , 传播行为 , 异常信息等
属性 :
-
propagation :
- 用于设置事务的传播属性 , 该属性类型为 Propagation 枚举 , 默认值为Propagation.REQUIRED
-
isolation :
- 用于设置事务的隔离级别 , 该属性为 Isolation 枚举 , 默认值为 Isolation.DEFAULT
-
readOnly :
- 用于设置该方法对数据库 的操作是否只是只读 , 该属性为boolean , 默认值为false , 当你的操作都是查询的时候 , 可以考虑使用这个属性
-
timeout :
- 用于设置本操作与数据库连接的超时时限 , 单位为秒 , 类型为int , 默认值为 -1 , 即没有时限
-
rollbackFor:
-
指定需要回滚的异常类 , 类型为 Class[] , 默认值为空数组 , 当然 , 若只有一个异常类时 , 可以不使用数组
-
rollbackFor :表示发生指定的异常一定回滚 处理的逻辑 : (1)spring框架会首先检查方法抛出的异常是不是在rollbackFor的属性值中 如果异常在rollbackFor列表中 , 不管是什么类型的异常 , 一定回滚 (2)如果你的抛出的异常不在rollbackFor列表中 , spring会判断异常是不是RuntimeException , 如果是一定会回滚
-
rollbackForClassName :
- 指定需要回滚的异常类类名 , 类型为String[] , 默认值为空数组 , 当然 , 若只有一个异常类时 , 可以不使用数组
-
noRollbackFor :
- 指定不需要回滚的异常类 , 类型为 Class[] , 默认值为空数组 , 当然 , 若只有一个异常类时 , 可以不使用数组
使用@Transactional注解 的步骤 :
1.需要声明事务的管理器对象
<!--使用spring的事务处理-->
<!--声明事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--知道连接的数据库 , 指定数据源-->
<property name="dataSource" ref="mybatabase"/>
</bean>
2.开启事务注解的驱动 , 告诉spring框架 , 我要使用注解的方法管理对象 ,
<!--
开启事务注解驱动 , 告诉spring使用注解来管理事务 , 创建代理对象
transaction-manager :值是事务管理器的id
-->
<tx:annotation-driven transaction-manager="transactionManager" />
spring使用aop机制 :
- 创建 @Transactional 所在类的类代理对象 , 给方法加入事务的功能 , spring给业务方法加入事务
- 在你的业务方法执行之前 , 先开启事务 , 在业务方法执行之后 , 提交或者回滚事务 , 使用aop的环绕通知
3.在你的方法上面加入@Transactional注解 (使用默认值就可以实现功能 , 异常检测的是运行时异常)
2.AspectJ框架的AOP配置管理事务 (大型项目)
声明式事务处理:
有很多的类 , 方法 , 需要大量的配置事务 , 使用的aspectJ框架功能 , 在spring配置文件中
声明类 , 方法需要的事务 , 这种方式业务方法和事务配置完全分离
实现步骤 :
都是在xml配置文件中实现的
(1) 要使用的是aspectJ框架 , 需要加入依赖
<!--aspectJ依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.14</version>
</dependency>
(2)声明事务管理器对象
<!--声明事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--声明数据源-->
<property name="dataSource" ref="myDataBase"/>
</bean>
(3)声明方法需要的事务类型(配置方法的事务属性 , 传播行为 , 超时)
<!--
声明业务方法它的事务属性 , (隔离级别 , 传播行为 , 超时时间)
id : 自定义名称 , 表示<tx:advice>和</tx:advice>之间配置内容的
transaction-manager : 事务管理器对象的id
-->
<tx:advice id="myadvice" transaction-manager="transactionManager">
<!--tx:attributes : 它表示要配置事务的属性-->
<tx:attributes>
<!--
tx:method : 给具体的方法配置事务属性 , method可以有多个 , 分别给不同的方法设置事务属性
name:方法的名称 , (1)完整的方法名称 , 不带有包和类
(2)方法可以使用通配符的 , '*' 表示任意字符
propagation : 传播行为 : 枚举值
isolation : 隔离级别
rollback-for : 你指定的异常类名 , 全限定名称 ,发生异常一定回滚
-->
<tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"
rollback-for="java.lang.NullPointerException,com.sichen.excep.NotEnoughException"/>
<!--
在进行事务的匹配过程中 ,
优先找完全匹配的方法 , 如果不合适 , 就找带通配符的 , 然后都不匹配 , 就找最后的 * 的
-->
</tx:attributes>
</tx:advice>
(4)配置Aop , 指定哪些类要创建代理 ,
将切入点表达式和方法的通知关联起来 , 让spring知道那个类的那个方法需要使用事务
<!--配置AOP-->
<aop:config>
<!--配置切入点表达式 : 指定那些包中类 , 要使用事务 这里代表的是
所有包中的service包中的所有子包中的所有类 ,的任意参数的所有方法-->
<aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>
<!--
配置增强器 : 关联advice和pointcut , 将切入点表达式和方法的通知关联起来 , 让spring知道那个类的那个方法需要使用事务
advice-ref : 通知 , 上面tx:advice那里的配置
pointcut-ref : 切入点表达式的id
-->
<aop:advisor advice-ref="myadvice" pointcut-ref="servicePt"/>
</aop:config>
ger=“transactionManager”>
tx:attributes
<!–
tx:method : 给具体的方法配置事务属性 , method可以有多个 , 分别给不同的方法设置事务属性
name:方法的名称 , (1)完整的方法名称 , 不带有包和类
(2)方法可以使用通配符的 , ‘*’ 表示任意字符
propagation : 传播行为 : 枚举值
isolation : 隔离级别
rollback-for : 你指定的异常类名 , 全限定名称 ,发生异常一定回滚
-->
<tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"
rollback-for="java.lang.NullPointerException,com.sichen.excep.NotEnoughException"/>
<!--
在进行事务的匹配过程中 ,
优先找完全匹配的方法 , 如果不合适 , 就找带通配符的 , 然后都不匹配 , 就找最后的 * 的
-->
</tx:attributes>
</tx:advice>
(4)配置Aop , 指定哪些类要创建代理 ,
将**切入点表达式**和**方法的通知**关联起来 , 让spring知道那个类的那个方法需要使用事务
```xml
<!--配置AOP-->
<aop:config>
<!--配置切入点表达式 : 指定那些包中类 , 要使用事务 这里代表的是
所有包中的service包中的所有子包中的所有类 ,的任意参数的所有方法-->
<aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>
<!--
配置增强器 : 关联advice和pointcut , 将切入点表达式和方法的通知关联起来 , 让spring知道那个类的那个方法需要使用事务
advice-ref : 通知 , 上面tx:advice那里的配置
pointcut-ref : 切入点表达式的id
-->
<aop:advisor advice-ref="myadvice" pointcut-ref="servicePt"/>
</aop:config>