Spring学习笔记(超详细)

本文详细介绍了Spring框架的核心概念,包括Spring的概述、优点和体系结构。深入讲解了Spring的IoC和DI,通过XML配置和注解方式展示了Bean的创建和管理。此外,文章还涵盖了AOP、JdbcTemplate的使用、Spring与Mybatis的整合以及Spring的事务管理。最后,提到了Spring的新特性和如何使用Spring发送邮件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

spring5

1 spring概述

1.1 spring框架是什么

Spring 是于 2003 年兴起的一个轻量级的 Java 开发框架,它是为了解决企业应用开发的复杂性而创建的。

Spring 的核心是控制反转(IoC)和面向切面编程(AOP)。Spring 是可以在 Java SE/EE 中使用的轻量级开源框架

spring官网地址:https://spring.io/

spring官网下载地址:https://repo.spring.io/release/org/springframework/spring/

GitHub : https://github.com/spring-projects

1.2 spring的优点

  • Spring是一个开源的免费的框架(容器)。
  • spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能(AOP 编程的支持)
  • 支持事务的处理,对框架整合的支持。
  • Spring 不排斥各种优秀的开源框架,相反 Spring 可以降低各种框架的使用难度(方便集成各种优势框架)
  • Spring 框架运行占用的资源少,运行效率高,不依赖其他 jar(轻量),非侵入式的框架。
  • 提供了 Ioc 控制反转,由容器管理对象及对象的依赖关系(针对接口编程,解耦合)

1.3 spring体系结构

Spring 由 20 多个模块组成,它们可以分为数据访问/集成(Data Access/Integration)、Web、面向切面编程(AOP, Aspects)、提供JVM的代理(Instrumentation)、消息发送(Messaging)、核心容器(Core Container)和测试(Test)

在这里插入图片描述

Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式 .

在这里插入图片描述

  • 核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开
  • Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
  • Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能 , 集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理任何支持 AOP的对象。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中。
  • Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
  • Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
  • Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
  • Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。

2 spring第一个程序

2.1 环境搭建

  • 创建maven工程

2.2 导入依赖

    <dependencies>
        <!--spring依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <!--maven插件-->
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

2.3 定义接口和实现类

  • 接口
public interface UserService {
    public void addUser();
}

  • 实现类
public class UserServiceImpl implements UserService {
    @Override
    public void addUser() {
        System.out.println("执行了底层增加用户的方法!");
    }
}

2.4 创建spring配置文件

在 src/main/resources/目录现创建一个 xml 文件,文件名可以随意,但 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">

    <!-- 配置service
            bean: 配置需要创建的对象,一个实例对应一个 bean元素
            id:该属性是 Bean 实例的唯一标识,程序通过 id 属性访问 Bean,用于之后从spring容器获得实例时使用的
            class:需要创建实例的全限定类名,注意这里只能是类,不能是接口
     -->
    <bean id="userService" class="com.zwh.service.UserServiceImpl"></bean>
</beans>

2.5定义测试类

public class MyTest {
    @Test
    public void test() {
        // 指定spring配置文件的位置和名称
        String resource = "applicationContext.xml";
        // 创建spring容器的对象
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(resource);
        // 从spring容器中获取对象,使用id
        UserService userService = (UserServiceImpl) context.getBean("userService");
        // 执行对象的业务方法
        userService.addUser();
    }
}

测试结果:
执行了底层增加用户的方法!

2.6 使用 spring 创建非自定义类

spring 配置文件的class属性直接加全限定类名即可

<bean id="myDate" class="java.util.Date" />

2.7 容器中bean的作用域

当通过 Spring 容器创建一个 Bean 实例时,不仅可以完成 Bean 的实例化,还可以通过scope 属性,为 Bean 指定特定的作用域,Spring 支持 5 种作用域。

  • singleton:单态模式。即在整个 Spring 容器中,使用 singleton 定义的 Bean 将是单例的,只有一个实例。默认为单态的。
  • prototype:原型模式。即每次使用 getBean 方法获取的同一个bean的实例都是一个新的实例。
  • request:对于每次 HTTP 请求,都将会产生一个不同的 Bean 实例。
  • session:对于每个不同的 HTTP session,都将产生一个不同的 Bean 实例。
  • global session:每个全局的 HTTP session 对应一个 Bean 实例。典型情况下,仅在使用portlet 集群时有效,多个 Web 应用共享一个 session。一般应用中,global-session 与 session是等同的。

注意:

  • 对于 scope 的值 request、session 与 global session,只有在 Web 应用中使用 Spring 时,该作用域才有效。
  • 对于 scope 为 singleton 的单例模式,该 Bean 是在容器被创建时即被装配好了。
  • 对于 scope 为 prototype 的原型模式,Bean 实例是在代码中使用该 Bean 实例时才进行装配的。
<bean id="myDate" class="java.util.Date" scope="prototype"></bean>

<bean id="myDate" class="java.util.Date" scope="singleton"></bean>

在这里插入图片描述

2.8 bean的生命周期

Bean 实例从创建到最后销毁,需要经过很多过程,执行很多生命周期方法。

  1. 调用无参构造器,创建实例对象。
  2. 调用参数的 setter,为属性注入值。
  3. 若 Bean 实现了 BeanNameAware 接口,则会执行接口方法 setBeanName(String beanId),使 Bean 类可以获取其在容器中的 id 名称。
  4. 若 Bean 实现了 BeanFactoryAware 接口,则执行接口方法 setBeanFactory(BeanFactory factory),使 Bean 类可以获取到 BeanFactory 对象。
  5. 若 定义并注册了 Bean 后 处 理 器 BeanPostProcessor , 则 执 行 接 口 方 法postProcessBeforeInitialization()。
  6. 若 Bean 实现了 InitializingBean 接口,则执行接口方法 afterPropertiesSet ()。该方法在 Bean 的所有属性的 set 方法执行完毕后执行,是 Bean 初始化结束的标志,即 Bean 实例化结束。
  7. 若设置了 init-method 方法,则执行。
  8. 若 定 义 并 注 册 了 Bean 后 处 理 器 BeanPostProcessor , 则 执 行 接 口 方 法postProcessAfterInitialization()。
  9. 执行业务方法。
  10. 若 Bean 实现了 DisposableBean 接口,则执行接口方法 destroy()。
  11. 若设置了 destroy-method 方法,则执行。

3 IOC控制反转

3.1 IOC概念和原理

什么是 IOC:

  • 控制反转,把对象创建和对象之间的调用过程,交给 Spring 进行管理
  • 使用 IOC 目的:为了降低耦合度
  • IOC 底层原理:xml 解析、工厂模式、反射

spring底层原理:

在这里插入图片描述

