Spring5

课程内容介绍

  1. Spring框架概述

  2. IOC容器

    (1)IOC底层原理

    (2)IOC接口(BeanFactory)

    (3)IOC操作Bean管理(基于xml)

    (4)IOC操作Bean管理(基于注解)

  3. AOP

  4. JdbcTemplate

  5. 事务管理

  6. Spring5新特性

Spring框架概述

  1. Spring是轻量级开源的javaEE框架

  2. Spring可以解决企业应用端开发的复杂性

  3. Spring有两个核心部分:IOC和Aop

    (1) IOC: 控制反转,把创建对象的过程交给Spring进行管理

    (2) Aop:面向切面,不修改源代码进行功能增强

  4. Spring特点

    (1) 方便解耦,简化开发

    (2) Aop 编程支持

    (3) 方便程序测试

    (4) 方便和其他框架进行整合

    (5) 方便进行事务操作

    (6) 降低API开发难度

  5. 现在课程中,选取Spring版本5.x

入门案例

(1)下载Spring5

下载最新稳定版本5.2.9

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zyJ1mmcu-1615205031489)(C:\Users\小雄\AppData\Roaming\Typora\typora-user-images\1611889044728.png)]

(2)下载地址

https://repo.spring.io/release/org/springframework/spring/5.2.9.RELEASE/

(3)导入jar包

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9CDd43jD-1615205031491)(C:\Users\小雄\AppData\Roaming\Typora\typora-user-images\1611902696306.png)]

(4)创建一个类,在这个类内创建一个普通方法

public class User {
    public void add() {
        System.out.println("add...");
    }
}

(5)创建Spring配置文件,在配置文件配置创建对象

  1. Spring配置文件使用xml格式

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dDi46I9b-1615205031492)(C:\Users\小雄\AppData\Roaming\Typora\typora-user-images\1611903477212.png)]

<?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">

<!--  配置User对象创建  -->
    <bean id="user" class="com.company.User"></bean>
</beans>

(6)进行测试代码编写

public class TestSpring5 {

    @Test
    public void testAdd() {
        //1 加载spring配置文件
        ApplicationContext context =
                new ClassPathXmlApplicationContext("bean1.xml");

        //2 获取配置创建的对象
        User user = context.getBean("user", User.class);

        System.out.println(user);
        user.add();
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O4kfYMRW-1615205031494)(C:\Users\小雄\AppData\Roaming\Typora\typora-user-images\1611904138469.png)]

IOC(概念和原理)

  1. 什么是IOC

    (1)控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理

    (2)使用IOC目的:为了耦合度降低

    (3)做入门案例就是IOC实现

  2. IOC底层原理

(1)xml解析,工厂模式,反射

  1. 画图讲解IOC底层原理

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B6yRimy5-1615205031496)(C:\Users\小雄\AppData\Roaming\Typora\typora-user-images\1611905344937.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yw7cHQN2-1615205031497)(C:\Users\小雄\AppData\Roaming\Typora\typora-user-images\1611905276958.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p2OuD4ow-1615205031499)(C:\Users\小雄\AppData\Roaming\Typora\typora-user-images\1611905685302.png)]

IOC(BeanFactory接口)

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

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

    (1)BeanFactory:IOC容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用

    *加载配置文件的时候不会创建对象,在获取对象(使用对象)才去创建对象

    (2)ApplicationContext:BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用

    *加载配置文件的时候同时创建对象

  3. ApplicationContext接口有实现类

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7r09HwSV-1615205031500)(C:\Users\小雄\AppData\Roaming\Typora\typora-user-images\1611907486137.png)]

IOC操作Bean管理(概念)

  1. 什么是Bean管理

    (0)Bean管理指的是两个操作

    (1)Spring创建对象

    (2)Spring注入属性

  2. Bean管理操作有两种方式

    (1)基于xml配置文件方式实现

    (2)基于注解方式实现

IOC操作Bean管理(基于xml方式)

  1. 基于xml方式创建对象

    <!--  配置User对象创建  -->
        <bean id="user" class="com.company.User"></bean>
    

    (1)在Spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建

    (2)在bean标签有很多属性,介绍常用的属性

    *id属性:唯一标识

    *class属性:类的全路(包类路径)

    (3)创建对象时候,默认也是执行无参数构造方法完成对象创建

  2. 基于xml方式注入属性

    (1)DI:依赖注入,就是注入属性

    第一种注入方式:使用set方法进行注入

    ​ (1)创建类,定义属性和对应的set方法

    /**
     * 演示使用set方法进行注入属性
     */
    public class Book {
    
        //创建属性
        private String bname;
        private String bauthor;
        
        //创建属性对应的set方法
        public void setBname(String bname) {
            this.bname = bname;
        }
        
        public void setBauthor(String bauthor) {
            this.bauthor = bauthor;
        }
        
    }
    

    ​ (2)在spring配置文件中配置对象

    <!-- 2 set方法注入属性  -->
        <bean id="book" class="com.company.Book">
                <!-- 使用property完成属性注入
                name: 类里面属性名称
                value: 向属性注入的值
                -->
            <property name="bname" value="易筋经"></property>
            <property name="bauthor" value="掌门"></property>
        </bean>
    

    (3)测试

        @Test
        public void testBook() {
            //1 加载spring配置文件
            ApplicationContext context =
                    new ClassPathXmlApplicationContext("bean1.xml");
    
            //2 获取配置创建的对象
            Book book = context.getBean("book", Book.class);
    
            System.out.println(book);
            book.testDemo1();
        }
    }
    

    1611911500499

    第二种注入方式:使用有参数的构造注入

    (1)创建类,定义属性,创建属性对应有参数构造方法

    /**
     * 使用有参数构造注入
     */
    public class Orders {
        //属性
        private String oname;
        private String address;
        //有参数构造
        public Orders(String oname, String address) {
            this.oname = oname;
            this.address = address;
        }
    }
    

    (2)在spring配置文件中进行配置

    <!--  3 有参数构造方法注入属性  -->
        <bean id="orders" class="com.company.Orders">
            <constructor-arg name="oname" value="abc"></constructor-arg>
            <constructor-arg name="address" value="China"></constructor-arg>
    <!--        <constructor-arg index="0" value="abc"></constructor-arg>-->
        </bean>
    

    第三种注入方式:p名称空间注入(了解)

    (1)使用p名称空间注入,可以简化基于xml配置方式

    第一步 添加p名称空间在配置文件中

    <?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:p="http://www.springframework.org/schema/p"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    

    第二步 进行属性注入,在bean标签里面进行操作

        <!-- 4 set方法注入属性(使用p名称)  -->
    <bean id="book" class="com.company.Book" p:bname="九阳神功" p:bauthor="无名氏"></bean>
    

