Spring

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的体系结构 :

1646651849608

  • 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 : 指对象的作用范围 , 取值如下

1646655517186

使用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的原始注解详解 :

image-20220317184736501

创建对象的注解 (四个):
(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

image-20220317191909421


主配置类: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

image-20220317202102657

  • 导入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>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值