3.2 BeanFactory接口

IOC 思想基于 IOC 容器完成,IOC 容器底层就是对象工厂

Spring 提供 IOC 容器实现两种方式:(两个接口)

  • BeanFactory:IOC 容器基本实现,是 Spring 内部的使用接口,不提供开发人员使用,加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象
  • ApplicationContext:BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人员进行使用,加载配置文件时候就会把在配置文件对象进行创建

3.3 spring之DI注入

3.3.1 基于 XML 的DI注入

DI注入定义:bean 实例在调用无参构造器创建对象后,就要对 bean 对象的属性进行初始化。初始化是由容器自动完成的,称为注入。

根据注入方式的不同,常用的有两类:set 注入、构造注入。

3.3.1.1 第一种注入方式set 注入(掌握)

set 注入也叫设值注入是指,通过 setter 方法传入被调用者的实例。这种注入方式简单、直观,因而在 Spring 的依赖注入中大量使用。

创建实体类,定义属性和对应的 set 方法

在 spring 配置文件配置对象创建,配置属性注入

  • 简单类型的set注入
    <!--简单类型使用set注入-->
    <bean id="student" class="com.zwh.pojo.Student">
        <!--使用 property 完成属性注入
            name:类里面属性名称
            value:向属性注入的值
        -->
        <property name="sname" value="张三"/>
        <property name="sage" value="19"/>
    </bean>
  • 引用类型的set注入

在这里插入图片描述

注意:

当指定 bean 的某属性值为另一 bean 的实例时,通过 ref 指定它们间的引用关系,ref的值必须为某 bean 的 id 值。

3.3.1.2 第二种注入方式构造器注入(理解)
    <!--使用有参数构造进行注入 -->
    <bean id="student2" class="com.zwh.pojo.Student">
        <!--
            name:指定参数名称
            value:向属性注入的值
        -->
        <constructor-arg name="sname" value="李四"/>
        <constructor-arg name="sage" value="29"/>
    </bean>

注意:

index:指明该参数对应着构造器的第几个参数,从 0 开始。不过,该属性不要也行,但要注意,若参数类型相同,或之间有包含关系,则需要保证赋值顺序要与构造器中的参数顺序一致。

3.3.1.3 外部bean注入

在 service 调用 dao 里面的方法

    <!--service与dao之间的关联注入-->
    <bean id="userService2" class="com.zwh.service.UserServiceImpl">
        <!--注入 userDao 对象
            name 属性:类里面属性名称
            ref 属性:创建 userDao 对象 bean 标签 id 值
        -->
        <property name="userDao" ref="userDao"/>
    </bean>
    <!--userDao-->
    <bean id="userDao" class="com.zwh.dao.UserDao"/>
3.3.1.4 内部bean注入

在实体类之间表示一对多关系,员工表示所属部门,使用对象类型属性进行表示

<!--内部 bean-->
< bean  id= "emp"  class= "com.zwh.spring5.bean.Emp">
    <!--设置两个普通属性-->
    < property  name= "ename"  value= "lucy"></ property>
    < property  name= "gender"  value=" " 女" "></ property>
    <!--设置对象类型属性-->
    < property  name= "dept">
        < bean  id= "dept"  class= "com.zwh.spring5.bean.Dept">
            < property  name= "dname"  value=" " 安保部" "></ property>
        </ bean>
    </ property>
</ bean>
3.3.1.5 集合注入
  • 注入数组类型属性
  • 注入 List 集合类型属性
  • 注入 Map 集合类型属性

创建类,定义数组、list、map、set 类型属性,生成对应 set 方法:

@Data
public class Collect {
    private String[] str;
    private List<String> strings;
    private Set<String> mySet;
    private Map<String, Integer> myMap;
    private Properties myPro;
    }
  • 在 spring 配置文件进行配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--集合类型属性注入-->
    <bean id="collect" class="com.zwh.pojo.Collect">
        <!--数组注入-->
        <property name="str">
            <array>
                <value>java</value>
                <value>mysql</value>
            </array>
        </property>
        <!--list类型属性注入-->
        <property name="strings">
            <list>
                <value>张三</value>
                <value>李四</value>
            </list>
        </property>
        <!--set属性注入-->
        <property name="mySet">
            <set>
                <value>mysql</value>
                <value>redis</value>
            </set>
        </property>
        <!--map属性注入-->
        <property name="myMap">
            <map>
                <entry key="height" value="192"/>
                <entry key="weight" value="120"/>
            </map>
        </property>
        <!--为 Properties 注入值-->
        <property name="myPro">
            <props>
                <prop key="tel">119</prop>
                <prop key="address">江西</prop>
            </props>
        </property>
    </bean>
</beans>
  • 测试
    @Test
   // 测试集合注入
    public void test7() {
        // 指定spring配置文件的位置和名称
        String resource = "bean-coll.xml";
        // 创建spring容器的对象
        BeanFactory context = new ClassPathXmlApplicationContext(resource);
        // 从spring容器中获取对象,使用id
        Collect coll = (Collect) context.getBean("collect");
        System.out.println(coll);
    }
}
3.3.1.6 引用类型 属性自动注入

对于引用类型属性的注入,也可不在配置文件中显示的注入。可以通过为<bean/>标签设置 autowire 属性值,为引用类型属性进行隐式自动注入(默认是不自入引用类型属性)。

根据自动注入判断标准的不同,可以分为两种:

  • byName:根据名称自动注入

当配置文件中被调用者 bean 的 id 值与代码中调用者 bean 实体类的属性名相同时,可使用byName 方式,让容器自动将被调用者 bean 注入给调用者 bean。容器是通过调用者的 bean类的属性名与配置文件的被调用者 bean 的 id 进行比较而实现自动注入

<!--声明Student对象-->
    <bean id="student" class="com.zwh.pojo.Student">
        <property name="sname" value="zs"/>
        <property name="sage" value="10"/>
    </bean>

    <bean id="myUsers" class="com.zwh.pojo.User" autowire="byName">
        <property name="name" value="user"/>
        <property name="age" value="23"/>
        <!--传统set方式注入还需要引用Student的bean,但是使用byName,会自动查找-->
<!--        <property name="student" ref="student"/>-->
    </bean>
  • byType: 根据类型自动注入

使用 byType 方式自动注入。

    <!--声明Student对象-->
    <bean id="student1" class="com.zwh.pojo.Student">
        <property name="sname" value="zs"/>
        <property name="sage" value="10"/>
    </bean>

    <bean id="myUsers" class="com.zwh.pojo.User" autowire="byType">
        <property name="name" value="user"/>
        <property name="age" value="23"/>
    </bean>

要求:

配置文件中被调用者 bean 的 class 属性指定的类,要与代码中调用者 bean 类的某引用类型属性类型同源。

即要么相同,要么有 is-a 关系(子类,或是实现类)。但这样的同源的被调用 bean 只能有一个。多于一个,容器就不知该匹配哪一个了。

使用byType时IDEA爆红,但是不影响结果。

3.3.1.7 命名空间注(了解)

有对于设值注入与构造注入,在配置文件中,除了使用<property/><constructor-arg/>标签外,还可使用命名空间注入的方式,让注入的值以<bean/>标签属性的方式出现。

根据注入实现方式的不同,分为 p 命名空间注入与 c 命名空间注入。

p 命名空间注入:采用设值注入方式,故需要有相应的 setter

c 命名空间注入:采用构造注入方式,故需要有相应的构造器

  • p 命名空间注入

修改配置文件头,即添加相应约束xmlns:p="http://www.springframework.org/schema/p"

<bean id="cStudent" class="com.zwh.pojo.Student" p:sname="zszs" p:sage="110"></bean>
  • c 命名空间注入

修改配置文件头,即添加相应约束xmlns:c="http://www.springframework.org/schema/c"

 <bean id="cStudent" class="com.zwh.pojo.Student" c:sname="hello" c:sage="120"></bean>
3.3.1.8 其他注入
  • null 值注入
<!--null 值-->
< property  name= "address">
    < null/>
</property>
  • 特殊符号注入
<!--属性值包含特殊符号
    1 把<>进行转义 &lt; &gt;
    2 把带特殊符号内容写到 CDATA
-->
< property  name= "address">
    < value><![CDATA[<<南京>>]]></ value>
</ property>
**3.3.1.9 **SpEL

SPEL,Spring Expression Language,即 Spring EL 表达式语言。

在 Spring 配置文件中为 Bean 的属性注入值时,可直接使用 SPEL 表达式计算的结果。

SPEL 表达式以#开头,后跟一对大括号。用法:<bean id="abc" value="#{…}"/>

随机生成年龄:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="spelStu" class="com.zwh.pojo.Student">
        <property name="sname" value="张三"></property>
        <!--生成随机数的年龄-->
        <property name="sage" value="#{T(java.lang.Math).random()*30}"></property>
    </bean>
</beans>
  • 其他用法

1、<property name="school" value="#{mySchool}"/>引用另一个 bean。指定 school 的值为另一个 Bean 实例 mySchool。

2、<property name="schoolName" value="#{mySchool.name.toUpperCase()}"/>使用指定属性,并使用其方法。指定 schoolName 的值为 mySchool 的 name 属性值,并将其字母均转换为大写字母(toUpperCase()方法)。

3.3.1.10 引入外部属性文件

1、直接配置数据库信息

  • 配置德鲁伊连接池
<!--直接配置连接池-->
< bean  id ="dataSource"  class ="com.alibaba.druid.pool.DruidDataSource">
    < property  name ="driverClassName"  value ="com.mysql.jdbc.Driver"></ property>
    < property  name ="url" value ="jdbc:mysql://localhost:3306/userDb"></ property>
    < property  name ="username"  value ="root"></ property>
    < property  name ="password"  value ="root"></ property>
</ bean
  • 引入德鲁伊连接池依赖 jar 包

2、引入外部属性文件配置数据库连接池

  • 创建外部属性文件,properties 格式文件,写数据库信息

  • 把外部 properties 属性文件引入到 spring 配置文件中,需要引入 context 名称空间

<!--引入外部属性文件-->
< context :property- - placeholder  location ="classpath:jdbc.properties"/>

<!--配置连接池-->
< bean  id ="dataSource"  class ="com.alibaba.druid.pool.DruidDataSource">
    < property  name ="driverClassName"  value ="${prop.driverClass}"></ property>
    < property  name ="url"  value ="${prop.url}"></ property>
    < property  name ="username"  value ="${prop.userName}"></ property>
    < property  name ="password"  value ="${prop.password}"></ property>
</ bean>
3.3.1.11 为应用指定多个 Spring 配置文件

在实际应用里,随着应用规模的增加,系统中 Bean 数量也大量增加,导致配置文件变得非常庞大、臃肿。

为了避免这种情况的产生,提高配置文件的可读性与可维护性,可以将Spring 配置文件分解成多个配置文件。

包含关系的配置文件:

多个配置文件中有一个总文件,总配置文件将各其它子文件通过<import/>引入。在 Java代码中只需要使用总配置文件对容器进行初始化即可。

   父配置文件不能匹配 spring-*.xml 的格式,即不能起名为spring-total.xml。
   <import resource="classpath:com/zwh/spring-*.xml"/>
3.3.1.12 使用同类抽象 Bean 注入

当若干Bean实例同属于一个类,且这些实例的属性值又有相同值时,可以使用抽象Bean,以简化配置文件。

抽象 Bean 是用于让其它 bean 继承的。这个 bean 在 Bean 类中是不能通过 getBean 方法获取的。

设置 abstract 属性为 true 来指明该 bean 为抽象 bean, 默认值为 false。

不过,该bean 不为抽象 bean 时,也可被继承。 只不过,在应用中,用于被继承的 bean 一般为抽象bean。

在这里插入图片描述

3.3.1.12 使用异类抽象 Bean 注入

当若干不同的类对象具有相同的属性,且其值也相同时,可使用异类抽象 Bean。

在这里插入图片描述

3.3.2 基于注解的DI注入

对于 DI 使用注解,将不再需要在 Spring 配置文件中声明 bean 实例。Spring 中使用注解,需要在原有 Spring 运行环境基础上再做一些改变.

需要在 Spring 配置文件中配置组件扫描器,用于在指定的基本包中扫描注解。

1、什么是注解

  • 注解是代码特殊标记,格式:@注解名称(属性名称=属性值, 属性名称=属性值…)
  • 使用注解,注解作用在类上面,方法上面,属性上面
  • 使用注解目的:简化 xml 配置

2、Spring 针对 Bean 管理中创建对象提供注解

  • @Component
  • @Service(用于对 Service 实现类进行注解)
  • @Controller(用于对 Controller 实现类进行注解)
  • @Repository(用于对 DAO 实现类进行注解)

上面四个注解功能是一样的,都可以用来创建 bean 实例,@Repository,@Service,@Controller 是对@Component 注解的细化,标注不同层的对
象。即持久层对象,业务层对象,控制层对象。