IOC 操作Bean管理(xml注入其他类型属性)

  1. 字面量

    (1)null值

            <bean id="book" class="com.company.Book">
                <property name="bname" value="易筋经"></property>
                <property name="bauthor" value="掌门"></property>
            <!-- null值! -->
                <property name="baddress">
                    <null/>
                </property>
            </bean>
    

    (2)属性值包含特殊符号

        <!--  属性值包含特殊符号
          1<>进行转义 &lt; &gt;
          2 把带特殊符号内容写到CDATA
          -->
                <property name="baddress">
                    <value><![CDATA[<<南京>>]]></value>
                </property>
    
  2. 注入属性-外部bean

    1. 创建两个类service类和dao类
    public class UserService {
    
        //创建UserDao类型属性,生成set方法
        private UserDao userDao;
    
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    
        public void add() {
            System.out.println("service add......");
            userDao.update(); //在service调用dao里面的方法
        }
    }
    
    public interface UserDao {
    
        public void update();
    
    }
    
    public class UserDaoImpl implements UserDao {
        @Override
        public void update() {
            System.out.println("dao update......");
        }
    }
    
    1. 在service调用dao里面的方法

    2. 在spring配置文件中进行配置

        <bean name="userService" class="com.company.service.UserService">
    <!-- 注入UserDao对象
           name属性:类里面属性名称
           ref属性:创建UserDao对象bean标签id值
         -->
            <property name="userDao" ref="userDaoImpl"></property>
        </bean>
        <bean id="userDaoImpl" class="com.company.dao.UserDaoImpl"></bean>
    </beans>
    
  3. 注入属性-内部bean

    1. 一对多关系:部门和员工

    一个部门有多个员工,一个员工属于一个部门 部门是一,员工是多

    1. 在实体类之间表示一对多的关系
    //部门类
    public class Dept {
        private String dname;
    
        public void setDname(String dname) {
            this.dname = dname;
        }
    }
    
    //员工类
    public class Emp {
        private String ename;
        private String gender;
        //员工属于某一个部门,使用对象形式表示
        private Dept dept;
    
        public void setDept(Dept dept) {
            this.dept = dept;
        }
    
        public void setEname(String ename) {
            this.ename = ename;
        }
    
        public void setGender(String gender) {
            this.gender = gender;
        }
    }
    
    <!--内部bean-->
        <bean id="emp" class="com.company.bean.Emp">
    <!--   设置两个普通属性     -->
            <property name="ename" value="lucy"></property>
            <property name="gender" value="女"></property>
    <!--    设置对象类型属性    -->
            <property name="dept">
                <bean id="dept" class="com.company.bean.Dept">
                    <property name="dname" value="安保部"></property>
                </bean>
            </property>
        </bean>
    
  4. 注入属性-级联赋值

    第一种方法

    <!--内部bean-->
        <bean id="emp" class="com.company.bean.Emp">
    <!--   设置两个普通属性     -->
            <property name="ename" value="lucy"></property>
            <property name="gender" value="女"></property>
    <!--   级联赋值     -->
            <property name="dept" ref="dept"></property>
        </bean>
        <bean id="dept" class="com.company.bean.Dept">
            <property name="dname" value="财务部"></property>
        </bean>
    

    第二种方法

    1612018499072
    <!--内部bean-->
        <bean id="emp" class="com.company.bean.Emp">
    <!--   设置两个普通属性     -->
            <property name="ename" value="lucy"></property>
            <property name="gender" value="女"></property>
    <!--   级联赋值     -->
            <property name="dept" ref="dept"></property>
            <property name= "dept.dname" value="技术部"></property>
        </bean>
        <bean id="dept" class="com.company.bean.Dept">
            <property name="dname" value="财务部"></property>
        </bean>
    

    相当于第一种级联后(是财务部)再重新赋值(是技术部)!!!

IOC操作Bean管理(xml注入集合属性)

  1. 注入数组类型属性
  2. 注入List集合类型属性
  3. 注入Map集合类型属性
  4. 注入set集合类型属性
    1. 创建类,定义数组、list、map、set类型属性,生成对应set方法
public class Stu {
    
    //1 数组类型属性
    private String[] courses;
    //2 list集合类型属性
    private List<String> list;
    //3 map集合类型属性
    private Map<String,String>maps;
    //4 set集合类型属性
    private Set<String>sets;
    
    //5 集合的对象属性类型  第五点
    private List<Course> courseList;

    public void setCourseList(List<Course> courseList) {
        this.courseList = courseList;
    }

    public void setCourses(String[] courses) {
        this.courses = courses;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public void setMaps(Map<String, String> maps) {
        this.maps = maps;
    }

    public void setSets(Set<String> sets) {
        this.sets = sets;
    }
}
public class Course {

    private String cname;  //课程名称

    public void setCname(String cname) {
        this.cname = cname;
    }

    @Override
    public String toString() {
        return "Course{" + "cname='" + cname + '\'' + '}';
    }
}

​ b. 在spring文件中进行配置

<!--  集合属性的注入  -->
    <bean id="stu" class="com.company.collectiontype.Stu">
<!--    数组属性的注入    -->
        <property name="courses">
            <array>
                <value>JAVA课程</value>
                <value>C#课程</value>
            </array>
        </property>
<!--    List属性的注入    -->
        <property name="list">
            <list>
                <value>张三</value>
                <value>小三</value>
            </list>
        </property>
<!--    map属性的注入    -->
        <property name="maps">
            <map>
                <entry key="尼个" value="这个"></entry>
                <entry key="果个" value="那个"></entry>
            </map>
        </property>
<!--    set属性的注入    -->
        <property name="sets">
            <set>
                <value>MySql</value>
                <value>Redis</value>
            </set> 
        </property>
    </bean>
  1. 在集合里面设置对象类型值
<!--bean.xml里面的配置-->
<!--    集合对象属性类型的注入    -->
        <property name="courseList">
            <list>
                <ref bean="course1"></ref>
                <ref bean="course2"></ref>
            </list>
        </property>
    </bean>

    <bean id="course1" class="com.company.collectiontype.Course">
        <property name="cname" value="Spring5框架"></property>

    </bean>
    <bean id="course2" class="com.company.collectiontype.Course">
        <property name="cname" value="Mybatis框架"></property>
    </bean>
  1. 把集合注入部分提取出来(作为公共部分)
    1. 在spring配置文件中引入名称空间util
<?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:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

</beans>

​ b.使用util标签完成list集合注入提取

<!--  1 提取list集合类型属性注入  -->
    <util:list id="bookList">
        <value>葵花宝典</value>
        <value>九阳神功</value>
        <value>易筋经</value>
    </util:list>

<!--  2 提取list集合类型属性注入使用  -->
    <bean id="book" class="com.company.collectiontype.Book">
        <property name="list" ref="bookList"></property>
    </bean>

IOC操作Bean管理(FactoryBean)

