Spring初始

以下内容如有侵权请联系删除

Spring第一天

第一章 JavaEE三层架构

在这里插入图片描述

第二章 IOC思想推导(难点)

之前的代码逻辑

  • servlet中需要调用service : UserService userService = new UserServicelmpl()

耦合关系 代码片段中的依赖关系(如上,UserServicelmpl与UserService耦合关系太强,如何验证代码片段的耦合关系的强弱(依赖关系),例如将UserServicelmpl.java文件删除,看是否会报编译异常,如果会报编译异常,说明耦合关系强;如果不报编译异常,说明没有耦合关系;如何解决这种耦合关系,通过反射或者配置文件)解耦:

反射,配置文件,面向接口编程(创建工厂类的实质就是利用反射和配置文件)

2.1 简化版本

(1)创建工程导入依赖

    <dependencies>
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6</version>
        </dependency>
    </dependencies>

(2)配置dao接口
在这里插入图片描述
(3)创建BeanFactory工具类

package cn.itcast.utils;


public class BeanFactory {

    /**
     * 根据传入的参数名,反射创建对象
     * @param name
     * @return
     * @throws Exception
     */
    public static Object getBean(String name) throws Exception{
        Class<?> aClass = Class.forName(name);
        return aClass.newInstance();
    }

}

2.2 升级版本

(1)创建配置文件
在这里插入图片描述
(2)在BeanFactory解析配置文件

package cn.itcast.utils;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 单例工厂:多次调用getBean方法,获取的对象是同一个
 *  1、当程序第一次访问BeanFactory,解析Bean.xml配置文件,创建对象
 *  2、当调用getBean方法的时候,从map集合中直接获取目标对象
 */

public class BeanFactory {

    /**
     * map存放:
     *      userDao = UserDaoImpl对象
     */
    private static Map<String,Object> map=new HashMap<String,Object>();

    static{
        try {
            //1.获取配置文件的输入流
            InputStream is = BeanFactory.class.getClassLoader().getResourceAsStream("bean.xml");
            //2.创建SAXReader对象
            SAXReader reader = new SAXReader();
            //3.获取Document对象
            Document document = reader.read(is);
            //4.获取所有的bean节点
            Element root = document.getRootElement(); //获取跟节点
            List<Element> bean = root.elements("bean");
            //5.解析bean节点,获取id和class属性
            for (Element element : bean) {
                String id = element.attributeValue("id");  //userDao
                String cls = element.attributeValue("class");  //全限定类名
                Class<?> aClass = Class.forName(cls);
                Object obj = aClass.newInstance();
                //6.存入map集合
                map.put(id, obj);
            }
        } catch (Exception e) {

        }
    }

    /**
     * 根据传入的参数名,反射创建对象
     * @param key
     * @return
     * @throws Exception
     */
    public static Object getBean(String key) throws Exception {
        return map.get(key);
    }

}

在这里插入图片描述

2.3 总结

通过两种方式完成了对象的对象(userDaolmpl)

  • 最简单的版本:通过手动反射创建对象(手动new对象)
  • 第二个版本:所需的目标对象,从map集合(容器)中获取(控制反转:IOC)

ioc:对象的获取方式,由传统的主动实例化对象〈new对象),变为从容器(map)集合中获取

第三章 Spring概述和控制反转

3.1 什么是Spring

  • Spring是于2003年兴起的一个full-stack轻量级的Java开源框架,由Rod Johnson创建
  • Spring以IOC(控制反转)和AOP(面向切面编程)为核心
  • Spring提供了展现层Spring MVC、持久层Spring JDBC、业务层事务管理等众多的企业级应用技术
  • Spring还能整合开源世界众多的第三方框架和类库,逐渐成为使用最多的ava EE企业应用开源框架

在这里插入图片描述

3.2 认识IOC

IOC(控制反转)不是什么技术,而是一种设计思想。它的目的是指导我们设计出更加松耦合的程序。

  • 控制:指的是控制权,在java中可以简单理解为对象的控制权限(比如对象的创建、销毁等权限)
  • 反转:指的是将对象的控制权由原来的程序员在类中主动控制反转到由Spring容器来控制。
  • 对象的创建交由Spring容器管理,需要对象从容器中获取即可
  • 主要功能解耦
  • IOC的底层原理:反射

举个例子:找对象

  • 传统方式:自己找,想要啥样的自己去大街上找(new) ,主动出击
  • l0C方式:婚介所,首先人们将自己的信息注册到婚介所。然后,等你想要对象的时候,婚介所就会帮你找到,然后给你送来。

第四章 SpringIOC的入门案例(重点)

4.1 入门案例

创建工程导入依赖

    <dependencies>
        <!--spring的坐标-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>
        <!--单元测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

创建Dao接口和实现类
在这里插入图片描述
创建Spring配置文件

  • spring配置文件约定俗称::applicationContext.xml
  • spring配置文件,需要引入名称空间(约束)
  • 在spring的配置文件中通过标签,定义对象id和实现类全类名

在resource目录下创建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">

    <!--创建对象
        id:容器中对象的唯—标识(唯—别名)
        class:实现类的全限定类名,在springIoc容器中,会通过全类名,反射创建对象
    -->
    <bean id="userDao" class="cn.itcast.dao.impl.UserDaoImpl"></bean>

</beans>

测试
在这里插入图片描述

4.2 执行过程分析

在这里插入图片描述

4.3 Spring的API介绍

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

第五章 对象的生命周期

5.1 对象作用域

    <!--创建对象的作用域
        scope :作用
            singleton :单例对象
                容器创建的时候,反射创建对象存入map集合中
            prototype:多例对象
                容器创建的时候,不会反射创建此对象
                当调用容器的getBean方法的时候,反射创建一个新的对象
    -->

    <bean id="userDao" class="cn.itcast.dao.impl.UserDaoImpl" scope="prototype"></bean>

5.2 生命周期

<!--对象的生命周期(对象和创建和销毁过程),和对象的作用域息息相关
            init-method : 对象实例化之后,自动执行的对象方法
            destroy-method:容器关闭,对象被销毁之前,自动执行的对象方法
        singleton(单例对象)
            1、容器创建(加载配置文件app1icationContext.xm1文件时,容器创建),自动的反射创建UserDaoImp1对象存入容器
            2、自动的执行init-method中配置的对象方法
            3、容器关闭时,对象销毁之前自动的执行destroy-method配置的对象方法
        prototype(多例对象)
            1、当调用一次getBean方法,创建一次对象
            2、当对象创建,调用init-method中配置的对象方法
            3、多例对象(没有存在容器中),自动的进行垃圾回收
    -->
    <bean id="userDao" class="cn.itcast.dao.impl.UserDaoImpl" scope="prototype"
          init-method="initMethod" destroy-method="destoryMethod"></bean>