3、基于注解方式实现对象创建

  • 引入spring-aop的依赖。spring内置spring-aop的依赖
        <!--spring依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.5.RELEASE</version>
        </dependency>
  • 开启组件扫描
 <!--声明组件扫描器(component-scan):指定注解所在的包-->
    <context:component-scan base-package="com.zwh"/>
  • 开启组件扫描细节配置
    <!--use-default-filters="false" 表示现在不使用默认 filter,使用自己配置的filter-->
    <context:component-scan base-package="com.zwh" use-default-filters="false">
        <!--filter:context:include-filter ,设置扫描哪些内容-->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
        <!--context:exclude-filter,设置哪些内容不进行扫描-->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

4、基于注解方式实现属性注入

  • @Autowired根据属性类型进行自动装配

    @Service
    public class UserService {
        //定义 dao 类型属性
        //不需要添加 set 方法
        //添加注入属性注解
            @Autowired
            private  UserDao  userDao; ;
            public void add() {
            System. out .println( "service add.......");
            userDao.add();
        }
    }
    
  • @Qualifier根据名称进行注入,这个@Qualifier 注解的使用,和上面@Autowired 一起使用

    @Service
    public class UserServiceImpl implements UserService {
        @Autowired
        @Qualifier("userDao")// 根据名称进行注入
        private UserDao userDao;
    }
    
  • @Resource:可以根据类型注入,也可以根据名称注入

    @Service
    public class UserServiceImpl implements UserService {
         // @Resource   根据类型进行注入
         @Resource(name = "userDao1");// 根据名称进行注入
         private UserDao userDao;
    }
    
  • @Value:注入普通类型属性

    @Component
    public class Student {
        @Value("zszs")
        private String sname;
        @Value("12350")
        private Integer sage;
    }
    

注意:

如果在上面注入了值,在setter方法上又注入了,后面注入的值会覆盖前面注入的值。

5、完全注解开发

  • 创建配置类,替代 xml 配置文件

    //作为配置类,替代 xml 配置文件
    @Configuration 
    // 组件扫描,指定扫描路径
    @ComponentScan(basePackages = { "com.zwh"})
    public class SpringConfig {
    
    }
    
  • 编写测试类

        @Test
        // 测试基于完全注解开发
        public void test3() {
            //加载配置类
            ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
            UserServiceImpl bean = context.getBean(UserServiceImpl.class);
            System.out.println(bean);
            bean.addUser();
        }
    

小结:

xml与注解最佳实践:

  • xml用来管理bean

  • 注解只用来完成属性的注入

  • 使用过程中,需要注意一个问题:让注解生效,就需要开启注解的支持!

    <!--指定要扫描的包,这个包下的注解就会生效-->
    <context:component-scan base-package="com.zwh"/>
    <!--开启注解支持-->
    <context:annotation-config/>
    

4 AOP 面向切面编程

4.1 AOP底层原理

  • 什么是AOP(面向切面编程)?

利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

通俗描述:不通过修改源代码方式,在主干功能里面添加新功能

  • 底层原理

第一种 有接口情况,使用 JDK 动态代理,创建接口实现类代理对象,增强类的方法

第二种 没有接口情况,使用 CGLIB 动态代理,创建子类的代理对象,增强类的方法

  • JDK动态代理的实现

使用 JDK 动态代理,使用 Proxy 类里面的方法创建代理对象,调用 newProxyInstance 方法。

方法有三个参数:

第一参数,类加载器

第二参数,增强方法所在的类,这个类实现的接口,支持多个接口

第三参数,实现这个接口 InvocationHandler,创建代理对象,写增强的部分

  • 创建接口,定义方法
public interface UserDao {
    public int add(int a,int b);

    public String update(String id);
}
  • 创建接口实现类,实现方法
public class UserDaoImpl implements UserDao{

    @Override
    public int add(int a, int b) {
        System.out.println("add-----");
        return a + b;
    }

    @Override
    public String update(String id) {
        System.out.println("update----");
        return id;
    }
}
  • 使用 Proxy 类创建接口代理对象
public class JDKProxy {
    public static void main(String[] args) {
        // 创建接口实现类的代理对象
        Class[] interfaces = {UserDao.class};
        UserDaoImpl userDao = new UserDaoImpl();
        UserDao dao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoImplProxy(userDao));
        int add = dao.add(1, 2);
        System.out.println("result:  " + add);
    }
}
// 创建代理对象代码,对象被创建,方法就会执行
class UserDaoImplProxy implements InvocationHandler {
    // 1 创建的是谁的代理对象,就把谁传递过来,有参构造传值
    private Object object;
    public UserDaoImplProxy(Object object) {
        this.object = object;
    }
    // 增强的逻辑
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 方法之前
        System.out.println("方法之前执行:  " + method.getName() + "" + "传递的参数:  " + Arrays.toString(args));
        // 被增强的方法执行
        Object res = method.invoke(object, args);
        // 方法之后
        System.out.println("方法之后执行:  " + object);
        return res;
    }
}
  • 测试,执行main方法
测试结果:
方法之前执行:  add传递的参数:  [1, 2]
add-----
方法之后执行:  com.zwh.doa.UserDaoImpl@6f94fa3e
result:  3

4.2 AOP专业术语

  • 连接点

类里面哪些方法可以被增强,这些方法被称为连接点。

  • 切入点

实际被真正增强的方法,称为切入点。

  • 通知(增强)

实际增强的逻辑部分称为通知。

通知分为:前置通知、后置通知、环绕通知、异常通知、最终通知

  • 切面

把通知应用到切入点的过程。

4.3 AOP准备工作

AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使用,进行 AOP 操作。

  • 相关依赖
        <!--spring依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>
  • 切入点表达式

切入点表达式作用:知道对哪个类里面的哪个方法进行增强

语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称] ([参数列表]) )

对 com.zwh.dao 包里面所有类,类里面所有方法进行增强

execution(* com.zwh.dao.*.* (..))

4.4 AOP的实现(AspectJ 注解)

  • 创建类,在类里面定义方法
// 被增强的类
@Component
public class User {
    public void add() {
//        int a = 10 / 0;
        System.out.println("执行了add方法~");
    }
}
  • 创建增强类(编写增强逻辑)
// 增强的类
@Component
// 生成代理对象
@Aspect
// 设置优先级
@Order(1)
public class UserProxy {
    // 相同切入点抽取
    @Pointcut(value = "execution(* com.zwh.aspectj_ano.User.add(..))")
    public void pointdemo() {

    }
    // 前置通知
    // @Before 注解表示作为前置通知
    @Before(value = "pointdemo()")
    public void before() {
        System.out.println("前置通知before.......");
    }


    //后置通知(返回通知)
    @AfterReturning(value = "execution(* com.zwh.aspectj_ano.User.add(..))")
    public void afterReturning() {
        System.out.println("后置通知AfterReturning.......");
    }