  1. Spring有两种类型bean,一种普通bean,另外一种工厂bean(FactoryBean)

  2. 普通bean:在配置文件中定义bean类型就是返回类型

  3. 工厂bean:在配置文件定义bean类型可以和返回类型不一样

    第一步 创建类,让这个类作为工厂bean,实现接口FactoryBean

    第二步 实现接口里面的方法,在实现的方法中定义返回的bean类型

public class MyBean implements FactoryBean<Course> {
    //定义返回bean
    @Override
    public Course getObject() throws Exception {
        Course course = new Course();
        course.setCname("abc");
        return course;
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }
}
    <bean id="myBean" class="com.company.factorybean.MyBean">
    </bean>
    @Test
   public void test3() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
        
Course course = context.getBean("myBean",Course.class);

System.out.println(course);
    }

IOC操作Bean管理(bean作用域)

  1. 在Spring里面,设置创建bean实例是单实例还是多实例
  2. 在Spring里面,默认情况下,创建的bean是单实例对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JKfBYsu8-1615205031501)(Spring5.assets/image-20210201153155349.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5fgZQ0w4-1615205031502)(Spring5.assets/image-20210201153240759.png)]

  1. 如何设置单实例还是多实例

    1. 在spring配置文件bean标签里面有属性(scope)用于设置单实例还是多实例

    2. scope属性值

      第一个值 默认值,singleton,表示是单实例对象

      第二个值 prototype,表示是多实例对象

    <!--  2 提取list集合类型属性注入使用  -->
        <bean id="book" class="com.company.collectiontype.Book" scope="prototype">
            <property name="list" ref="bookList"></property>
        </bean>
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RnwG7lRk-1615205031502)(Spring5.assets/image-20210201154408801.png)]

    1. singleton和prototype区别

      第一 singleton 单实例,prototype 多实例

      第二 设置scope值是singleton时候,加载spring配置文件时候就会创建单实例对象

      设置scope值是prototype时候,不是在加载spring配置文件时候创建对象,在调用getBean方法时候创建多实例对象

IOC操作Bean管理(bean生命周期)

  1. 生命周期

    1. 从对象创建到销毁的过程
  2. bean生命周期

    1. 通过构造器创建bean实例(无参数构造)
    2. 为bean的属性设置值和对其他bean引用(调用set方法)
    3. 调用bean的初始化的方法(需要进行配置)
    4. bean可以使用了(对象获取到了)
  3. 当容器关闭时候,调用bean的销毁的方法(需要进行配置销毁的方法)

  4. 演示bean生命周期

    public class Orders {
    
        private String oname;
    
        public Orders() {
            System.out.println("第一步 调用无参构造方法");
        }
    
        public void setOname(String oname) {
            this.oname = oname;
            System.out.println("第二步 对bean属性设置值");
        }
    
        public void initmethod() {
            System.out.println("第三步 调用bean初始化方法");
        }
    
        public void destorymethod() {
            System.out.println("第五步 调用bean的销毁方法");
        }
    }
    
        <bean id="orders" class="com.company.bean.Orders" init-method="initmethod" destroy-method="destorymethod">
            <property name="oname" value="手机"></property>
         </bean>
    
        @Test
        public void test4() {
    //        ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
    
            Orders orders = context.getBean("orders", Orders.class);
            System.out.println("第四步 获取 对象");
            System.out.println(orders);
    
            //手动销毁
            context.close();
        }
    
    image-20210201162426442
  5. bean的后置处理器,bean生命周期有七步

    1. 通过构造器创建bean实例(无参数构造)
    2. 为bean的属性设置值和对其他bean引用(调用set方法)
    3. 把bean实例传递bean后置处理器的方法postProcessBeforeInitialization
    4. 调用bean的初始化的方法(需要进行配置)
    5. 把bean实例传递bean后置处理器的方法postProcessAfterInitialization
    6. bean可以使用了(对象获取到了)
    7. 当容器关闭时候,调用bean的销毁的方法(需要进行配置销毁的方法)
  6. 演示添加后置处理器效果

    1. 创建类,实现接口BeanPostProcessor,创建后置处理器

      public class MyBeanPost implements BeanPostProcessor {
      
          @Override
          public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
              System.out.println("第三步 bean初始化方法前的调用");
              return bean;
          }
      
          @Override
          public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
              System.out.println("第五步 bean初始化方法后的调用");
              return bean;
          }
      }
      
      <!--配置后置处理器-->
          <bean id="myBeanPost" class="com.company.bean.MyBeanPost" ></bean>
      
      image-20210201164433185

IOC操作Bean管理(xml自动装配)

  1. 什么是自动装配

  2. 根据指定装配规则(属性名称或者属性类型),Spring自动将匹配的属性值进行注入

  3. 演示自动装配过程

    1. 根据属性名自动注入
    <!--  实现自动装配
          bean标签属性autowire,配置自动装配
          autowire属性常用两个值:
               byName根据属性名称注入,注入值bean的id值和类属性名称一样
               byType根据属性类型注入-->
        <bean id="emp" class="com.company.beanwire.Emp" autowire="byName" >
    -->
        </bean>
        <bean id="dept" class="com.company.beanwire.Dept" ></bean>
    
    1. 根据属性类型自动注入
    <!--  实现自动装配
          bean标签属性autowire,配置自动装配
          autowire属性常用两个值:
               byName根据属性名称注入,注入值bean的id值和类属性名称一样
               byType根据属性类型注入-->
    <!--使用byType有一个缺点,就是不能使用多个同类型的属性值-->
        <bean id="emp" class="com.company.beanwire.Emp" autowire="byType" >
        </bean>
        <bean id="dept" class="com.company.beanwire.Dept" ></bean>
    

IOC操作Bean管理(外部属性文件)

  1. 直接配置数据库信息

    1. 配置德鲁伊连接池
    2. 引入德鲁伊连接池依赖jar包

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z75UQe4v-1615205031503)(Spring5.assets/image-20210202222635761.png)]