第六章 依赖注入(重点)

依赖注入:Dependency lnjection (Dl)。它是spring框架核心ioc的具体实现。我们的程序在编写时,通过控制反转,把对象的创建交给了spring,但是代码中不可能出现没有依赖的情况。比如我们的Book中可能引入一个Publish类,在使用了Spring之后,它会为我们解决这些依赖对象的注入。

本质:向对象中的私有属性赋值

构造方法

set万法调用

6.1 构造方法注入

向对象添加有参构造方法

package cn.itcast.dao.impl;

import cn.itcast.dao.UserDao;

public class UserDaoImpl implements UserDao {

    private String username;

    private Integer age;
    
    public UserDaoImpl(){
        
    }

    //1.配置有参构造,在new对象的时候,直接对属性赋值
    public UserDaoImpl(String username,Integer age) {
        this.username = username;
        this.age = age;
    }


    public void save() {
        System.out.println("调用dao保存用户");
    }
}

在spring的配置文件中,通过bean标签配置对象创建(需要添加构造方法参数)

    <!--
        相当于调用无参构造方法,实例化 UserDaoImpl
            new UserDaoImpl();
    构造方法注入参数:
        在bean标签中,通过constructor-arg配置构造方法参数
        new UserDaoImpl("王者荣耀",12);
    -->
    <bean id="userDao" class="cn.itcast.dao.impl.UserDaoImpl">
        <!--配置构造参数
            index:指定参数的索引定位,从0开始
            type:通过参数的类型定位,Integer
            name:通过参数的名称定位,username
                以上三个属性,适用于定位构造参数(三选一就可以)

            value:对基本数据类型的数据赋值(8个基本数据类型+String)
            ref:对java实体类对象赋值
                以上两个属性,适用于对参数赋值
        -->
        <constructor-arg name="username" value="王者荣耀" ></constructor-arg>
        <constructor-arg name="age" value="12" ></constructor-arg>
    </bean>

6.2 set方法注入

提供属性的set方法

package cn.itcast.dao.impl;

import cn.itcast.dao.UserDao;

public class UserDaoImpl implements UserDao {

    private String username;

    private Integer age;

    public UserDaoImpl(){
        
    }

    //1.配置有参构造,在new对象的时候,直接对属性赋值
    public UserDaoImpl(String username,Integer age) {
        this.username = username;
        this.age = age;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public void save() {
        System.out.println("调用dao保存用户");
    }
}

在spring配置文件中,通过bean结合property配置set方法调用

    <!--set方法注入
            通过bean标签结合property标签配置对象创建之后,自动执行的set方法
                UserDao userDao = new UserDaoImpl();
                userDao.setUsername();
                userDao.setAge();
            property标签
                name :定位待执行的set方法
                value:对基本数据类型的属性赋值
                ref :对java实体类对象赋值
    -->
    <bean id="userDao" class="cn.itcast.dao.impl.UserDaoImpl">
        <property name="username" value="LOL"></property>
        <property name="age" value="15"></property>
    </bean>

6.3 注入复杂类型(集合)

(1)注入数组类型

配置set方法
在这里插入图片描述
spring配置
在这里插入图片描述
(2)注入kv数据

java对象
在这里插入图片描述
配置文件
在这里插入图片描述

6.4 注入对象(重点)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

第七章 配置文件的模块化(了解)

我们现在的配置都集中配在了一个applicationContext.xml文件中,当开发人员过多时,如果所有bean都配置到同一个配置文件中,会使这个文件巨大,而且也不方便维护。针对这个问题,Spring提供了多配置文件的方式,也就是所谓的配置文件模块化。

1、并列的多个配置文件
直接编写多个配置文件,比如说beans1.xml,beans2.xm......然后在创建ApplicationContext的时候,直接传入多个配置文件。

ApplicationContext act=new ClassPathXmlApplicationContext("beans1.xml","beans2.xml","...");

2、主从配置文件
先陪一个主配置文件,然后在里面导入其它的配置文件。

    <import resource="beans1.xml"/>
    <import resource="beans2.xml"/>

注意事项:

  • 同一个xml文件中不能出现相同名称的bean,如果出现会报错
  • 多个xml文件如果出现相同名称的bean,不会报错,但是后加载的会覆盖前加载的bean,所以企业开发中尽量保证bean的名称是唯一的。

Spring第二天

第一章 DbUtils(会用)

1.1 DbUtils介绍

DbUtils是Apache的一款用于简化Dao代码的工具类,它底层封装了JDBC技术。

核心类:

  • QueryRunner用于执行增删改查的SQL语句
  • ResultSetHandler这是一个接口,主要作用是将数据库返回的记录封装进实体对象-------查询数据封装,结果集处理器

核心方法:

  • update()用来执行增、删、改语句executeUpate
  • query()用来执行查询语句executeQuery
//1.创建datasourcexxX
//2.创建QueryRunner
QueryRunner queryRunner = new QueryRunner(datasource); //update方法,用于执行增删改语句
//第一个参数:sq1语句后面的参数:sq1语句中的所需要的的值
queryRunner.update("insert into account value(nu71,?,?)",1,2);
//query方法,用于执行查询语句
//第一个参数:sq1语句―第一个参数:封装返回值―后面的参数:sq1语句中的所需要的的值
//BeanHandler用于将一条返回数据封装成一个JavaBean,类似的子类还有BeanListHandler等
queryRunner. query("select * from account where aid = ?", new BeanHandler<Account>(Account.class),1);

1.2 DbUtils基本使用

准备数据库环境

create table account(
	id int primary key auto_increment,
	name varchar(100) not null unique,
	money float(10,2)
)

创建工程导入依赖

    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <!--druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.15</version>
        </dependency>
        <!--dbutils-->
        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.7</version>
        </dependency>
        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

    </dependencies>

创建实体类

package cn.itcast.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Account {
    private Integer id;
    private String name;
    private Double money;
}

保存

