Spring初始

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

Spring第一天

第一章 JavaEE三层架构

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

之前的代码逻辑

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

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

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

2.1 简化版本

(1)创建工程导入依赖

java 复制代码
    <dependencies>
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6</version>
        </dependency>
    </dependencies>

(2)配置dao接口

(3)创建BeanFactory工具类

java 复制代码
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解析配置文件

java 复制代码
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 入门案例

创建工程导入依赖

java 复制代码
    <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配置文件

java 复制代码
<?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 对象作用域

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

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

5.2 生命周期

java 复制代码
<!--对象的生命周期(对象和创建和销毁过程),和对象的作用域息息相关
            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 构造方法注入

向对象添加有参构造方法

java 复制代码
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标签配置对象创建(需要添加构造方法参数)

java 复制代码
    <!--
        相当于调用无参构造方法,实例化 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方法

java 复制代码
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方法调用

java 复制代码
    <!--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的时候,直接传入多个配置文件。

java 复制代码
ApplicationContext act=new ClassPathXmlApplicationContext("beans1.xml","beans2.xml","...");

2、主从配置文件

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

java 复制代码
    <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
java 复制代码
//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)
)

创建工程导入依赖

java 复制代码
    <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>

创建实体类

java 复制代码
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;
}

保存

java 复制代码
    /**
     * 保存账户数据
     * @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);
    }

根据名称查询

java 复制代码
/**
 * 根据名称查询账户
 */
@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);
}

查询所有

java 复制代码
/**
 * 查询列表记录
 */
@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 环境搭建

创建工程导入坐标

java 复制代码
<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>

创建实体

java 复制代码
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接口

java 复制代码
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接口实现类

java 复制代码
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接口

java 复制代码
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接口实现类

java 复制代码
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 测试

java 复制代码
    /**
     * 调用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标签删除

java 复制代码
<?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中加入包扫描

java 复制代码
<?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代码如下

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代码如下

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配置文件

新建配置类

java 复制代码
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容器管理

java 复制代码
@Propertysource(value="jdbc.properties ")

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

java 复制代码
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

java 复制代码
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引入其他的配置类

java 复制代码
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 导入坐标

java 复制代码
<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 配置注解

java 复制代码
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的配置文件

java 复制代码
<?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>

测试

java 复制代码
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层代码

java 复制代码
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方法就可以得到当前线程对应的值。

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

第二章 开发事务管理器

2.1 代码开发

复制一个工程

编写事务管理器(copy)

java 复制代码
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)

java 复制代码
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的方式创建代理对象

java 复制代码
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 代码开发

创建工程,引入坐标

java 复制代码
<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):在切点的最终执行

java 复制代码
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配置文件

java 复制代码
<?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切面类

java 复制代码
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)工程准备,导入依赖

java 复制代码
<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

java 复制代码
<?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介绍



方法介绍

java 复制代码
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)创建工程,导入坐标

java 复制代码
<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)测试

java 复制代码
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 纯注解版(了解)

java 复制代码
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 准备

sql 复制代码
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 事务嵌套影响


上面的代码可以改造成如下代码:

java 复制代码
@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的代码抛出了异常,所以代码可以改造成如上样子。所以结合结论一,事务回滚都失败。


相关推荐
zl9798995 小时前
SpringBoot-入门介绍
java·spring boot·spring
Knight_AL5 小时前
Redis 限流解决方案:结合 Lua 脚本、AOP 和自定义注解的实现
redis·spring
AAA修煤气灶刘哥8 小时前
Spring AI 通关秘籍:从聊天到业务落地,Java 选手再也不用馋 Python 了!
后端·spring·openai
zl97989911 小时前
SpringBoot-配置文件yaml
java·spring boot·spring
_extraordinary_11 小时前
Java Spring配置
java·开发语言·spring
工业甲酰苯胺12 小时前
Java并发机制的底层实现原理:从CPU到JVM的全面解析
java·jvm·spring
zl97989913 小时前
SpringBoot-常用注解
java·spring boot·spring
yacolex14 小时前
Mac安装使用Gradle
spring·macos·gradle
JAVA学习通15 小时前
Spring AI 1.0 GA 深度解析:Java生态的AI革命已来
java·人工智能·spring·springai