<!-- 直接配置连接池 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/userDb"></property>
        <property name="username" value="root"></property>
        <property name="password" value="password"></property>
    </bean>
  1. 引入外部属性文件配置数据库连接池

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

      image-20210202220401719
    2. 把外部properties属性文件引入到spring配置文件中

      *引入context名称空间

      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:p="http://www.springframework.org/schema/p"
             xmlns:util="http://www.springframework.org/schema/util"
             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/util http://www.springframework.org/schema/util/spring-util.xsd
                                 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
      
      • 在spring配置文件使用标签引入外部属性文件
      <!--  引入外部属性文件  -->
          <context:property-placeholder location="classpath:jdbc.properties"/>
      
      <!--  配置连接池  -->
              <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
                  <property name="driverClassName" value="${prop.driverClass}"></property>
                  <property name="url" value="${prop.url}"></property>
                  <property name="username" value="${prop.userName}"></property>
                  <property name="password" value="${prop.password}"></property>
              </bean>
      

IOC操作Bean管理(基于注解方式)

  1. 什么是注解

    1. 注解是代码特殊标记,格式:@注解名称(属性名称=属性值,属性名称=属性值…)
    2. 使用注解,注解作用在上面,方法上面,属性上面
    3. 使用注解目的:简化xml配置
  2. Spring针对Bean管理中创建对象提供注解

    1. @Component
    2. @Service
    3. @Controller
    4. @Repository

    *上面四个注解功能是一样的,都可以用来创建bean实例

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

    第一步 引入依赖

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KgRdVrjA-1615205031504)(Spring5.assets/image-20210203151132966.png)]

    第二步 开启组件扫描

        <!--  开启组件扫描
          1. 如果扫描多个包,多个包使用逗号隔开
          2. 扫描包上层目录
          -->
        <context:component-scan base-package="com.company"></context:component-scan>
    

    第三步 创建类,在类上面添加创建对象注解

    //在注解里面value属性值可以省略不写
    //默认值是类名称,首字母小写
    @Component(value = "userService") //效果同<bean id="userService" class=".."/>
    public class UserService {
    
        public void add() {
            System.out.println("service add...");
        }
    }
    
  4. 开启组件扫描细节配置

        <!--示例1
            use-default-filters="false"表示现在不使用默认filter,自己配置filter
            context:include-filter,设置扫描哪些内容
        -->
        <context:component-scan base-package="com.company" use-default-filters="false">
            <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        </context:component-scan>
    // annotation  注解
        <!--示例2
            下面配置扫描包所有内容
            context:exclude-filter: 设置哪些内容不进行扫描
        -->
        <context:component-scan base-package="com.company">
            <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        </context:component-scan>
    
  5. 基于注解方式实现属性注入

    1. @autowired:根据属性类型进行自动装配

      第一步 把service和dao对象创建,在service和dao类添加创建对象注解

      第二步 在service注入dao对象创建,在service类添加dao类型属性,在属性上面使用注解

      //在注解里面value属性值可以省略不写
      //默认值是类名称,首字母小写
      @Service //效果同<bean id="userService" class=".."/>
      public class UserService {
          //定义dao类型属性
          //不需要添加set方法
          //添加注入属性注解
          @Autowired
          UserDao userDao = new UserDaoImpl();
      
          public void add() {
              System.out.println("service add...");
              userDao.add();
          }
      }
      
      
    2. @Qualifier:根据属性名称进行注入

      这个@Qualifier注解的使用,和上面@Autowired一起使用

          //定义dao类型属性
          //不需要添加set方法
          //添加注入属性注解
          @Autowired
          @Qualifier(value = "userDaoImpl1") //根据名称进行注入
          UserDao userDao = new UserDaoImpl();
      
      @Repository(value = "userDaoImpl1")
      public class UserDaoImpl implements UserDao{
          @Override
          public void add() {
              System.out.println("userDao add。。。。。。");
          }
      }
      
    3. @Resource:可以根据类型注入,可以根据名称注入

          //根据类型进行注入
      //    @Resource
      //    private UserDao userDao;
          @Resource(name = "userDaoImpl1")  //根据属性名称进行注入
          private UserDao userDao;
      
    4. @Value:注入普通类型属性

          @Value(value="abc")  //相当于xml配置文件中的properties
          private String name;
      
  6. 完全注解开发

    1. 创建配置类,替代xml配置文件

      @Configuration
      @ComponentScan(basePackages = {"com.company"})
      public class SpringConfig {
      }
      
    2. 编写测试类

          @Test
          public void test1() {
      
              //加载配置类
              ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
      
              UserService userService = context.getBean("userService", UserService.class);
      
              userService.add();
      
          }
      

AOP(概念)

  1. 什么是AOP(Aspect Oriented Programming)

    1. 面向切面编程(面向方面编程),利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
    2. 通俗描述:不通过修改源代码方式,在主干功能里面添加新功能
      3. 使用登录例子说明AOP

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-st2XVb4E-1615205031506)(Spring5.assets/image-20210203224203163.png)]

AOP(底层原理)

动态代理:什么是动态代理

​ 使用jdk的反射机制,创建对象的能力,创建的是代理类的对象。而不用你创建类文件。不用写java文件。

​ 动态:在程序执行时,调用jdk提供的方法才能创建代理类的对象

image-20210204110947210

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NItxkol0-1615205031507)(Spring5.assets/image-20210302202321274.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fjmEJv32-1615205031508)(Spring5.assets/image-20210302204441646.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kGdYafvl-1615205031509)(Spring5.assets/image-20210302205335577.png)]

image-20210303215856755
  1. AOP底层使用动态代理

    1. 有两种情况动态代理

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

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0x7Cg6Fl-1615205031509)(Spring5.assets/image-20210203225208864.png)]

      1. 第二种 没有接口情况,使用CGLIB动态代理(CGLIB(Code Generation Library)是一个强大的高性能的代码生成包)
      • 创建子类的代理对象,增强类的方法

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6flxz5RI-1615205031510)(Spring5.assets/image-20210203225816771.png)]

AOP(JDK动态代理)

  1. 使用JDK动态代理,使用Proxy类里面的方法创建代理对象

  2. image-20210303164045334
    1. 调用newProxyInstance方法
    image-20210303164212793

    方法有三个参数:

    第一参数,类加载器

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

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

  3. 编写JDK动态代理代码

    1. 创建接口,定义方法
    public interface UserDao {
    
        public int add(int a,int b);
    
        public String update(String id);
    
    }
    
    
    1. 创建接口实现类,实现方法
    public class UserDaoImpl implements UserDao {
        @Override
        public int add(int a, int b) {
            return a+b;
        }
    
        @Override
        public String update(String id) {
            return id;
        }
    }
    
    1. 使用Proxy类创建接口代理对象
    //InvocationHandler对象:增强代码
    public class UserDaoProxy implements InvocationHandler {
        //1 把创建的是谁的代理对象,把水传递过来
        //有参数构造传递
        private Object obj;
    
        public UserDaoProxy(Object obj) {
            this.obj = obj;
        }
    
        //增强的逻辑
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            //方法之前
            System.out.println("方法之前执行..."+method.getName()+" :传递的参数..."+ Arrays.toString(args));
    
            //被增强的方法执行
            Object res = method.invoke(obj, args); //执行目标方法
    
            //方法之后
            System.out.println("方法执行之后..."+obj);
    
            return res;
        }
    }
    
    //测试
    public class JDKProxy {
    
        public static void main(String[] args) {
    
            //创建代理对象,使用Proxy
            //1.创建目标对象
            UserDao userDao = new UserDaoImpl();
    
            //2.创建InvocationHandler对象
            InvocationHandler handler = new UserDaoProxy(userDao);
    
            //3.创建代理对象(两种写法)
            UserDao instance = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(), handler);
    //        UserDao instance = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), new Class[]{UserDao.class}, handler);
    
            //4.通过代理执行方法
            int add = instance.add(1, 3);
            System.out.println("通过动态代理对象,调用方法   result:"+add);
    
        }
    }
    