    //异常通知
    @AfterThrowing(value = "execution(* com.zwh.aspectj_ano.User.add(..))")
    public void afterThrowing() {
        System.out.println("异常通知afterThrowing.........");
    }

    //环绕通知
    @Around(value = "execution(* com.zwh.aspectj_ano.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕通知,环绕之前.........");
        //被增强的方法执行
        proceedingJoinPoint.proceed();
        System.out.println("环绕通知,环绕之后.........");
    }

    //最终通知
    @After(value = "execution(* com.zwh.aspectj_ano.User.add(..))")
    public void after() {
        System.out.println("最终通知after......");
    }
}
  • 进行通知的配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--开启注解扫描-->
    <context:component-scan base-package="com.zwh.aspectj_ano"/>

    <!--开启aspectj生成代理对象-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
  • 相同的切入点抽取
    // 相同切入点抽取
    @Pointcut(value = "execution(* com.zwh.aspectj_ano.User.add(..))")
    public void pointdemo() {

    }

	// 引用:    @Before(value = "pointdemo()")
  • 有多个增强类同一个方法进行增强,设置增强类优先级
@Component
@Aspect
@Order(1)
public class PersonProxy
    
// 在增强类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高
  • 完全使用注解开发
@Configuration
@ComponentScan(basePackages = {"com.atguigu"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop {
    
}

4.5 AOP的实现(AspectJ 配置文件)

  • 创建增强类
public class Book {
    public void buy() {
        System.out.println("buy.......");
    }
}
  • 创建被增强类
public class BookProxy {
    public void before() {
        System.out.println("before......");
    }
}
  • spring配置文件配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd 
       http://www.springframework.org/schema/aop 
       https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--创建对象-->
    <bean id="book" class="com.zwh.aspectjxml.Book"></bean>
    <bean id="bookProxy" class="com.zwh.aspectjxml.BookProxy"></bean>
    <!--配置 aop 增强-->
    <aop:config>
        <!--切入点-->
        <aop:pointcut id="p" expression="execution(* com.zwh.aspectjxml.Book.buy(..))"/>
        <!--配置切面-->
        <aop:aspect ref="bookProxy">
            <!--增强作用在具体的方法上-->
            <aop:before method="before" pointcut-ref="p"/>
        </aop:aspect>
    </aop:config>
</beans>

5 JdbcTemplate

Spring 框架对 JDBC 进行封装,使用 JdbcTemplate 方便实现对数据库操作

5.1 环境搭建

  • 导入相关依赖
<dependencies>
        <!--spring依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>5.3.5</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.12</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.2.9.RELEASE</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
  • 核心配置文件
<?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/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--开启组件扫描-->
    <context:component-scan base-package="com.zwh"/>
    <!--配置数据库连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          destroy-method="close">
        <property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=utf-8"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    </bean>
    <!--配置JdbcTemplate对象,注入 dataSource-->
    <!-- JdbcTemplate 对象 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--注入 dataSource-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
</beans>
  • 实体类
@Component
@Data
public class Student {
    private int id;
    private String name;
    private int age;
}
  • Dao
@Repository("stuDaoImpl")
public class StuDaoImpl {
    //注入 JdbcTemplate
    @Resource(name = "jdbcTemplate")
    private JdbcTemplate jdbcTemplate;
}
  • Service
@Service("stuServiceImpl")
public class StuServiceImpl {
    // 注入dao
    @Autowired
    @Qualifier("stuDaoImpl")
    private StuDaoImpl stuDao;
}
  • TestMain
public class TestMain {
    private JdbcTemplate jdbcTemplate;
    private ApplicationContext context = null;

    //    初始化连接
    {
        context = new ClassPathXmlApplicationContext("applicationContext.xml");
        jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");
    }  
}

5.2 JdbcTemplate操作数据库(添加)

  • dao
    // 添加的方法
    public void add(Student student) {
        // 1.创建sql语句
        String sql = "insert into student values(?,?,?)";
        System.out.println(sql);
        // 2.调用方法实现
        Object[] args = {student.getId(), student.getName(), student.getAge()};
        int update = jdbcTemplate.update(sql, args);
        System.out.println(update);
    }
  • service
public void addStu(Student student) {
        stuDao.add(student);
    }
  • test
    @Test
    // 测试添加方法
    public void test() {
        StuServiceImpl stuService = (StuServiceImpl) context.getBean("stuServiceImpl");
        stuService.addStu(new Student(104, "zf", 12));
        System.out.println("success");
    }

5.3 JdbcTemplate操作数据库(修改)

  • dao
    //修改方法
    public void update(Student student) {
        String sql = "update student set name = ?,age = ? where id = ?";
        Object[] args = {student.getName(), student.getAge(), student.getId()};
        int update = jdbcTemplate.update(sql, args);
        System.out.println(update);
    }
  • service
 public void updateStu(Student student) {
        stuDao.update(student);
    }
  • test
    @Test
    // 测试修改方法
    public void test2() {
        StuServiceImpl stuService = (StuServiceImpl) context.getBean("stuServiceImpl");
        stuService.updateStu(new Student(104, "张飞", 124));
        System.out.println("success");
    }

5.4 JdbcTemplate操作数据库(删除)

  • dao
    // 删除方法
    public void delete(int id) {
        String sql = "delete from student where id = ?";
        int update = jdbcTemplate.update(sql, id);
        System.out.println(update);
    }
  • service
public void deleteStu(int id) {
        stuDao.delete(id);
    }
  • test
    @Test
    // 测试删除方法
    public void test3() {
        StuServiceImpl stuService = (StuServiceImpl) context.getBean("stuServiceImpl");
        stuService.deleteStu(102);
        System.out.println("success");
    }

5.5 JdbcTemplate操作数据库(单条查询)

  • dao
    // 查询返回某个值
    public int selectCount() {
        String sql = "select count(*) from student";
        Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
        return count;
    }

    @Test
    // 测试返回对象
    public void test5() {
        StuServiceImpl stuService = (StuServiceImpl) context.getBean("stuServiceImpl");
        Student stu = stuService.findStu(5);
        System.out.println(stu);
    }
  • service
public int returnObj() {
        int i = stuDao.selectCount();
        return i;
    }

 public Student findStu(int id) {
        return stuDao.findStu(id);
    }
  • test
    @Test
    // 测试返回某个值
    public void test4() {
        StuServiceImpl stuService = (StuServiceImpl) context.getBean("stuServiceImpl");
        int i = stuService.returnObj();
        System.out.println(i);
        System.out.println("success");
    }

    // 查询返回对象
    public Student findStu(int id) {
        String sql = "select * from student where id = ?";
        // 调用方法
        Student student = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Student.class), id);
        return student;
    }