    /**
     * 保存账户数据
     * @throws Exception
     */
    @Test
    public void test() throws Exception {
        //1.创建DataSource
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql:///heima31");
        //2.创建QueryRunner对象(通过构造参数,传入datasource)
        QueryRunner queryRunner = new QueryRunner(dataSource);
        //3.调用QueryRunner方法完成数据库操作(update, query)
        queryRunner.update("insert into account(name , money) values (?,?)","小张",2);
    }

根据名称查询

/**
 * 根据名称查询账户
 */
@Test
public void testQueryByName() throws Exception {
    //1、创建DataSource
    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setUsername("root");
    dataSource.setPassword("root");
    dataSource.setDriverClassName("com.mysql.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql:///heima31");
    //2、创建QueryRunner对象 (通过构造参数,传入datasource)
    QueryRunner queryRunner = new QueryRunner(dataSource);
    //3、调用QueryRunner方法完成数据库操作(update,query)
    /**
     * 结果集处理器:
     *      BeanHandler :处理返回值仅有一条数据,将查询结果封装为唯一的java对象
     *      BeanListHandler:处理返回值有多条记录,将查询结果封装为List<>对象
     * 语法:
     *      new BeanHandler<封装的对象类型>(返回对象.class)
     */
    Account account = queryRunner.query("select * from account where name=?",
            new BeanHandler<Account>(Account.class), "小张");//sql语句,结果集处理器,sql语句的参数(不是必须)
    System.out.println(account);
}

查询所有

/**
 * 查询列表记录
 */
@Test
public void testQueryAll() throws Exception {
    //1、创建DataSource
    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setUsername("root");
    dataSource.setPassword("root");
    dataSource.setDriverClassName("com.mysql.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql:///heima31");
    //2、创建QueryRunner对象 (通过构造参数,传入datasource)
    QueryRunner queryRunner = new QueryRunner(dataSource);
    //3、调用QueryRunner方法完成数据库操作(update,query)
    /**
     * 结果集处理器:
     *      BeanHandler :处理返回值仅有一条数据,将查询结果封装为唯一的java对象
     *      BeanListHandler:处理返回值有多条记录,将查询结果封装为List<>对象
     * 语法:
     *      new BeanHandler<封装的对象类型>(返回对象.class)
     */

    List<Account> list = queryRunner.query("select * from account", new BeanListHandler<Account>(Account.class));

    for (Account account : list) {
        System.out.println(account);
    }
}

第二章 基于XML配置数据库操作(重点)

在这里插入图片描述

2.1 环境搭建

创建工程导入坐标

<dependencies>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <!--druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.15</version>
        </dependency>
        <!--dbutils-->
        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.7</version>
        </dependency>
        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!--spring-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>


        <!--spring-junit-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>
    </dependencies>

创建实体

package cn.itcast.domain;

public class Account {

    private Integer id;

    /**
     * 账户名称
     */
    private String name;

    /**
     * 账户金额
     */
    private Float money;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Float getMoney() {
        return money;
    }

    public void setMoney(Float money) {
        this.money = money;
    }
}

创建dao接口

package cn.itcast.dao;

import cn.itcast.domain.Account;

import java.util.List;

public interface AccountDao {

    /**
     * 1、保存账户
     * @param account
     */
    public void saveAccount(Account account);

    /**
     * 2、更新账户
     * @param account
     */
    public void updateAccount(Account account);

    /**
     * 3、根据名称查询账户
     */
    public Account findByName(String name);

    /**
     * 4、查询所有账户
     * @return
     */
    public List<Account> findA1l();

    /**
     * 5、根据i d删除账户
     * @param id
     */
    public void de1eteById(Integer id) ;


}

创建dao接口实现类

package cn.itcast.dao.impl;

import cn.itcast.dao.AccountDao;
import cn.itcast.domain.Account;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

import java.sql.SQLException;
import java.util.List;

/**
 * 在dao层中,使用QueryRunner完成数据库操作
 */
public class AccountDaoImpl implements AccountDao {

    /**
     * 依赖属性
     */
    private QueryRunner queryRunner;

    public void setQueryRunner(QueryRunner queryRunner) {
        this.queryRunner = queryRunner;
    }