AOP(术语)

  1. 连接点

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

  2. 切入点

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

  3. 通知(增强)

    1. 实际增强的逻辑部分称为通知(增强)

    2. 通知有多种类型

      *前置通知

      *后置通知

      *环绕通知

      *异常通知

      *最终通知

  4. 切面

    是动作

    1. 把通知应用到切入点过程

AOP操作(准备)

  1. Spring框架一般基于AspectJ实现AOP操作

    1. 什么是AspectJ?

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

  2. 基于AspectJ实现AOP操作

    1. 基于xml配置文件实现
    2. 基于注解方式实现(使用)
  3. 在项目工程里面引入AOP相关依赖

    image-20210304150455527
  4. 切入点表达式

在使用spring框架配置AOP的时候,不管是通过XML配置文件还是注解的方式都需要定义pointcut”切入点”

例如定义切入点表达式 execution (* com.sample.service.impl…*. *(…))

execution()是最常用的切点函数,其语法如下所示:

整个表达式可以分为五个部分:

1、execution(): 表达式主体。

2、第一个*号:表示返回类型, *号表示所有的类型。

3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。

4、第二个*号:表示类名,*号表示所有的类。

5、*(…):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数

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

  2. 语法结构(execution:执行,实施)

    execution([][][][][权限修饰符][返回类型][类全路径]方法名称)

    举例1:对com.atguigu.dao.BookDao类里面的add进行增强

    execution(* com.atguigu.dao.BookDao.add(…))

    举例2:对com.atguigu.dao.BookDao类里面的所有方法进行增强

    execution(* com.atguigu.dao.BookDao.*(…))

    举例3:对com.atguigu.dao包里面的所有类,类里面所有方法进行增强

    execution(* com.atguigu.dao..(…))

AOP操作(AspectJ注解)

  1. 创建类,在类里面定义方法
public class User {