5.6 JdbcTemplate操作数据库(返回集合对象)

  • dao
    // 查询返回集合
    public List<Student> findAllStu() {
        String sql = "select * from student";
        List<Student> query = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Student.class));
        return query;
    }
  • service
 public List<Student> findAll() {
        return stuDao.findAllStu();
    }
  • test
    @Test
    // 测试返回集合
    public void test6() {
        StuServiceImpl stuService = (StuServiceImpl) context.getBean("stuServiceImpl");
        List<Student> all = stuService.findAll();
        for (Student student : all) {
            System.out.println(student);
        }
    }

5.7 JdbcTemplate操作数据库(批量添加)

  • dao
    // 批量添加数据
    public void batchAdd(List<Object[]> batchArgs) {
        String sql = "insert into student values(?,?,?)";
        int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
        System.out.println(Arrays.toString(ints));
    }
  • service
    public void batchAdd(List<Object[]> args) {
        stuDao.batchAdd(args);
    }
  • test
    @Test
    // 测试批量添加
    public void test7() {
        StuServiceImpl stuService = (StuServiceImpl) context.getBean("stuServiceImpl");
        List<Object[]> args = new ArrayList<>();
        Object[] o1 = {8, "zs1", 12};
        Object[] o2 = {9, "zs2", 12};
        Object[] o3 = {10, "zs3", 12};
        Object[] o4 = {11, "zs4", 12};
        args.add(o1);
        args.add(o2);
        args.add(o3);
        args.add(o4);
        // 调用批量添加
        stuService.batchAdd(args);
    }

5.8 JdbcTemplate操作数据库(批量修改)

  • dao
    // 批量修改数据
    public void batchUpd(List<Object[]> batchArgs) {
        String sql = "update student set name = ?,age = ? where id = ?";
        int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
        System.out.println(Arrays.toString(ints));
    }
  • service
public void batchUpd(List<Object[]> args) {
        stuDao.batchUpd(args);
    }
  • test
    @Test
    // 测试批量修改
    public void test8() {
        StuServiceImpl stuService = (StuServiceImpl) context.getBean("stuServiceImpl");
        List<Object[]> args = new ArrayList<>();
        Object[] o1 = {"zs11", 122,8};
        Object[] o2 = {"zs22", 122,9};
        Object[] o3 = {"zs33", 122,10};
        Object[] o4 = {"zs44", 122,11};
        args.add(o1);
        args.add(o2);
        args.add(o3);
        args.add(o4);
        // 调用批量添加
        stuService.batchUpd(args);
    }

5.9 JdbcTemplate操作数据库(批量删除)

  • dao
    // 批量删除数据
    public void batchDel(List<Object[]> batchArgs) {
        String sql = "delete from student where id = ?";
        int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
        System.out.println(Arrays.toString(ints));
    }
  • service
public void batchDel(List<Object[]> args) {
        stuDao.batchDel(args);
    }
  • test
    @Test
    // 测试批量删除
    public void test9() {
        StuServiceImpl stuService = (StuServiceImpl) context.getBean("stuServiceImpl");
        List<Object[]> args = new ArrayList<>();
        Object[] o1 = {8};
        Object[] o2 = {9};
        Object[] o3 = {10};
        Object[] o4 = {11};
        args.add(o1);
        args.add(o2);
        args.add(o3);
        args.add(o4);
        // 调用批量添加
        stuService.batchDel(args);
    }

6 Spring整合Mybatis

6.1 导入相关依赖

<dependencies>
        <!--spring依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.3.5</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.1</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.9</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.12</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
        <resources>
            <resource>
                <directory>src/main/java</directory><!--所在的目录-->
                <includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

6.2 定义实体类

@Data
public class Student {
    private Integer id;
    private String name;
    private Integer age;
}

6.3 定义dao接口

@Repository
public interface StudentMapper {

    int insertStudent(Student student);

    int updateStudent(Student student);

    int deleteStudent(int id);

    Student selectStudentById(int id);

    List<Student> selectAllStudent();
}

6.4 定义mapper映射文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zwh.dao.StudentMapper">

    <insert id="insertStudent" parameterType="student">
        insert into student(name,age) values(#{name},#{age});
    </insert>

    <update id="updateStudent" parameterType="student">
        update student set name=#{name},age=#{age} where id=#{id}
    </update>

    <delete id="deleteStudent">
        delete from student where id=#{id}
    </delete>

    <select id="selectStudentById" resultType="student">
        select * from student where id=#{id}
    </select>

    <select id="selectAllStudent" resultType="student">
        select * from student order by id desc
    </select>

</mapper>

6.5 定义service接口

public interface IStudentService {

    int addStu(Student student);

    int updStu(Student student);

    int delStu(int id);

    Student selectById(int id);

    List<Student> selectAll();
}

6.6 定义service实现类

@Service("studentService")
public class StudentServiceImpl implements IStudentService {
    @Resource
    private StudentMapper studentMapper;

    @Override
    public int addStu(Student student) {
        return studentMapper.insertStudent(student);
    }

    @Override
    public int updStu(Student student) {
        return studentMapper.updateStudent(student);
    }

    @Override
    public int delStu(int id) {
        return studentMapper.deleteStudent(id);
    }

    @Override
    public Student selectById(int id) {
        return studentMapper.selectStudentById(id);
    }

    @Override
    public List<Student> selectAll() {
        return studentMapper.selectAllStudent();
    }
}

6.7 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>
    <settings>
        <!--设置mybatis输出日志-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

    <typeAliases>
        <package name="com.zwh.pojo"/>
    </typeAliases>

</configuration>

6.8 spring核心配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           https://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.zwh"/>
    <!--引入数据库配置文件-->
    <context:property-placeholder location="classpath:db.properties"/>
    <!--配置阿里的数据库连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <!--注册 SqlSessionFactoryBean-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!--指定mybatis主配置文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>
    <!--定义 Mapper 扫描配置器 MapperScannerConfigurer-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <!--指定基本扫描包,即dao接口包-->
        <property name="basePackage" value="com.zwh.dao"/>
    </bean>
</beans>

6.9 测试文件

public class MyTest {
    @Test
    // 测试插入方法
    public void test1() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        StudentServiceImpl bean = (StudentServiceImpl) context.getBean("studentService");
        System.out.println(bean);
        int addStu = bean.addStu(new Student("鲁智深", 49));
        System.out.println(addStu);
    }

    @Test
    // 测试修改方法
    public void test2() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        StudentServiceImpl bean = (StudentServiceImpl) context.getBean("studentService");
        int updStu = bean.updStu(new Student(7, "杨迪", 12));
        System.out.println(updStu);
    }

    @Test
    // 测试删除方法
    public void test3() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        StudentServiceImpl bean = (StudentServiceImpl) context.getBean("studentService");
        int delStu = bean.delStu(9);
        System.out.println(delStu);
    }


    @Test
    // 测试单条查询方法
    public void test4() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        StudentServiceImpl bean = (StudentServiceImpl) context.getBean("studentService");
        Student student = bean.selectById(8);
        System.out.println(student);
    }

    @Test
    // 测试查询所有数据方法
    public void test5() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        StudentServiceImpl bean = (StudentServiceImpl) context.getBean("studentService");
        List<Student> students = bean.selectAll();
        for (Student student : students) {
            System.out.println(student);
        }
    }
}