    /**
     * 保存
     * @param account
     */
    public void saveAccount(Account account) {
        try {
            queryRunner.update("insert into account(name,money) values(?,?)", account.getName(),account.getMoney());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据用户名称更新金额
     * @param account
     */
    public void updateAccount(Account account) {
        try {
            queryRunner.update("update account set money=? where name=?", account.getMoney(),account.getName());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据名称查询
     * @param name
     * @return
     */
    public Account findByName(String name) {
        try {
            return queryRunner.query("select * from account where name=?", new BeanHandler<Account>(Account.class),name);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 查询所有
     * @return
     */
    public List<Account> findAll() {
        try {
            return queryRunner.query("select * from account", new BeanListHandler<Account>(Account.class));
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 删除
     * @param id
     */
    public void deleteById(Integer id) {
        try {
            queryRunner.update("delete from account where id=?",id);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}


创建service接口

package cn.itcast.service;

import cn.itcast.domain.Account;

import java.util.List;

public interface AccountService {

    /**
     * 1、保存账户
     * @param account
     */
    public void saveAccount (Account account);

    /**
     * 2、更新账户
     * @param account
     */
    public void updateAccount(Account account) ;

    /**
     * 3、根据名称查询账户
     * @param name
     * @return
     */
    public Account findByName (String  name) ;

    /**
     * 4、查询所有账户
     * @return
     */
    public List<Account> findAll();

    /**
     * 5、根据id删除账户
     * @param id
     */
    public void deleteById(Integer id);


}

创建service接口实现类

package cn.itcast.service.impl;

import cn.itcast.dao.AccountDao;
import cn.itcast.domain.Account;
import cn.itcast.service.AccountService;

import java.util.List;

public class AccountServiceImpl implements AccountService {

    /**
     * 依赖属性(依赖注入)
     */
    private AccountDao accountDao;
    
    /**
     * 用于property的set注入
     * @param accountDao
     */
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    /**
     * 保存
     * @param account
     */
    public void saveAccount(Account account) {
        accountDao.saveAccount(account);
    }
    
    /**
     * 更新
     * @param account
     */
    public void updateAccount(Account account) {
        accountDao.updateAccount(account);
    }

    /**
     * 根据名称查询
     * @param name
     * @return
     */
    public Account findByName(String name) {
        return accountDao.findByName(name);
    }

    /**
     * 查询全部
     * @return
     */
    public List<Account> findAll() {
        return accountDao.findAll();
    }

    /**
     * 根据id删除
     * @param id
     */
    public void deleteById(Integer id) {
        accountDao.deleteById(id);
    }
}


2.2 Spring配置(重点)

在这里插入图片描述

2.3 测试

    /**
     * 调用Userservice完成数据库CRUD操作
     * @throws Exception
     */
    @Test
    public void testOne() throws Exception {
        //1、创建spring容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

        //2、从容器中获取对象
        AccountService accountService = (AccountService)ac.getBean( "accountService");

        List<Account> list = accountService.findAll();

        for (Account account : list) {
            System.out.println(account);
        }
    }

第三章 Spring中的常见注解

3.1 环境搭建

(1)案例拷贝
在这里插入图片描述
(2)xml配置文件删除bean配置
使用注解替换bean标签的配置,我们将applicationContext.xml中的bean标签删除

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">

</beans>

(3)xml中加入包扫描

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">

    <!--包扫描:扫描指定包下的所有java类,扫描类中的Spring注解,帮助我们自动的创建对象存入容器-->
    <!--扫描路径:当前包及子包-->
    <context:component-scan base-package="cn.itcast"></context:component-scan>

</beans>

包扫描:扫描指定包下的所有java类,扫描类中的Spring注解,帮助我们自动的创建对象存入容器

3.2 对象创建的注解

语法规则

创建对象交给spring容器管理,语义话的形式代码分层
	<bean id="userDao" class="cn.itcast.dao.impl. userDaoImpl"></bean>
	@Component(组件):
	@Controller:在web层使用
	@Service:在service层
	@Repository:在dao层使用

当spring容器启动时,根据包扫描配置自动的扫描到@Component注解,反射创建注解标注的对象,存入容器

默认存入容器的id(唯一标识)=当前类名首字母小写(userDaoImpl)

value属性:自定义容器中对象的唯一标识

java代码如下

@Repository(value="userDao")
public class UserDaoImpl implements UserDao{
	public void save(){
		System.out.println("调用dao11111完成保存");
	}
}

3.3 生命周期的注解

语法规则

对象的生命周期的注解
	@Scope:配置到类上,生命对象的生命周期
			singleton:单例(默认)
			prototype:多例
	@PostConstruct:相当于xml中的init-method
			对象初始化方法:配置到对象的方法上
	@PreDestory:相当于xml中的destory-method
			对象的销毁方法:配置到对象的方法上

java代码如下

@Repository(value = "userDao1")
@Scope("prototype")
public class UserDaoImpl implements UserDao {

    public void save() {
        System.out.println("调用dao11111保存用户");
    }

    /**
     * 初始化方法
     */
    @PostConstruct
    public void initMethod(){
        System.out.println("对象执行了init方法");
    }

    /**
     * 销毁方法
     */
    @PreDestroy
    public void destoryMethod(){
        System.out.println("对象执行了destory方法");
    }
}

3.4 依赖注入的注解

在这里插入图片描述
@Autowired
在这里插入图片描述
在这里插入图片描述
@Reource
在这里插入图片描述
@value
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.5 注解总结

使用注解编程之前,必须执行包扫描
在这里插入图片描述

第四章 注解结合XML完成案例(重点)

注解结合XML:
自定义的java对象通过注解配置(service, dao)---service,repository,autowired
第三方对象通过XML配置(QueryRunner,DataSource)

4.1 环境搭建

在这里插入图片描述

4.2 持久层代码

在这里插入图片描述

4.3 业务层代码

在这里插入图片描述

4.4 XML配置

在这里插入图片描述

第五章 第五章 Spring案例之纯注解版(了解)

注解版:使用注解配置第三方对象交给spring容器管理
Bean

5.1 开发步骤

环境搭建
在这里插入图片描述

删除XML配置文件

新建配置类

package cn.itcast.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

/**
 * 通过SpringConfig替换之前的xml配置项
 * 1、声明配置类,在类上配置一个注解
 *  @Configuration
 * 2、开启包扫描
 *  @ComponentScan(basePackages = "cn.itcast")
 *      basePackages: 指定需要扫描的包
 * 3、将第三方jar包对象,创建并交给容器管理
 *  @Bean
 */
@Configuration
@ComponentScan(basePackages = "cn.itcast")
public class SpringConfig {

    @Bean
    public DataSource getDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        //四元素
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql:///heima31");
        return dataSource;
    }


    //创建QueryRunner
    @Bean
    public QueryRunner getQueryRunner(DataSource dataSource) {
        //dataSource : 从容器中获取的对象
        QueryRunner queryRunner = new QueryRunner(dataSource);
        return queryRunner;
    }

}

新的注解
@Configuration---声明配置
@ComponentScan---指定包扫描
@Bean---创建第三方jar包中的对象
在这里插入图片描述

测试
在这里插入图片描述

5.2 案例优化

从外部加载配置文件
(1)准备properties文件

jdbc.username=root
jdbc.password=root
jdbc.driver=com.mysql.jdbc. Driver
jdbc.url=jdbc :mysql: ///heima31

(2)通过注解将此文件交给spring容器管理

@Propertysource(value="jdbc.properties ")

(3)通过@Value,获取文件中的属性,赋值到变量中

package cn.itcast.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import javax.sql.DataSource;

/**
 * 通过SpringConfig替换之前的xml配置项
 * 1、声明配置类,在类上配置一个注解
 *  @Configuration
 * 2、开启包扫描
 *  @ComponentScan(basePackages = "cn.itcast")
 *      basePackages: 指定需要扫描的包
 * 3、将第三方jar包对象,创建并交给容器管理
 *   @Bean
 * 4、将properties配置文件,交给spring容器管理
 *   @PropertySource("jdbc.properties")
 *      value : properties路径
 */
@Configuration
@ComponentScan(basePackages = "cn.itcast")
@PropertySource(value="jdbc.properties")
public class SpringConfig {

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    @Value("${jdbc.driver}")
    private String driver;

    @Value("${jdbc.url}")
    private String url;

    /**
     * @Bean : 创建第三方jar包对象,交给容器管理
     * 语法:
     *      1、@Bean注解需要配置到方法上
     *      2、方法需要返回值
     *      3、在Spring容器启动的时候,自动的扫描所有配置了@Bean的方法
     *      4、自动执行被@Bean扫描的方法,将返回值存入Spring容器
     *      5、如果方法需要参数,Spring会从容器中根据类型获取对象,再调用
     *  在@Bean标注的方法中,可以配置依赖的属性参数
     *      spring会从容器中获取到依赖的对象,自动调用方法
     */
    @Bean
    public DataSource getDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        //四元素
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        return dataSource;
    }


    //创建QueryRunner
    @Bean
    public QueryRunner getQueryRunner(DataSource dataSource) {
        //dataSource : 从容器中获取的对象
        QueryRunner queryRunner = new QueryRunner(dataSource);
        return queryRunner;
    }
}

模块化
配置类臃肿,Spring支持多配置类(配置类的模块)
拆分配置类SpringConfig,添加一个新的子配置类JdbcConfig

package cn.itcast.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;

import javax.sql.DataSource;

public class JdbcConfig {
    
    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    @Value("${jdbc.driver}")
    private String driver;

    @Value("${jdbc.url}")
    private String url;

    /**
     * @Bean : 创建第三方jar包对象,交给容器管理
     * 语法:
     *      1、@Bean注解需要配置到方法上
     *      2、方法需要返回值
     *      3、在Spring容器启动的时候,自动的扫描所有配置了@Bean的方法
     *      4、自动执行被@Bean扫描的方法,将返回值存入Spring容器
     *      5、如果方法需要参数,Spring会从容器中根据类型获取对象,再调用
     *  在@Bean标注的方法中,可以配置依赖的属性参数
     *      spring会从容器中获取到依赖的对象,自动调用方法
     */
    @Bean
    public DataSource getDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        //四元素
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        return dataSource;
    }
}

在主配置类中,通过Import引入其他的配置类

package cn.itcast.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import javax.sql.DataSource;

@Configuration
@ComponentScan(basePackages = "cn.itcast")
@PropertySource(value="jdbc.properties")
//引入其他的配置类
@Import(value=JdbcConfig.class)
public class SpringConfig {
    
	//创建QueryRunner
    @Bean
    public QueryRunner getQueryRunner(DataSource dataSource) {
        //dataSource : 从容器中获取的对象
        QueryRunner queryRunner = new QueryRunner(dataSource);
        return queryRunner;
    }
}

5.3 新注解总结

在这里插入图片描述

第六章 Spring整合单元测试(会用)

当在单元测试中,点击run的时候,底层工作的其实是一个运行器,默认是ParentRunner.这个运行器是junit提供的,它是不认识Spring的环境.这也就意味着,它无法从spring的容器中获取bean.

如果想要从Spring的容器中获取对象,那就必须先认识Spring环境.这时候,Spring提供了一个运行器,这个运行器就认识Spring环境,也就可以获取对象了

6.1 导入坐标

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>


<!--spring-junit 整合单元测试-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.1.6.RELEASE</version>
</dependency>

6.2 配置注解

package cn.itcast.test;

import cn.itcast.domain.Account;
import cn.itcast.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * 1、指定单元测试环境:指定spring提供的单元测试环境
 *      @RunWith(SpringJUnit4ClassRunner.class)
 * 2、指定spring的容器配置信息
 *      @ContextConfiguration
 *          locations : 配置文件路径
 *          classes : 配置类
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class AccountJunitTest {

    @Autowired
    private AccountService accountService;

    //测试保存
    @Test
    public void testInsert() {
        //3、调用方法保存
        Account account = new Account();
        account.setMoney(100.0);
        account.setName("小李1");
        accountService.saveAccount(account);
    }
}

Spring第三天

第一章 转账案例(练习)

介绍:通过spring结合serivce,dao,dbuitils完成转账工程

转入账户:加款

转出账户:扣款

配置:采用流行的xml+注解的方式

效果:张三,向李四转账100元

1.1 代码开发

准备数据环境(略)

创建工程导入坐标

在这里插入图片描述
编写domain
在这里插入图片描述
编写dao接口
在这里插入图片描述
编写dao实现
在这里插入图片描述
编写service接口
在这里插入图片描述
编写service实现
在这里插入图片描述
加入spring的配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans.xsd
             http://www.springframework.org/schema/context
             http://www.springframework.org/schema/context/spring-context.xsd">

    <!--开启包扫描-->
    <context:component-scan base-package="cn.itcast"></context:component-scan>

    <!--配置queryrunner-->
    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>

    <!--配置datasource-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql:///spring"></property>
    </bean>
</beans>

测试

import cn.itcast.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class AccountServiceTest {

    @Autowired
    private AccountService accountService;

    @Test
    public void testTransfer() {
        accountService.transfer("小张","小李",1f);
    }

}

1.2 问题分析

在这里插入图片描述

1.3 解决思路

1想办法让同一个业务中的所有sql使用同一个connection

2想办法禁止自动提交,然后手动控制事务的提交和回滚

1.4 传统解决方案

service层代码

package cn.itcast.service.impl;

import cn.itcast.dao.AccountDao;
import cn.itcast.domain.Account;
import cn.itcast.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    /**
     *  已经配置好了数据源 datasource,可以通过datasource获取一个连接
     */
    @Autowired
    private DataSource dataSource;


    //转账:转出账户名,转入账户名,转账金额
    public void transfer(String sourceName, String targetName, Double money) throws SQLException {

        //获取数据库连接
        Connection connection = dataSource.getConnection();

        //开启事务
        connection.setAutoCommit(false);

        try {
            //1、根据账户名称,查询两个账户
            Account sourceAccount = accountDao.findByName(connection,sourceName); //转出
            Account targetAccount = accountDao.findByName(connection,targetName); //转入
            //2、对于转出账户,扣款
            sourceAccount.setMoney(sourceAccount.getMoney() - money);
            //3、对于转入账户,加款
            targetAccount.setMoney(targetAccount.getMoney() + money);
            //4、更新转出账户和转入账户到数据库中
            accountDao.update(connection,sourceAccount);
            int i = 1/0;
            accountDao.update(connection,targetAccount);
            //提交事务
            connection.commit();
        }catch (Exception e) {
            e.printStackTrace();
            //回滚事务
            connection.rollback();
        }finally {
            //释放资源
            connection.close();
        }
    }
}

dao层代码
在这里插入图片描述
在这里插入图片描述
传统的解决方法,随着业务代码的不断增多,参数会越来越多且传递复杂

1.5 Threadlocal

Threadlocal:将数据绑定到当前线程上,同一个线程中。进过的不同方法都可以从Threadlocal获取数据,并且获取的数据是同一个对象

Threadlocal使用方法很简单

大致意思就是ThreadLocal提供了线程内存储变量的能力,这些变量不同之处在于每一个线程读取的变量是对应的互相独立的。通过get和set方法就可以得到当前线程对应的值。

ThreadLocal<T> sThreadLocal=new ThreadLocal<T>();
//将数据绑定到当前线程
sThreadLocal.set();
//从当前线程中获取数据
sThreadLocal.get();

在这里插入图片描述

第二章 开发事务管理器

2.1 代码开发

复制一个工程
在这里插入图片描述
编写事务管理器(copy)

package cn.itcast.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

//事务管理器
@Component
public class TxManager {

    @Autowired
    private DataSource dataSource;

    //准备好本地存储Connection对象的ThreadLocal
    private ThreadLocal<Connection> th = new ThreadLocal<Connection>();

    //获取Connection对象
    public Connection getConnection() throws SQLException {
        Connection connection = th.get();
        if (connection == null){
            connection = dataSource.getConnection();
            th.set(connection);
        }
        return connection;
    }

    //开启
    public void begin(){
        try {
            getConnection().setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    //提交
    public void commit(){
        try {
            getConnection().commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    //回滚
    public void rollback(){
        try {
            getConnection().rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    //关闭
    public void close(){
        try {
            getConnection().close();
            th.remove();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

修改dao
在这里插入图片描述
修改service
在这里插入图片描述

2.2 问题分析

现在的事务代码和业务代码严重耦合在一起了,我们希望的是这样
在不改动原来业务代码的前提下,给代码添加事务管理功能
即:在不修改源代码的情况下,给代码增强功能

2.3 解决思路

动态代理

第三章 动态代理优化转账

动态代理:在不改变源代码的前提下,对功能进行增强(对指定类中方法进行业务增强)

  • java代码中只需要重点关注业务逻辑即可
  • 增强部分内容,通过动态代理添加

动态代理在目前两种实现方式

  • jdk动态代理
  • cglib动态代理

3.1 jdk动态代理

在这里插入图片描述
复制工程
在这里插入图片描述
制作被代理对象
在这里插入图片描述
制作增强功能
在这里插入图片描述
产生代理对象(JDK)

package cn.itcast.test;

import cn.itcast.service.AccountService;
import cn.itcast.utils.TxManager;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AccountServiceTest {

    @Autowired
    private AccountService accountService;

    @Autowired
    private TxManager txManager;

    @Test
    public void testTransfer() {
        // 产生目标对象(注入)

        // 编写代理逻辑
        InvocationHandler invocationHandler = new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object obj = null;
                try {
                    //开启事务
                    txManager.begin();

                    //调用目标对象的方法
                    obj = method.invoke(accountService, args);

                    //提交事务
                    txManager.commit();
                } catch (Exception e) {
                    e.printStackTrace();
                    //回滚事务
                    txManager.rollback();
                } finally {
                    txManager.close();
                }

                return obj;
            }
        };

        // 创建代理对象
        AccountService instance = (AccountService) Proxy.newProxyInstance(
                accountService.getClass().getClassLoader(),
                accountService.getClass().getInterfaces(),
                invocationHandler

        );

        //让代理对象去工作
        instance.transfer("B01", "B02", 10.0);
    }
}

3.2 cglib动态代理(了解)

在这里插入图片描述
复制工程
在这里插入图片描述
去掉接口相关所有代码
只需要删除AccountService接口即可(注意修改实现类)

使用cglib的方式创建代理对象

package cn.itcast.test;

import cn.itcast.service.impl.AccountServiceImpl;
import cn.itcast.utils.TxManager;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.InvocationHandler;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.lang.reflect.Method;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AccountServiceTest {

    @Autowired
    private AccountServiceImpl accountService;

    @Autowired
    private TxManager txManager;

    @Test
    public void testTransfer() {
        // 产生目标对象(注入)

        // 编写代理逻辑
        InvocationHandler invocationHandler = new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object obj = null;
                try {
                    //开启事务
                    txManager.begin();

                    //调用目标对象的方法
                    obj = method.invoke(accountService, args);

                    //提交事务
                    txManager.commit();
                } catch (Exception e) {
                    e.printStackTrace();
                    //回滚事务
                    txManager.rollback();
                } finally {
                    txManager.close();
                }

                return obj;
            }
        };

        // 使用cglib的方式创建代理对象
        // 1 创建增强器
        Enhancer enhancer = new Enhancer();

        // 2 设置父类
        enhancer.setSuperclass(AccountServiceImpl.class);

        // 3  设置代理逻辑
        enhancer.setCallback(invocationHandler);

        // 4 产生代理对象
        AccountServiceImpl instance = (AccountServiceImpl) enhancer.create();

        //5 让代理对象去工作
        instance.transfer("B01", "B02", 10.0);
    }
}

3.3 jdk和cglib两种代理方式的选择(面试)

首先明确在创建代理实现类时, jdk的速度要高于cglib,所以选择的时候:
当被代理类有接口的时候,使用jdk动态代理,因为它的效率高
当被代理类没有接口的时候,使用cglib动态代理,因为没办法

3.4 总结

当核心业务(转账)和增强业务(事务)同时出现时,我们可以在开发时对他们分别开发,运行时再组装在一起(使用动态代理的方式)。这样做的好处是:

1.逻辑清晰,开发核心业务的时候,不必关注增强业务的代码
2.代码复用性高:增强代码不用重复书写
这就是一种AOP的思想。

我的总结:
开发阶段分离开发,运行阶段组装运行

第四章 AOP概述

在这里插入图片描述

4.1 概念

AOP(面向切面编程)是一种思想,它的目的就是在不修改源代码的基础上,对原有功能进行增强.

Spring AOP是对AOP思想的一种实现,Spring底层同时支持jdk和cglib动态代理.

Spring会根据被代理的类是否有接口自动选择代理方式:

如果有接口,就采用jdk动态代理(当然,也可以强制使用cglib)

没有接口就采用cglib的方式

4.2 术语(难点)

目标对象--target:被代理的对象

连接点--jointPoint:被代理对象中的所有方法

切入点--pointCut:被增强的方法

增强(通知)
advice: 一个具体的增强功能

通知分为5种类型:

前置通知,后置通知,异常通知,最终通知,环绕通知

代理对象--proxy :生成的动态代理对象

切面
aspect

切面是一种描述,描述了一件事:一个什么样的功能添加到了哪个切入点的什么位置上

切面=切点+增强

第五章 SpringAOP的入门案例(重点)

在AccountDaolmpl类中的方法上打印日志

5.1 思路分析

目标对象(target) ----AccountDaolmpl类

被增强方法(pointcut) --- AccountDaolmpl类中的方法

增强功能(advice) ----打印日志

切面配置(aspect)-切点+增强日志在目标对象中的哪个方法的哪个位置上执行

5.2 代码开发

创建工程,引入坐标

<dependencies>
        <!--spring aop-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>

        <!--切点表达式的解析坐标-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>

        <!--test-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

创建AccountDao的接口和实现类
在这里插入图片描述
在这里插入图片描述
开发增强功能
在这里插入图片描述
基于XML配置切面
切面类需要交给Spring容器管理

通过aop:config开启AOP配置

(1)配置切入点(被增强的类和方法)
(2)配置切面
在这里插入图片描述

第六章 SpringAOP配置详解

6.1 切入点表达式

在这里插入图片描述

6.2 四大通知

四大通知描述的就是增强方法在切点方法的什么位置上执行
前置通知(before):在切点运行之前执行
后置通知(after-returning):在切点正常运行结束之后执行
异常通知(after-throwing):在切点发生异常的时候执行
最终通知(after):在切点的最终执行

try{
	前置通知(before) :在切点运行之前执行
	
	//切点执行,被代理对象方法调用

	后置通知(after-returning):在切点正常运行结束之后执行

}catch(Exception e){

	异常通知(after-th rowing):在切点发生异常的时候执行

}finally{

	最终通知(after):在切点的最终执行

}

方法
在这里插入图片描述
配置
在这里插入图片描述
由于多个通知类的配置顺序不同,导致不一样样的执行效果!!!

6.3 环绕通知

它是一种特殊的通知,他允许你以编码的形式实现四大通知(和手动定义动态代理类似)

方法
在这里插入图片描述
配置
在这里插入图片描述

6.4 AOP工作原理

开发阶段分别开发运行阶段组装运行

开发阶段
开发共性功能,制作成增强

开发非共性功能,制作成切点

在配置文件中,声明切点与增强间的关系,即切面

容器启动阶段
Spring读取配置文件中的切面信息,根据切面中的描述,
将增强功能增加在目标对象的切点方法上,动态创建代理对象
最后将经过代理之后对象放入容器中(存入容器的是动态代理对象!!!!!)

Spring第四天

第一章 SpringAOP注解版(重点)

AOP注解版
基于XML结合注解的配置方式(重点)
基于纯注解的配置方式(了解)

1.1 基于XML结合注解的配置

环境准备
在这里插入图片描述
XML配置文件
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    <!--
        基于XML结合注解配置
            IOC的注解:开启包扫描
                自定义的java对象,通过注解配置
                第三方jar包对象,通过XML配置
            AOP的注解:开启AOP注解的支持
                在切面类方法上,通过注解的形式配置AOP

         * 开启IOC的包扫描,开启AOP的自动代理
         * 在切面类中完成
            * 声明切面类
            * 配置AOP通知类型
    -->
    <!--包扫描-->
    <context:component-scan base-package="cn.itcast"></context:component-scan>

    <!--开启AOP注解的支持,开启自动代理-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

修改dao
在这里插入图片描述
修改Logger切面类
在这里插入图片描述

package cn.itcast.dao.utils;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * Logger : 切面类,增强类
 * 基于注解的形式配置AOP
 *    1、声明切面类 (Spring知道此类中具有增强通知方法)
 *      @Aspect
 *    2、在增强通知方法上,通过注解配置通知类型
 *      @before:前置通知
 *      @afterReturning:后置通知
 *      @afterThrowing:异常通知
 *      @after:最终通知
 *      @around:环绕通知
 */
@Component
@Aspect
public class Logger {


    /**
     * 前置通知:在被代理对象方法之前,增强日志
     */
    @Before(value="execution(* cn.itcast.dao.impl.*.*(..))")
    public void before() {
        System.out.println("执行前置通知:before方法");
    }

    /**
     * 后置通知:被代理对象方法正常执行,获取返回值之后
     */
    @AfterReturning(value="execution(* cn.itcast.dao.impl.*.*(..))")
    public void afterReturining() {
        System.out.println("执行后置通知:afterReturining");
    }

    /**
     * 异常通知:调用过程中抛出异常时执行
     */
    @AfterThrowing(value="execution(* cn.itcast.dao.impl.*.*(..))")
    public void afterThrowing() {
        System.out.println("执行异常通知:afterThrowing");
    }

    /**
     * 最终通知:在最终代码块中需要执行的逻辑
     */
    @After(value="execution(* cn.itcast.dao.impl.*.*(..))")
    public void after() {
        System.out.println("执行最终通知:after");
    }


    //环绕通知
    public Object around(ProceedingJoinPoint pjp) throws Throwable {

        Object obj = null;

        try {
            System.out.println("执行前置通知");
            obj = pjp.proceed(); //执行被代理对象的方法
            System.out.println("执行后置通知");
        }catch (Exception e) {
            System.out.println("执行异常通知");
        }finally {
            System.out.println("执行最终通知");
        }

        return obj;
    }
}

四大通知类型的问题
通过注解配置四大通知类型,存在小BUG。执行顺序和XML配置不一致。在正常企业开发中不用

环绕通知(重点)
在这里插入图片描述

1.2 基于纯注解的配置(了解)

环境准备
在这里插入图片描述
编写配置类
在这里插入图片描述

第二章 SpringAOP实现事务管理

SpringAOP实现事务管理:基于AOP完成转账案例,通过AOP配置事务增强

2.1 基于XML的AOP事务配置

四个通知类型配置

(1)工程准备,导入依赖
在这里插入图片描述

<dependencies>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <!--druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.15</version>
        </dependency>
        <!--dbutils-->
        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.7</version>
        </dependency>
        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!--spring ioc依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>

        <!--spring-junit 整合单元测试-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>

        <!--切点表达式的解析坐标-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>
    </dependencies>

(2)修改AccountServiceImpl
在这里插入图片描述
在这里插入图片描述
(3)修改AccountDaoImpl
在这里插入图片描述
(4)配置切面类
在这里插入图片描述
(5)配置AOP
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       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
             http://www.springframework.org/schema/beans/spring-beans.xsd
             http://www.springframework.org/schema/context
             http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--对象创建IOC-->
    <bean id="accountService" class="cn.itcast.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>

    <bean id="accountDao" class="cn.itcast.dao.impl.AccountDaoImpl">
        <property name="queryRunner" ref="queryRunner"></property>
        <property name="txManager" ref="txManager"></property>
    </bean>

    <bean id="txManager" class="cn.itcast.utils.TxManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>


    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql:///heima31"></property>
    </bean>


    <!--AOP配置-->
    <aop:config>
        <!--切入点表达式 : 查找需要增强的方法-->
        <aop:pointcut id="pt" expression="execution(* cn.itcast.service.impl.*.*(..))"/>
        <!--配置切面-->
        <aop:aspect ref="txManager">
            <!--前置通知-->
            <aop:before method="begin" pointcut-ref="pt"></aop:before>
            <!--后置通知-->
            <aop:after-returning method="commit" pointcut-ref="pt"></aop:after-returning>
            <!--异常通知-->
            <aop:after-throwing method="rollback" pointcut-ref="pt"></aop:after-throwing>
            <!--最终通知-->
            <aop:after method="close" pointcut-ref="pt"></aop:after>
        </aop:aspect>
    </aop:config>
</beans>

借助环绕通知配置(重点)

(1)切面类添加环绕通知方法
在这里插入图片描述
(2)XML中配置环绕通知
在这里插入图片描述

2.2 基于XML结合注解的AOP事务配置

搭建环境
在这里插入图片描述
(1)Dao层代码
在这里插入图片描述
(2)Service层代码
在这里插入图片描述
配置
在这里插入图片描述
(1)XML配置文件
在这里插入图片描述
(2)切面类配置
在这里插入图片描述

第三章 JdbcTemplate(会用)

3.1 JdbcTemplate介绍

在这里插入图片描述
在这里插入图片描述
方法介绍

package cn.itcast.test;

import cn.itcast.domain.Account;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

public class JdbcTemplateTest {

    /**
     * 练习JdbcTemplate的基本使用
     */
    public static void main(String[] args) {
        //1、创建DataSource
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql:///heima31");
        //2、创建JdbcTemplate对象
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        //3、调用方法完成数据库操作
        //3.1 保存,删除(update)
        //jdbcTemplate.update("insert into account (name,money) values (?,?)" ,"小王",100f);

        //3.2 查询数据列表(query)
        //BeanPropertyRowMapper : 结果集处理器 (查询列表和唯一的时候,都使用同一个)
//        List<Account> list = jdbcTemplate.query("select * from account", new BeanPropertyRowMapper<Account>(Account.class));
//        for (Account account : list) {
//            System.out.println(account);
//        }

        //3.3 根据id查询唯一的对象( queryForObject)
        Account account = jdbcTemplate.queryForObject("select * from account where id=?", new BeanPropertyRowMapper<Account>(Account.class), 1);
        System.out.println(account);
    }
}

3.2 JdbcTemplate案例

使用dbcTemplate完成一个crud和转账的案例,使用xml结合注解的形式配置

(1)准备数据环境

和之前的数据库一模一样,账户操作

(2)创建工程,导入坐标

<dependencies>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <!--druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.15</version>
        </dependency>
        <!--spring-jdbc-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>
        <!--spring-context-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>
        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!--spring-test-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>

        <!--添加切点表达式-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>
    </dependencies>

(3)创建实体类
在这里插入图片描述
(4)创建dao接口
在这里插入图片描述
(5)创建dao实现
在这里插入图片描述
(6)创建service接口
在这里插入图片描述
(7)创建service实现
在这里插入图片描述
(8)加入Spring的配置文件
在这里插入图片描述
(9)测试

package cn.itcast.test;

import cn.itcast.domain.Account;
import cn.itcast.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class AccountServiceTest {

    @Autowired
    private AccountService accountService;
    
    @Test
    public void testFindAll() {
        List<Account> list = accountService.findAll();
        for (Account account : list) {
            System.out.println(account);
        }
    }
    
    @Test
    public void testTransfer() throws Exception {
        accountService.transfer("小张", "小李",1f);
    }

}

第四章 Spring中的事务管理(了解)

Spring中的事务管理:根据SpringAOP拓展出来的事务控制功能

4.1 事务管理方式

Spring支持两种事务管理方式:编程式事务和声明式事务

编程式事务就是将业务代码和事务代码放在一起书写,它的耦合性太高,开发中不使用

声明式事务通过一段配置让程序组装运行,最后达到事务控制的目的

声明式事务就是通过AOP原理实现的

4.2 Spring事务管理相关的API

PlateformTransactionManager
在这里插入图片描述
在这里插入图片描述
TransactionDefinition

TransactionDefinition这个API是用来做事务定义的

面向配置(事务隔离级别,传播行为,是否只读事务,超时时间)

在这里插入图片描述
隔离级别(*)
在这里插入图片描述
传播行为(理解)
在这里插入图片描述
在这里插入图片描述
是否只读事务
在这里插入图片描述
超时时长
在这里插入图片描述
配置项总结
在这里插入图片描述

第五章 声明式事务(重点)

5.1 思路

目标对象[业务类]service

增强[事务管理器]DataSourceTransactionManager

事务配置:[事务隔离级别事务传播行为事务是否只读事务超时时长]

5.2 xml版(重点)

在这里插入图片描述

5.3 注解结合XML(重点)

在这里插入图片描述
(1)复制工程
在这里插入图片描述
(2)删除xml中的tx:advice和aop:config

(3)添加一个事务注解驱动
在这里插入图片描述
(4)在方法上添加声明式事务的注解
在这里插入图片描述

5.4 纯注解版(了解)

在这里插入图片描述

package cn.itcast.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;

/**
 * 1、声明配置类
 * 2、开启包扫描
 * 3、开始事务注解管理
 *  @EnableTransactionManagement
 * 4、第三方对象
 */
@Configuration
@ComponentScan(basePackages = "cn.itcast")
@EnableTransactionManagement
public class SpringConfig {

    /**
     * @Bean:配置到方法上,表明此方法的返回值交给Spring容器管理
     *  在Spring容器启动时,自动的扫描所有@Bean的方法
     *  自动执行并获取返回值,存入Spring容器
     *  在方法上配置参数,Spring会自动的从容器中获取对象,自动调用方法
     */

    /**
     * DataSource
     */
    @Bean
    public DataSource getDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql:///heima31");
        return dataSource;
    }

    /**
     * JdbcTemplate
     */
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }


    /**
     * 事务管理器
     */
    @Bean
    public PlatformTransactionManager getTransactionManager(DataSource dataSource) {
        DataSourceTransactionManager tm = new DataSourceTransactionManager();
        tm.setDataSource(dataSource);
        return  tm;
    }
}

第六章 事务补充

6.1 准备

在这里插入图片描述

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL,
  `age` smallint(3) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
 
CREATE TABLE `role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `role_name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

6.2 测试

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.3 try catch影响

在这里插入图片描述
在这里插入图片描述

6.4 事务嵌套影响

在这里插入图片描述
在这里插入图片描述
上面的代码可以改造成如下代码:

@Transactional
public void out() throws Exception{
	Role role=new Role();
	Role.setRoleName(“roleName:”+new Random().nextInt(100));
	roleService.save(role);
	int age=random.nextInt(100);
	User user=new User().setAge(age).setName(“name:”+age);
	userService.save(user);
	throw new Exception();
}

这个是inner的方法没有抛出异常,out的代码抛出了异常,所以代码可以改造成如上样子。所以结合结论一,事务回滚都失败。

在这里插入图片描述
在这里插入图片描述

posted @ 2022-01-24 00:08  KeepArlen  阅读(10)  评论(0)    收藏  举报