    public void add() {
        System.out.println("add.............");
    }
}
  1. 创建增强类(编写增强逻辑)

    1. 在增强类里面,创建方法,让不同方法代表不同通知类型
    //增强的类
    public class UserProxy {
        /**
         * 前置通知
         * 在add()目标方法之前实现
         */
        public void before() {
            System.out.println("before...........");
        }
    }
    
  2. 进行通知的配置

    1. 在Spring配置文件中,开启注解扫描

      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:context="http://www.springframework.org/schema/context"
             xmlns:aop="http://www.springframework.org/schema/aop"
             xsi:schemaLocation="http://www.springframework.org/schema/beans 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">
          
          <!-- 开启注解扫描 -->
          <context:component-scan base-package="com.company.aopanno"></context:component-scan>
      
    2. 使用注解创建User和UserProxy对象

      //被增强类   目标类
      @Component
      public class User {
      
      
      //增强的类  代理类
      @Component
      public class UserProxy {
      
    3. 在增强类上面添加注解@Aspect

      //增强的类  代理类
      @Component
      @Aspect   //生成代理对象
      public class UserProxy {
      
    4. 在spring配置文件中开启生成代理对象

          <!--  开启AspectJ生成代理对象  -->
          <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
      
  3. 配置不同类型的通知

    1. 在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置
    //增强的类
    @Component
    @Aspect   //生成代理对象
    public class UserProxy {
        /**
         * 前置通知
         * 在add()目标方法之前实现
         * @Before注解表示作为前置通知
         */
        @Before(value = "execution(* com.company.aopanno.entity.User.add(..))")
        public void before() {
            System.out.println("before...........");
        }
    
        /**
         * 最终通知
         * 在add()目标方法之后一定会执行
         * @After注解表示作为最终通知
         */
        @After(value = "execution(* com.company.aopanno.entity.User.add(..))")
        public void after() {
            System.out.println("after...........");
        }
    
        /**
         * 后置通知
         * 在add()目标方法之后实现
         * @AfterReturning注解表示作为后置通知
         */
        @AfterReturning(value = "execution(* com.company.aopanno.entity.User.add(..))")
        public void afterReturning() {
            System.out.println("afterReturning...........");
        }
    
        /**
         * 环绕通知
         * 在add()目标方法之前和之后都实现
         * @Around注解表示作为环绕通知
         */
        @Around(value = "execution(* com.company.aopanno.entity.User.add(..))")
        public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            System.out.println("环绕之前...........");
    
            //被增强的方法执行
            proceedingJoinPoint.proceed();
    
            System.out.println("环绕之后...........");
        }
    
        /**
         * 异常通知
         * 在add()目标方法执行中遇到异常实现
         * @AfterThrowing注解表示作为异常通知
         */
        @AfterThrowing(value = "execution(* com.company.aopanno.entity.User.add(..))")
        public void afterThrowing() {
            System.out.println("异常出现时...........");
    
        }
    }
    
  4. 相同的切入点抽取

        //相同切入点抽取
        @Pointcut(value = "execution(* com.company.aopanno.entity.User.add(..))")
        public void pointcut() {
    
        }
    
        /**
         * 前置通知
         * 在add()目标方法之前实现
         * @Before注解表示作为前置通知
         */
        @Before(value = "pointcut()")
        public void before() {
            System.out.println("before...........");
        }
    
  5. 有多个增强类对同一个方法进行增强,设置增强类优先级

    1. 在增强类上面添加注解@Order(数字类型值),数字类型值越小优先级越高
    @Component
    @Aspect
    @Order(-3)
    public class UserProxy2 {
    
        /**
         * 前置通知
         * 在add()目标方法之前实现
         * @Before注解表示作为前置通知
         */
        @Before(value = "execution(* com.company.aopanno.entity.User.add(..))")
        public void before() {
            System.out.println("before2...........");
        }
    }
    
    //增强的类
    @Component
    @Aspect   //生成代理对象
    @Order(3)
    public class UserProxy {
    
        //相同切入点抽取
        @Pointcut(value = "execution(* com.company.aopanno.entity.User.add(..))")
        public void pointcut() {
    
        }
    
        /**
         * 前置通知
         * 在add()目标方法之前实现
         * @Before注解表示作为前置通知
         */
        @Before(value = "pointcut()")
        public void before() {
            System.out.println("before...........");
        }
    
    
    image-20210304193445918
  6. 完全使用注解开发

    1. 创建配置类,不需要创建xml配置文件

      @Configuration
      @ComponentScan(basePackages = {"com.company"})
      @EnableAspectJAutoProxy(proxyTargetClass = true)
      public class config {
      }
      

AOP操作(AspectJ配置文件)了解即可

  1. 创建两个类,增强类和被增强类,创建方法

  2. 在spring配置文件中创建两个类对象

            <!--创建对象 -->
        <bean id="book" class="com.company.aopxml.entity.Book"></bean>
        <bean id="bookProxy" class="com.company.aopxml.proxy.BookProxy"></bean>
    
  3. 在spring配置文件中配置切入点

            <!--配置aop增强-->
        <aop:config>
            <!--1.配置切入点-->
            <aop:pointcut id="p" expression="execution(* com.company.aopxml.entity.Book.buy(..))"/>
            <!--2.配置切面-->
            <aop:aspect ref="bookProxy">
                <aop:before method="before" pointcut-ref="p"></aop:before>
            </aop:aspect>
        </aop:config>
    </beans>
    

JdbcTemplate(概念和准备)

template:样板,模板

  1. 什么是JdbcTemplate

    1. Spring框架对JDBC进行封装,使用JdbcTemplate方便实现对数据库操作
  2. 准备工作

    1. 引入相关jar包

      image-20210305152927003
    2. 在Spring配置文件配置数据库连接池

          <context:property-placeholder location="jdbc.properties"></context:property-placeholder>
              <!--数据库连接池-->
          <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
              <property name="url" value="${prop.driverClass}" />
              <property name="username" value="${prop.userName}"/>
              <property name="password" value="${prop.password}"/>
              <property name="driverClassName" value="${prop.driverClass}"/>
          </bean>
      
      //外部文件  jdbc.properties
      prop.driverClass=com.mysql.jdbc.Driver
      prop.url=jdbc:mysql://localhost:3306/userdb
      prop.userName=root
      prop.password=password
      
    3. 配置JdbcTemplate对象,注入DataSource

          <!--JdbcTemplate对象-->
          <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
              <!--注入dataSource-->
              <property name="dataSource" ref="dataSource"></property>
          </bean>
      
    4. 创建service类,创建dao类,在dao注入jdbcTemplate对象

      *配置文件

          <!--注入JdbcTemplate对象-->
          <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
              <!--注入dataSource-->
              <property name="dataSource" ref="dataSource"></property>
          </bean>
      

      *service

      @Service
      public class BookService {
          
          @Autowired
          BookDao bookDao;
          
      }
      

      *dao

      @Repository
      public class BookDaoImpl implements BookDao {
      
          @Autowired
          private JdbcTemplate jdbcTemplate;
      
      }
      

JdbcTemplate操作数据库(添加)

  1. 对应数据库创建实体类
public class User {

    private String userid;
    private String username;
    private String ustatus;

    public String getUserid() {
        return userid;
    }

    public void setUserid(String userid) {
        this.userid = userid;
    }

    public String getUsername() {
        return username;
    }

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

    public String getUstatus() {
        return ustatus;
    }

    public void setUstatus(String ustatus) {
        this.ustatus = ustatus;
    }
}
  1. 编写service和dao

    1. 在dao进行数据库添加操作
    image-20210305205754546
    • 有两个参数
    • 第一个参数:sql语句
    • 第二个参数:可变参数,设置sql语句
    public interface BookDao {
    
        public void add(Book book);
    
    }
    
    
    @Service
    public class BookService {
    
        //注入dao
        @Autowired
        BookDao bookDao;
    
        //添加的方法
        public void addBook(Book book) {
            bookDao.add(book);
        }
    }
    
    @Repository
    public class BookDaoImpl implements BookDao {
    
        //注入JdbcTemplate
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        //添加的方法
        @Override
        public void add(Book book) {
    
            //创建sql语句
            String sql = "insert into t_book values(?,?,?)";
    
            //调用方法实现
            Object[] args = {book.getUserid(), book.getUsername(), book.getUstatus()};
    
            int update = jdbcTemplate.update(sql, args);
            System.out.println(update);
    
        }
    }
    
  2. 测试类

    public class TestBook {
    
        @Test
        public void testJdbcTemplate() {
            ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    
            BookService bookService = context.getBean("bookService", BookService.class);
    
            Book book = new Book();
            book.setUserid("1");
            book.setUsername("java");
            book.setUstatus("架构师");
            bookService.addBook(book);
        }
    }
    

    结果:

    image-20210305211704959

JdbcTemplate(删除和修改)

    //修改的方法
    @Override
    public void updateBook(Book book) {
        
        String sql = "update t_book set username=?,ustatus=? where user_id=?";

        Object[] args = {book.getUsername(),book.getUstatus(),book.getUserid()};

        int update = jdbcTemplate.update(sql, args);
        System.out.println(update);
    }

    //删除的方法
    @Override
    public void deleteBook(String id) {

        String sql = "delete from t_book where user_id=?";
        int update = jdbcTemplate.update(sql,id);
        System.out.println(update);
    }

JdbcTemplate操作数据库(查询返回某个值)

  1. 查询表里面有多少条记录,返回是某个值

  2. 使用JdbcTemplate实现查询返回某个值代码

    image-20210306111344111
    • 第一个参数:sql语句
    • 第二个参数:返回类型的class
        //查询表中记录数
        @Override
        public int selectCount() {
    
            String sql = "select count(*) from t_book";
            Integer integer = jdbcTemplate.queryForObject(sql, Integer.class);
            return integer;
        }
    

JdbcTemplate操作数据库(查询返回对象)

  1. 场景:查询图书详情
  2. JdbcTemplate实现查询返回对象
image-20210306113146073
  • 第一个参数:SQL语句
  • 第二个参数:rowMapper, 是接口,返回不同类型数据,使用这个接口里面实现类完成数据封装
  • 第三个参数:sql语句值
    //查询返回对象
    @Override
    public Book findBookInfo(String id) {
        String sql = "select * from t_book where user_id=?";
        Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), id);
        return book;
    }

JdbcTemplate操作数据库(查询返回集合)

  1. 场景:查询图书列表分页…
  2. 调用JdbcTemplate方法实现查询返回集合
image-20210306151325069
  • 第一个参数:SQL语句
  • 第二个参数:rowMapper, 是接口,返回不同类型数据,使用这个接口里面实现类完成数据封装
  • 第三个参数:sql语句值
    @Override
    public List<Book> findAllBook() {
        String sql = "select * from t_book";
        //调用方法
        List<Book> bookList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class));
        return bookList;
    }

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