小结:

  • spring整合mybatis后,sqlsession交由spring托管
  • spring核心配置文件配置了MapperScannerConfigurer后,不需要在mybatis配置文件中注册mapper。
  • 使用@service、@repository等注解需要开启扫描包。<context:component-scan base-package="com.zwh"/>

6 spring事务

6.1 什么是事务

  • 把一组业务当成一个业务来做;要么都成功,要么都失败!
  • 事务是指一组sql语句的集合

6.2 事务的四大特性(ACID)

  • 原子性(Atomicity)

原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚。

因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。

  • 一致性(Consistency)

一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。

拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,

转几次账事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。

  • 隔离性(Isolation)

隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。

即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始。

这样每个事务都感觉不到有其他事务在并发地执行。

  • 持久性(Durability)

持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

例如我们在使用JDBC操作数据库时,在提交事务方法后,提示用户事务操作完成,当我们程序执行完成直到看到提示后,就可以认定事务以及正确提交。

即使这时候数据库出现了问题,也必须要将我们的事务完全执行完成,否则就会造成我们看到提示事务处理完毕

但是数据库因为故障而没有执行事务的重大错误。

6.2 Spring中事务隔离级别和传播行为

Spring中事务隔离级别:

这些常量均是以 ISOLATION_开头。即形如 ISOLATION_XXX。

隔离级别使得多事务操作之间不会产生影响。不考虑隔离性产生很多问题,脏读、不可重复读、虚(幻)读。

脏读:一个未提交事务读取到另一个未提交事务的数据

不可重复读:一个未提交事务读取到另一提交事务修改数据

虚读:一个未提交事务读取到另一提交事务添加数据

隔离级别含义
ISOLATION_DEFAULT使用后端数据库默认的隔离级别。
ISOLATION_READ_UNCOMMITTED读未提交,允许读取尚未提交的更改。可能导致脏读、幻影读或不可重复读。
ISOLATION_READ_COMMITTED读已提交,允许从已经提交的并发事务读取。可防止脏读,但幻影读和不可重复读仍可能会发生。oracle默认使用
ISOLATION_REPEATABLE_READ可重复读,对相同字段的多次读取的结果是一致的,除非数据被当前事务本身改变。可防止脏读和不可重复读,但幻影读仍可能发生。
ISOLATION_SERIALIZABLE串行化,完全服从ACID的隔离级别,确保不发生脏读、不可重复读和幻影读。这在所有隔离级别中也是最慢的,因为它通常是通过完全锁定当前事务所涉及的数据表来完成的。

Spring中事务传播行为:

事务传播行为常量都是以 PROPAGATION_ 开头,形如 PROPAGATION_XXX。

传播行为含义
REQUIRED业务方法需要在一个事务中运行,如果方法运行时,已处在一个事务中,那么就加入该事务,否则自己创建一个新的事务,这是spring默认的传播行为。
SUPPORTS如果业务方法在某个事务范围内被调用,则方法成为该事务的一部分,如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行。
MANDATORY只能在一个已存在事务中执行,业务方法不能发起自己的事务,如果业务方法在没有事务的环境下调用,就抛异常.
REQUIRES_NEW业务方法总是会为自己发起一个新的事务,如果方法已运行在一个事务中,则原有事务被挂起,新的事务被创建,直到方法结束,新事务才结束,原先的事务才会恢复执行
NOT_SUPPORTED声明方法需要事务,如果方法没有关联到一个事务,容器不会为它开启事务.如果方法在一个事务中被调用,该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行
NEVER声明方法绝对不能在事务范围内执行,如果方法在某个事务范围内执行,容器就抛异常.只有没关联到事务,才正常执行.
NESTED如果一个活动的事务存在,则运行在一个嵌套的事务中.如果没有活动的事务,则按REQUIRED属性执行.它使用了一个单独的事务,,这个事务拥有多个可以回滚的保证点,内部事务回滚不会对外部事务造成影响, 它只对DataSourceTransactionManager 事务管理器起效

6.3 Spring 事务管理介绍

1、事务添加到 JavaEE 三层结构里面 Service 层(业务逻辑层)

2、在 Spring 进行事务管理操作,有两种方式:编程式事务管理(了解)和声明式事务管理

3、声明式事务管理:基于注解方式、基于 xml 配置文件方式

4、在 Spring 进行声明式事务管理,底层使用 AOP 原理

5、Spring 事务管理 API,提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类

通过@Transactional 注解方式,可将事务织入到相应 public 方法中,实现事务管理。

@Transactional 的可选属性:

属性说明
propagation用于设置事务传播属性。该属性类型为 Propagation 枚举,默认值为Propagation.REQUIRED。
isolation用于设置事务的隔离级别。该属性类型为 Isolation 枚举,默认值为<br/Isolation.DEFAULT。
readOnly用于设置该方法对数据库的操作是否是只读的。该属性为 boolean,默认值 为 false。
timeout用于设置本操作与数据库连接的超时时限。单位为秒,类型为 int,默认值为-1,即没有时限
rollbackFor指定需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若只有 一个异常类时,可以不使用数组。
rollbackForClassName指定需要回滚的异常类类名。类型为 String[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组
noRollbackFor指定不需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组
noRollbackForClassName指定不需要回滚的异常类类名。类型为 String[],默认值为空 数组。当然,若只有一个异常类时,可以不使用数组

6.4 事务操作(注解声明式事务管理)

  • spring核心配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
       http://www.springframework.org/schema/context
       http://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/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--开启组件扫描-->
    <context:component-scan base-package="com.zwh"/>
    <!--配置数据库连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          destroy-method="close">
        <property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=utf-8"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    </bean>
    <!--配置JdbcTemplate对象,注入 dataSource-->
    <!-- JdbcTemplate 对象 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--注入 dataSource-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--创建事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--开启事务注解-->
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
  • service 类上面(或者 service 类里面方法上面)添加事务注解
@Service
@Transactional
public class UserServiceImplImprove {
    @Autowired
    private UserDaoImpl userDao;

    //转账的方法
    public void accountMoney() {
            userDao.reduceMoney();
            int b = 10 / 0;
            System.out.println("lucy转了100给Mary");
            //mary 多 100
            userDao.addMoney();
            System.out.println("Mary收到Lucy的100");
    }
}

6.5 使用AspectJ的AOP配置管理事务( 掌握)

  • 导入相关依赖
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>
  • spring核心配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/context
       http://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/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--开启组件扫描-->
    <context:component-scan base-package="com.zwh"/>
    <!--配置数据库连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          destroy-method="close">
        <property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=utf-8"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    </bean>
    <!--配置JdbcTemplate对象,注入 dataSource-->
    <!-- JdbcTemplate 对象 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--注入 dataSource-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--创建事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--配置通知-->
    <tx:advice id="txadvice">
        <!--配置事务参数-->
        <tx:attributes>
            <!--指定哪种方法上面添加事务-->
            <tx:method name="accountMoney" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
    <!--配置切入点和切面-->
    <aop:config>
        <!--配置切入点-->
        <aop:pointcut id="point" expression="execution(* com.zwh.service.UserServiceImplxml.*(..))"/>
        <!--配置切面-->
        <aop:advisor advice-ref="txadvice" pointcut-ref="point"/>
    </aop:config>
</beans>

6.6 事务操作(完全注解声明式事务管理)

  • 配置类
@Configuration //配置类
@ComponentScan(basePackages = "com.zwh") //组件扫描
@EnableTransactionManagement //开启事务
public class TxConfig {
    //创建数据库连接池
    @Bean
    public DruidDataSource getDruidDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql:///user_db");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }

    //创建 JdbcTemplate 对象
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
        //到 ioc 容器中根据类型找到 dataSource
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        //注入 dataSource
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    //创建事务管理器
    @Bean
    public DataSourceTransactionManager
    getDataSourceTransactionManager(DataSource dataSource) {
        DataSourceTransactionManager transactionManager = new
                DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}

7 Spring新特性

7.1 log4j2实现日志功能

  • 导入相关依赖
<dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.6.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.6.2</version>
        </dependency>
        <dependency>     <!-- 桥接:告诉Slf4j使用Log4j2 -->
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.6.2</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId> <!-- 桥接:log4j1使用Log4j2 也支持其他实现到log4j的桥接,引入不同的jar包即可-->
            <artifactId>log4j-over-slf4j</artifactId>
            <version>1.7.12</version>
        </dependency>
        <!-- 使用异步写日志功能 必须引入此包-->
        <dependency>
            <groupId>com.lmax</groupId>
            <artifactId>disruptor</artifactId>
            <version>3.3.6</version>
        </dependency>
  • 在resources下创建log4j2.xml文件
<?xml version="1.0" encoding="UTF-8"?>

<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE >
ALL -->
<!--Configuration 后面的 status 用于设置 log4j2 自身内部的信息输出,可以不设置,
当设置成 trace 时,可以看到 log4j2 内部各种详细输出-->
<configuration status="INFO">
    <!--先定义所有的 appender-->
    <appenders>
        <!--输出日志信息到控制台-->
        <console name="Console" target="SYSTEM_OUT">
            <!--控制日志输出的格式-->
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-
5level %logger{36} - %msg%n"/>
        </console>
    </appenders>
    <!--然后定义 logger,只有定义 logger 并引入的 appender,appender 才会生效-->
    <!--root:用于指定项目的根日志,如果没有单独指定 Logger,则会使用 root 作为
   默认的日志输出-->
    <loggers>
        <root level="info">
            <appender-ref ref="Console"/>
        </root>
    </loggers>
</configuration>

7.2 junit相关

7.2.1 spring整合Junit4
  • 导入相关jar
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.2.5.RELEASE</version>
        </dependency>
  • 创建测试类,使用注解完成
// 单元测试框架
@RunWith(SpringJUnit4ClassRunner.class)
// 加载配置文件
@ContextConfiguration("classpath:applicationContext2.xml")
public class TestJunit4 {
    @Autowired
    private StuServiceImpl stuService;

    @Test
    public void test1() {
        List<Student> all = stuService.findAll();
        for (Student student : all) {
            System.out.println(student);
        }
    }
}
7.2.2 spring整合Junit5
  • 导入相关jar
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.7.1</version>
            <scope>test</scope>
        </dependency>
  • 创建测试类
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class TestJunit5 {
    @Autowired
    private StuServiceImpl stuService;

    @Test
    public void test1() {
        List<Student> all = stuService.findAll();
        for (Student student : all) {
            System.out.println(student);
        }
    }
}
  • 使用复合注解
@SpringJUnitConfig(locations = "classpath:applicationContext2.xml")
public class TestJunit5 {
    @Autowired
    private StuServiceImpl stuService;

    @Test
    public void test1() {
        List<Student> all = stuService.findAll();
        for (Student student : all) {
            System.out.println(student);
        }
    }
}

8 基于spring实现邮件发送

  • 导入相关jar
        <!-- Spring 邮件发送 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>4.3.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.mail</groupId>
            <artifactId>mail</artifactId>
            <version>1.4.7</version>
        </dependency>
  • 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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="sender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
        <property name="host" value="smtp.qq.com"></property>
        <property name="username" value="3313221637@qq.com"></property>
        // 授权码
        <property name="password" value="*****"></property>
        <property name="defaultEncoding" value="UTF-8"></property>
        <property name="javaMailProperties">
            <props>
                <prop key="mail.smtp.auth">true</prop>
            </props>
        </property>
    </bean>

</beans>
  • 获得授权码

在这里插入图片描述

发送短信:

在这里插入图片描述

注意:

如若修改了QQ密码,那么授权码需要重新获取。

  • 测试
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;

public class SpringSendEmail {

    public static void main(String[] args) throws MessagingException {
        //1.跟SpringBeanFactory要一个对象
        BeanFactory factory = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        JavaMailSender sender = (JavaMailSender)factory.getBean("sender");//JavaMailSenderImpl
        //2.利用这个sender创建一个邮件对象
        MimeMessage message = sender.createMimeMessage();
        //3.发送这个邮件  找小弟帮忙
        MimeMessageHelper helper = new MimeMessageHelper(message);//是刚才那个Session和Transport的合体
        //4.需要告知helper 发给谁 发什么
        helper.setFrom("3313221637@qq.com");//设置发送的地址
        helper.setTo("1102918572@qq.com");//设置收件人地址
        helper.setSubject("基于Spring的新主题");
        helper.setText("噗呲噗呲切克切克muamua啊~");//验证的地址(新请求 认证成功)
        //5.直接发送消息
        sender.send(message);
        System.out.println("基于Spring发送成功");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值