image-20210306153448267
  • 第一个参数:sql语句
  • 第二个参数:List集合,添加多条记录数据
    @Override
    public void batchAddBook(List<Object[]> batchArgs) {
        String sql = "insert into t_book values(?,?,?)";
        int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
        System.out.println(ints);
    }

JdbcTemplate操作数据库(批量修改,删除)

    //批量修改
    @Override
    public void batchUpdateBook(List<Object[]> batchArgs) {
        String sql = "update t_book set username=?,ustatus=? where user_id=?";
        int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
        System.out.println(Arrays.toString(ints));
    }
    //批量刪除
    @Override
    public void batchDeleteBook(List<Object[]> batchArgs) {
        String sql = "delete from t_book where user_id=?";
        int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
        System.out.println(Arrays.toString(ints));
    }

事务操作(事务概念)

  1. 什么是事务

    1. 事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败

    2. 典型场景: 银行转账

      *Lucy转账100元给Mary

      *Lucy少100,Mary多100

  2. 事务四个特性(ACID)

    1. 原子性(Atomicity)
    2. 一致性(consistancy)
    3. 隔离性([isolation](javascript:😉
    4. 持久性([durability](javascript:😉

事务操作(搭建事务操作环境)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y8gRZOS4-1615205031512)(Spring5.assets/image-20210306162445919.png)]

  1. 创建数据库表,添加记录

    image-20210306163033433
  2. 创建service,搭建dao,完成对象创建和注入关系

    1. service注入dao,在dao注入JdbcTemplate,在JdbcTemplate注入DataSource
    @Service
    public class UserService {
        
        @Autowired
        UserDao userDao;
    }
    
    @Repository
    public class UserDaoImpl implements UserDao {
    
        @Autowired
        private JdbcTemplate jdbcTemplate;
    }
    
  3. 在dao创建两个方法:多钱和少钱的方法,在service创建方法(转账的方法)

    @Repository
    public class UserDaoImpl implements UserDao {
    
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        //多钱
        @Override
        public void addMoney(int money,String username) {
            String sql = "update t_account set money=money+? where username=?";
            jdbcTemplate.update(sql,money,username);
        }
    
        //少钱
        @Override
        public void reduceMoney(int money,String username) {
            String sql = "update t_account set money=money-? where username=?";
            jdbcTemplate.update(sql,money,username);
        }
    }
    
    @Service
    public class UserService {
    
        @Autowired
        UserDao userDao;
    
        //转账的方法
        public void account(int money,String username1,String username2) {
           //调用少钱方法
            userDao.reduceMoney(money,username2);
            
            //调用多钱方法
            userDao.addMoney(money,username1);
        }
    }
    
  4. 上面代码,如果正常执行没有问题的,但是如果代码执行过程中出现异常,有问题

    image-20210306211311398
    1. 上面问题如何解决

      *使用事务进行解决

    2. 事务操作过程

      image-20210306211750628

事务操作(Spring事务管理介绍)

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

  2. 在Spring进行事务管理操作

  3. 有两种方式:编程式事务管理和声明式事务管理(推荐使用)

  4. 声明式事务管理

    1. 基于注解方式(使用)
    2. 基于xml配置文件方式
  5. 在Spring进行声明式事务管理,底层使用AOP原理

  6. Spring事务管理API

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

      image-20210306213427419

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

  1. 在spring配置文件配置事务管理器

        <!--创建事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
         <!--注入数据源-->
    <property name="dataSource" ref="dataSource"></property>
    </bean>
    
  2. 在spring配置文件,开启事务注解

    1. 在spring配置文件事务管理器
    image-20210306215618990
    1. 开启事务注解
        <!--开启事务注解-->
        <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
    
  3. 在service类上面(或者service类里面方法上面)添加事务注解

    1. @Transactional,这个注解添加到类上面,也可以添加方法上面
    2. 如果把这个注解添加类上面,这个类里面所有方法都添加事务
    3. 如果把这个注解添加方法上面,只为这个方法添加事务
    @Service
    @Transactional
    public class UserService {
    

事务操作(声明式事务管理参数配置)

  1. 在service类上面添加注解@Transactional,在这个注解里面可以配置事务相关参数

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8OXXlRSL-1615205031513)(Spring5.assets/image-20210307150721852.png)]

  2. propagation:事务传播行为

    1. 多事务方法之间进行调用,这个过程中事务是如何进行管理的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S3FTp7wA-1615205031513)(Spring5.assets/image-20210307151450166.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nFXwUmi8-1615205031514)(Spring5.assets/image-20210307151500927.png)]

@Service
@Transactional(propagation = Propagation.REQUIRED)
public class UserService {
  1. isolation:事务隔离级别
    1. 事务有特性成为隔离性,多事务操作之间不会产生影响。不考虑隔离会产生很多问题
    2. 有三个读的问题:脏读、不可重复读、幻读
    3. 脏读:一个未提交事务读取到另一个未提交事务的数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3VDcKGJ5-1615205031515)(Spring5.assets/image-20210307152022304.png)]

​ d. 不可重复读:一个未提交事务读到一个提交事务的修改数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eo5x3DXh-1615205031515)(Spring5.assets/image-20210307152558738.png)]

​ e. 幻读:一个未提交事务读到一个提交事务添加数据

​ f. 解决:通过设置事务隔离级别,解决读问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eJPUcCf3-1615205031516)(Spring5.assets/image-20210307152826876.png)]

@Service
@Transactional(propagation = Propagation.REQUIRED,isolation =Isolation.REPEATABLE_READ)
public class UserService {
  1. timeout:超时时间

    1. 事务需要在一定时间内进行提交,如果不提交进行回滚
    2. 默认值是-1,设置时间以秒单位进行计算
  2. readOnly:是否只读

    1. 读:查询操作,写:增删改
    2. readOnly默认值false,表示可以查询,可以增删改操作
    3. 设置readOnly值是true,设置成true之后,只能查询
  3. rollbackFor:回滚

    1. 设置出现哪些异常进行事务回滚
  4. noRollbackFor:不回滚

    1. 设置出现哪些异常不进行事务回滚

事务操作(XML声明式事务管理)

  1. 在spring配置文件中进行配置

    第一步 配置事务管理器

    第二步 配置通知

    第三步 配置切入点和切面

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

    <!--2.配置通知-->
    <tx:advice id="txadvice">
        <!--指定哪种规则的方法上面添加事务-->
        <tx:attributes>
            <tx:method name="account" propagation="REQUIRED"/>
<!--            <tx:method name="account*"></tx:method>-->
        </tx:attributes>
    </tx:advice>

    <!--3.配置切入点和切面-->
    <aop:config>
        <!--配置切点-->
        <aop:pointcut id="pt" expression="execution(* com.company.service.UserService.*(..))"/>
        <!--配置切面-->
        <aop:advisor advice-ref="txadvice" pointcut-ref="pt"></aop:advisor>
    </aop:config>

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

  1. 创建配置类,使用配置类替代xml配置文件
@Configuration  //配置类
@EnableTransactionManagement   //开启事务
@ComponentScan(basePackages = "com.company")  //组件扫描
public class TxConfig {

    //创建数据库连接池
    @Bean
    public DruidDataSource getDruidDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/userdb");
        dataSource.setUsername("root");
        dataSource.setPassword("password");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        return dataSource;
    }

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

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

Spring5框架新功能

  1. 整个Spring5框架的代码基于Java8,运行时兼容JDK9,许多不建议使用的类和方法在代码库中删除

  2. Spring5框架自带了通用的日志封装

    1. Spring5已经移除Log4jConfigListener,官方建议使用Log4j2

    2. Spring5框架整合Log4j2

      第一步 引入jar包

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aEceCMp6-1615205031516)(Spring5.assets/image-20210308091422275.png)]

      第二步 配置log4j2.xml文件

image-20210308091645557
  1. Spring5框架核心容器支持@Nullable注解

    1. @Nullable注解可以使用在方法上面,属性上面,参数上面,表示方法返回可以为空,属性值可以为空,参数值可以为空

    2. 注解用在方法上面,方法返回值可以为空

      image-20210308145902105
    3. 注解使用在方法参数里面,方法参数可以为空

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZOxZMSQH-1615205031517)(Spring5.assets/image-20210308150139822.png)]

    4. 注解使用在属性上面,属性值可以为空

      image-20210308150305749
  2. Spring核心容器支持函数式风格GenericApplicationContext

        @Test
        //函数式风格创建对象,交给Spring进行管理
        public void testGeneticApplicationContext() {
            //1.创建GeneticApplicationContext对象
            GenericApplicationContext context = new GenericApplicationContext();
            //2.调用context方法进行对象注册
            context.refresh();
            context.registerBean("user1",User.class, () -> new User());  //lambda表达式
            //3.获取在spring注册的对象
            //方式一
    //        User user = (User)context.getBean("com.company.entity.User");
            //方式二
            User user = (User)context.getBean("user1");
            System.out.println(user);
        }
    
  3. 整合JUnit5

    1. Spring5整合JUnit4

    第一步 引入Spring相关针对测试依赖

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X681hx0m-1615205031518)(Spring5.assets/image-20210308152359256.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nQvs1ZIO-1615205031519)(Spring5.assets/image-20210308153126732.png)]

​ 第二步 创建测试类,使用注解方式完成

@RunWith(SpringJUnit4ClassRunner.class)    //单元测试框架
@ContextConfiguration("classpath:bean1.xml") //加载配置文件
public class JTest4 {

    @Autowired
    private UserService userService;

    @Test
    public void testJUnit4() {
        userService.account(100,"mary","lucy");
    }
}

​ b. Spring5整合JUnit5

​ 第一步 引入JUnit5的jar包

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HUHBOIa4-1615205031519)(Spring5.assets/image-20210308154032238.png)]

​ 第二步 创建测试类,使用注解完成

@RunWith(SpringJUnit4ClassRunner.class)    //单元测试框架
@ContextConfiguration("classpath:bean1.xml")  //加载配置文件
public class JTest4 {

    @Autowired
    private UserService userService;

    @Test
    public void testJUnit4() {
        userService.account(100,"mary","lucy");
    }
}

​ c. 使用一个复合注解替代上面两个注解完成整合

@SpringJUnitConfig(locations = "classpath:bean1.xml")
public class JTest5 {

    @Autowired
    private UserService userService;

    @Test
    public void testJUnit5() {

        userService.account(100,"mary","lucy");
    }
}

方法返回值可以为空

  <img src="Spring5.assets/image-20210308145902105.png" alt="image-20210308145902105" style="zoom:80%;" />
  1. 注解使用在方法参数里面,方法参数可以为空

    [外链图片转存中…(img-ZOxZMSQH-1615205031517)]

  2. 注解使用在属性上面,属性值可以为空

    image-20210308150305749
  3. Spring核心容器支持函数式风格GenericApplicationContext

        @Test
        //函数式风格创建对象,交给Spring进行管理
        public void testGeneticApplicationContext() {
            //1.创建GeneticApplicationContext对象
            GenericApplicationContext context = new GenericApplicationContext();
            //2.调用context方法进行对象注册
            context.refresh();
            context.registerBean("user1",User.class, () -> new User());  //lambda表达式
            //3.获取在spring注册的对象
            //方式一
    //        User user = (User)context.getBean("com.company.entity.User");
            //方式二
            User user = (User)context.getBean("user1");
            System.out.println(user);
        }
    
  4. 整合JUnit5

    1. Spring5整合JUnit4

    第一步 引入Spring相关针对测试依赖

[外链图片转存中…(img-X681hx0m-1615205031518)]

[外链图片转存中…(img-nQvs1ZIO-1615205031519)]

​ 第二步 创建测试类,使用注解方式完成

@RunWith(SpringJUnit4ClassRunner.class)    //单元测试框架
@ContextConfiguration("classpath:bean1.xml") //加载配置文件
public class JTest4 {

    @Autowired
    private UserService userService;

    @Test
    public void testJUnit4() {
        userService.account(100,"mary","lucy");
    }
}

​ b. Spring5整合JUnit5

​ 第一步 引入JUnit5的jar包

[外链图片转存中…(img-HUHBOIa4-1615205031519)]

​ 第二步 创建测试类,使用注解完成

@RunWith(SpringJUnit4ClassRunner.class)    //单元测试框架
@ContextConfiguration("classpath:bean1.xml")  //加载配置文件
public class JTest4 {

    @Autowired
    private UserService userService;

    @Test
    public void testJUnit4() {
        userService.account(100,"mary","lucy");
    }
}

​ c. 使用一个复合注解替代上面两个注解完成整合

@SpringJUnitConfig(locations = "classpath:bean1.xml")
public class JTest5 {

    @Autowired
    private UserService userService;

    @Test
    public void testJUnit5() {

        userService.account(100,"mary","lucy");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值