Spring个人笔记

课程流程

  1. Spring概念
  2. IOC容器
  3. AOP
  4. JDBCTemplate
  5. 事务管理
  6. Spring5新特性

Spring框架概述

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

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

  3. Spring有两个核心部分:AOC和AOP

    • IOC:控制反转,把创建对象过程交给Spring进行管理
    • AOP:面向切面,不修改源代码的情况下进行功能增强
  4. Spring特点

    1. 方便解耦,简化开发
    2. AOP编程的支持
    3. 方便程序的测试
    4. 方便集成整合其它框架
    5. 方便进行事务的操作
    6. 降低API开发难度

入门案例

Spring5模块image-20201005135337599

  1. 导入jar包,使用maven

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    
  2. 创建普通类,在这个类创建普通方法

    package com.yang;
    
    public class User {
        public void add(){
            System.out.println("add...");
        }
    }
    
  3. 创建spring配置文件,在配置文件中配置创建的对象

    • spring配置文件使用xml格式

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
      <!--配置User类对象创建-->
          <bean id="user" class="com.yang.User"/>
      </beans>
      
  4. 进行测试代码编写

    
    public class TestSpring5 {
        @Test
        public void testAdd() {
            //加载spring的配置文件
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
            //传值为xml中的id值
            User user = applicationContext.getBean("user", User.class);
            user.add();
            //获取配置创建的对象
        }
    }
    

IOC容器(控制反转)

1. IOC底层原理

  1. xml解析
  2. 工厂模式
  3. 反射

概念和原理

什么是IOC

  • 控制反转,把对象的创建和对象之间的调用过程,交给Spring进行管理
  • 使用IOC目的:为了耦合度降低
  • 做入门的案例就是IOC实现

工厂模式

目的:降低耦合度到最低限度

image-20201005174056198

IOC过程,进一步降低耦合度

  1. xml配置文件,配置创建的对象

    <bean id="dao" class="com.yang.UserDao"/>
    
  2. 第二步 有service和dao类,创建工厂类

    class UserFactory{
    	public static UserDao getDao{
    		String classValue = class属性值;//1.xml解析得到 
            //2.通过反射得到对象
            Class clazz = Class.forName(classValue);
            return (UserDao)clazz.newInstance();
    	}
    }
    

2. IOC接口(BeanFactory)

  1. IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
  2. Spring提供IOC容器实现的两种方式:(两个接口)
    • BeanFactory:IOC容器基本实现,是Spring内部使用的接口,不提供给开发人员使用,加载配置文件的时候不会创建对象,获取或使用时才去创建对象。
    • ApplicationContext: BeanFactory接口的子接口,提供更强大的功能。在加载配置文件的时候就会把配置对象进行创建。
  3. ApplicationContext接口有实现类image-20201005180402638

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

什么是Bean管理?包括创建对象与注入属性

  1. Spring创建对象
  2. Spring注入属性

3.1 基于xml方式创建对象

<bean id="user" class="com.yang.User"/>
  1. 在spring配置文件中,使用bean标签,标签中添加上对应属性,就可以实现对象创建
  2. 在bean标签中有很多属性,介绍常用属性
    • id属性:唯一标识
    • class属性:类的全路径
  3. 创建对象的时候默认执行无参的构造方法

3.2 基于xml方式注入属性

DI:依赖注入,就是注入属性,DI是IOC的一种实现方式

第一种注入方式,使用set注入

  • 实体类创建

    package com.yang;
    
    public class Book {
        private String bname;
        private String author;
    
        //set方法注入
        public void setBname(String bname, String author) {
            this.bname = bname;
            this.author = author;
        }
    
        public Book() {
    
        }
    
        public void setBname(String bname) {
            this.bname = bname;
        }
    
        public void setAuthor(String author) {
            this.author = author;
        }
    
        //有参构造注入
        public Book(String bname) {
            this.bname = bname;
        }
    
    }
    
  • bean文件配置

    <!--    set方法注入属性-->
    <bean id="book" class="com.yang.Book">
        <!--
        使用property完成属性注入
        name属性代表字段名称
        value为注入的值
        -->
        <property name="bname" value="天龙八部"/>
        <property name="author" value="杨剑"/>
    </bean>
    

第二种注入方式,使用有参构造注入

  • 创建类,定义属性,创建构造方法

    public class Orders {
        private String name;
        private String address;
    
        public Orders(String name, String address) {
            this.name = name;
            this.address = address;
        }
    }
    
  • 在spring配置文件中配置

    <bean id="orders" class="com.yang.Orders">
        <constructor-arg name="name" value="电脑"/>
        <constructor-arg name="address" value="中国"/>
    </bean>
    

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

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

  2. 在配置文件中添加p名称空间

    xmlns:p="http://www.springframework.org/schema/p"
    
  3. 进行属性注入,在bean标签里进行操作

    <bean id="book" class="com.yang.Book" p:bname="名字" p:author="作者"/>
    

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

  1. 字面量
  2. 注入属性-外部bean
  3. 注入属性-内部bean
  4. 级联赋值

字面量

  1. 空值

    <property name="author">
        <null/>
    </property>
    
  2. 属性值包含特殊符号

    • 转义:> <

    • CDATE

      <property name="author">
          <value>
              <![CDATA[
                  <<南京>>
              ]]>
          </value>
      </property>
      

注入属性-外部bean

  1. 创建两个类service和dao类

  2. 在service调用dao里面的方法

  3. 在Spring配置文件中进行配置

    <!--    service和dao对象的创建-->
    <bean id="userService" class="com.yang.service.UserService">
        <!--
        注入userDao对象
        name:属性值:类里面的属性名称
        -->
    
        <property name="userDao" ref="userDaoImpl"/>
    </bean>
    <bean id="userDaoImpl" class="com.yang.dao.UserDaoImpl"/>
    

注入属性-内部bean和级联赋值

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

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

  2. 在实体类中体现一对多关系

    员工表示所属部门,用对象表示

    //部门类
    public class Department {
        private String dName;
    
        public void setdName(String dName) {
            this.dName = dName;
        }
    }
    
    //员工类
    public class Emp {
        private String ename;
        private String gender;
        //员工属于某一个部门,使用对象形式表示
        private Department department;
        public void setEname(String ename) {
            this.ename = ename;
        }
    
        public void setGender(String gender) {
            this.gender = gender;
        }
    }
    
    
  3. 在spring配置文件中进行相关配置

        <!--内部bean-->
        <bean id="emp" class="com.yang.bean.Emp">
            <!--        先设置两个普通属性-->
            <property name="ename" value="ya"/>
            <property name="gender" value="男"/>
    <!--        对象类型属性-->
            <property name="department">
                <bean class="com.yang.bean.Department">
                    <property name="dName" value="公司"/>
                </bean>
            </property>
        </bean>
    

    注入属性:级联赋值

  4. 第一种方式

    <bean id="emp" class="com.yang.bean.Emp">
        <!--        先设置两个普通属性-->
        <property name="ename" value="ya"/>
        <property name="gender" value="男"/>
        <!--        对象类型属性-->
<!--        级联赋值-->
        <property name="department" ref="deparment"/>
    </bean>
    <bean id="deparment" class="com.yang.bean.Department">
        <property name="dName" value="财务部"/>
    </bean>
  1. 第二种方式,需要生成deparment的get方法
<bean id="emp" class="com.yang.bean.Emp">
        <!--        先设置两个普通属性-->
        <property name="ename" value="ya"/>
        <property name="gender" value="男"/>
        <!--        对象类型属性-->
<!--        级联赋值-->
        <property name="department" ref="department"/>
        <property name="department.dName" value="cai"/>
    </bean>
    <bean id="department" class="com.yang.bean.Department">
        <property name="dName" value="财务部"/>
    </bean>

xml注入集合属性

  1. 注入数组类型属性
  2. 注入List集合
  3. 注入Map集合类型的属性
  • 创建类,定义数组, list,map,set集合属性,生成对应的set方法

  • 在spring配置文件中配置

    <bean id="student" class="com.yang.collectionType.Student">
        <!--        数组类型-->
        <property name="courses">
            <array>
                <value>java</value>
                <value>数据库</value>
            </array>
        </property>
        <!--        list类型-->
        <property name="list">
            <list>
                <value>张三</value>
                <value>小三</value>
            </list>
        </property>
        <!--        Map类型-->
        <property name="maps">
            <map>
                <entry key="Java" value="java"/>
                <entry key="PHP" value="php"/>
            </map>
        </property>
        <!--        set-->
        <property name="set">
            <set>
                <value>123</value>
                <value>mysql</value>
            </set>
        </property>
    </bean>
    
  • 在集合里设置对象类型值

    <property name="courseList">
        <list>
            <ref bean="course1"/>
            <ref bean="course2"/>
        </list>
    </property>
    
    <bean id="course1" class="com.yang.collectionType.Course">
        <property name="cname" value="Spring5框架"/>
    </bean>
    <bean id="course2" class="com.yang.collectionType.Course">
        <property name="cname" value="MyBatis框架"/>
    </bean>
    
  • 把集合注入部分提取出来

    1. 在Spring配置文件中引入名称空间 util

      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:util="http://www.springframework.org/schema/util"
             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
                      http://www.springframework.org/schema/util
                      http://www.springframework.org/schema/util/spring-util.xsd
      ">
      
    2. 使用util标签提取

      <util:list id="bookList">
          <value>三国</value>
          <value>水浒</value>
      </util:list>
      <bean id="book" class="com.yang.collectionType.Book">
          <property name="list" ref="bookList"/>
      </bean>
      

IOC操作Bean管理(FactoryBean)

  1. Spring有两种类型的Bean,一种普通Bean,另一种:工厂Bean(FactoryBean)

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

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

    第一步:让这个类作为工厂Bean,实现接口FactoryBean

    <bean id="myBean" class="com.yang.factorybean.MyBean">
    
    </bean>
    

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

    public class MyBean implements FactoryBean<Course> {
        //定义返回Bean
        public Course getObject() throws Exception {
            Course course = new Course();
            course.setCname("abc");
            return course;
        }
    
        public Class<?> getObjectType() {
            return null;
        }
    
        public boolean isSingleton() {
            return false;
        }
    }
    

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

  4. 在Spring里面,设置创建bean实例是单实例还是多实例

  5. 在Spring里面,默认情况下,bean是单实例对象

    image-20201006124248107

image-20201006124320655

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

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

    • scope属性值

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

      第二个值 prototype,多实例对象

      image-20201006124715082

image-20201006124736770

scope和prototype的区别

第一:singleton:单实例

​ prototype:多实例

第二:设置scope为singleton的时候,加载配置文件的时候就会创建一个单实例对象

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

还有requestsession,一般不用,了解即可

生命周期: 从对象创建到对象销毁的的过程

bean生命周期

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

演示bean的生命周期image-20201006154715166

package com.yang.bean;

public class Orders {
    private String oname;

    public void setOname(String oname) {
        this.oname = oname;
        System.out.println("第二部,调用set方法设置属性值");
    }
    public Orders(){
        System.out.println("第一步,无参构造创建bean实例");
    }

    //创建执行的初始化方法
    public void initMethod(){
        System.out.println("第三步,执行初始化的方法");
    }
    //创建销毁的方法
    public void destroyMethod(){
        System.out.println("第五步,执行销毁的方法");
    }
}
public void test2(){
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
    Orders orders = context.getBean("orders", Orders.class);
    System.out.println("第四部,获取创建bean实例的对象");
    System.out.println(orders);
    //手动销毁bean实例
    context.close();
}

bean的后置处理器,bean生命周期变为七步

bean生命周期

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

添加后置处理器效果

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

    package com.yang.bean;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    
    public class MyBeanPost implements BeanPostProcessor {
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("在初始化之前执行的方法");
            return bean;
        }
    
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("在初始化之后执行的方法");
            return bean;
        }
    }
    
  2. 在bean中配置,自动为所有bean加上

    <bean id="myBeanPost" class="com.yang.bean.MyBeanPost"/>
    

xml自动装配

什么是自动装配?

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

演示自动装配过程

  1. 根据属性名称进行自动注入 byName

    <bean id="emp" class="com.yang.autowire.Emp" autowire="byName">
        <!--        <property name="dept" ref="dept"/>-->
    </bean>
    <bean id="dept" class="com.yang.autowire.Dept"/>
    
  2. 根据属性类型进行自动注入 byType

    <bean id="emp" class="com.yang.autowire.Emp" autowire="byType">
        <!--        <property name="dept" ref="dept"/>-->
    </bean>
    <bean id="dept" class="com.yang.autowire.Dept"/>
    

外部属性文件

  1. 直接配置数据库信息,配置druid连接池

    • 配置druid连接池

    • 引入druid连接池依赖

      <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
          <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
          <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true"/>
          <property name="username" value="root"/>
          <property name="password" value="123456"/>
      </bean>
      
  2. 引入外部属性文件配置数据库连接池

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

      prop.driverClass=com.mysql.jdbc.Driver
      prop.url=jdbc:mysql://localhost:3306/mybatis?useSSL=true
      prop.userName=root
      prop.password=123456
      
    • 把外部properties属性文件引入到Spring配置文件中

      引入context名称空间

      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:context="http://www.springframework.org/schema/context"
             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
             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 name="url" value="${prop.url}"/>
          <property name="username" value="${prop.userName}"/>
          <property name="password" value="${prop.password}"/>
      </bean>
      

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

  1. 什么是注解
  2. 使用注解的目的:简化xml操作

spring针对Bean管理中创建对象提供注解

  1. @Component

  2. @Service

  3. @Controller

  4. @Respository

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

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

  1. 引入AOP依赖

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    
    
  2. 开启组件扫描

    <!--
    开启组件扫描
    1.如果扫描多个包,多个包用逗号隔开
    2.扫描上层目录
    -->
    <context:component-scan base-package="com.yang"/>
    
  3. 创建类,在类上面添加对象注解

    //在注解里的value属性值可以省略不写
    //默认值是类的名称,把首字母小写
    @Service(value = "userService") //<bean id="userService" class=""/>
    public class UserService {
        public void add() {
            System.out.println("service add..............");
        }
    }
    

开启组件扫描细节配置

<!--    示例1
use-default-filters="false"  表示现在不使用默认的filter,而是使用自己配置的filter
context:include-filter:设置要扫描哪些内容
-->
<context:component-scan base-package="com.yang" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>

</context:component-scan>
<!--    事例2:
        context:exclude-filter  设置哪些内容不进行扫描
-->
<context:component-scan base-package="com.yang">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>

注解方式属性注入

  1. @Autowired:根据属性类型进行自动装配
  2. @Qualifier:根据属性名称进行注入
  3. @Resource:可以根据类型注入,也可以根据名称注入
  4. @Value:注入普通类型属性

@Autowired

  1. 把service和dao对象创建,在service和dao类添加创建对象的注解

  2. 在service注入dao,在service类添加dao类型的属性,在属性上面使用注解

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

@Qualifier:根据属性名称进行注入

  1. 这个@Qualifier注解的使用要和上面的@Autowired一起使用

    @Repository(value = "userDaoImpl1")
    public class UserDaoImpl implements UserDao{
        public void add() {
            System.out.println("dao add....");
        }
    }
    
    @Autowired //根据类型进行注入
    @Qualifier(value = "userDaoImpl1") //根据名称及逆行注入
    

@Resource:可以根据类型注入,也可以根据名称注入,包:javax.annotation.Resource

@Service(value = "userService") //<bean id="userService" class=""/>
public class UserService {
    //@Resource //根据类型进行注入
    @Resource(name = "userDaoImpl1")//根据名称进行注入
    private UserDao userDao;
    public void add() {
        System.out.println("service add..............");
        userDao.add();
    }
}

@Value:注入普通属性

@Value(value = "yj")
private String name;

完全注解开发

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

    @Configuration  //作为配置类 ,替代xml配置文件
    @ComponentScan(basePackages = {"com.yang"})
    public class SpringConfig {
    }
    
  2. 测试类

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

AOP(面向切面编程)

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

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

底层原理

  1. AOP底层使用动态代理方式
    • 有两种情况的动态代理
    • 有接口情况,使用JDK动态代理
    • 没有接口情况,使用CGLIB动态代理

有接口情况,使用JDK动态代理

创建UserDao接口实现类代理对象,增强类的方法image-20201007084912912

没有接口情况,使用CGLIB动态代理

创建当前类子类的代理对象image-20201007085245339

AOP(JDK动态代理)

  1. 使用JDK动态代理,使用Proxy类里面的方法创建代理对象image-20201007085625842
  2. 调用newProxyInstance方法来实现,有三个参数
    • 类加载器
    • 增强方法所在的类,类实现的接口,支持多个接口
    • 实现这个接口InvocationHandler,创建代理对象,写增强的方法

image-20201007085805054

  1. 编写JDK动态代理代码

    • 创建接口,定义方法

      public interface UserDao {
      
          int add(int a,int b);
          
          String update(String id);
      }
      
    • 创建接口实现类,实现方法

      public class UserDaoImpl implements UserDao{
          public int add(int a, int b) {
              return a+b;
          }
      
          public String update(String id) {
              return id; 
          }
      }
      
    • 使用Proxy创建接口的代理对象

      public class JDKProxy {
          public static void main(String[] args) {
              //创建接口实现类的代理对象
              UserDao userDao = new UserDaoImpl();
              Class[] interfaces = {UserDao.class};
              UserDao dao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
              int res = dao.add(1, 2);
              System.out.println("result = "+res);
          }
      }
      
      //创建代理对象代码
      class UserDaoProxy implements InvocationHandler {
          //把创建的代理对象的本体传递过来
          //有惨构造进行传递
          private Object obj;
      
          public UserDaoProxy(Object obj) {
              this.obj = obj;
          }
      
          //增强的逻辑
          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;
          }
      }
      

AOP术语

  1. 连接点,类里面哪些方法可以被增强,这些方法称为连接点
  2. 切入点,实际被增强的方法,称为切入点
  3. 通知(增强),
    • 实际增强的逻辑部分称为通知(增强)
    • 通知有多种类型,前置通知,后置通知,环绕通知,异常通知最终通知(finally)
  4. 切面:把通知应用到切入点的过程

AOP操作(准备)

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

    AspectJ:不是spring组成部分,独立AOP框架,一半把AspectJ与Spring框架一起使用。进行AOP相关操作

  2. 基于AspectJ实现AOP操作

    • 基于xml配置文件
    • 基于注解方式(使用)
  3. 在项目工程中引入AOP相关依赖

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    
  4. 切入点表达式

    • 切入点表达式的作用:知道对哪个类里面的哪个方法进行增强
    • 语法结构execution([权限修饰符][返回类型][全类名][方法名称]([参数列表])

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

    execution(* com.yang.dao.Book.add(..))

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

    execution(* com.yang.dao.Book.*(..))

    举例3:对com.yang.dao包里面的所有类里面的所有包进行增强

    execution(* com.yang.dao.*.*(..))

AOP操作(AspectJ注解)

  1. 创建类,在类里面定义方法,

    public class User {
        public void add(){
            System.out.println("add,,,,");
        }
    }
    
  2. 创建增强类(编写增强的逻辑)

    在增强类里面创建方法,让不同方法代表不同通知类型

    //增强的类
    public class UserProxy {
        //前置通知
        public void before(){
            System.out.println("before........");
        }
    }
    
  3. 进行通知的配置

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

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:context="http://www.springframework.org/schema/context"
             xmlns: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.yang.aopanno"/>
      </beans>
      
    • 使用注解创建Userh和UserProxy对象

      package com.yang.aopanno;
      
      import org.springframework.stereotype.Component;
      
      //被增强类
      @Component
      public class User {
          public void add(){
              System.out.println("add,,,,");
          }
      }
      
      package com.yang.aopanno;
      
      import org.springframework.stereotype.Component;
      
      //增强的类
      @Aspect
      @Component
      public class UserProxy {
          //前置通知
          public void before(){
              System.out.println("before........");
          }
      }
      
    • 在增强类上面添加注解@Aspect

    • 在Spring配置文件中,开启生成代理对象

      <aop:aspectj-autoproxy/>
      
    • 配置不同类型的通知

      在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式

package com.yang.aopanno;

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

//增强的类
@Component
@Aspect //生成代理对象
public class UserProxy {
    //前置通知
    //@Before注解表示作为前置通知
    @Before(value = "execution(* com.yang.aopanno.User.add(..))")
    public void before() {
        System.out.println("before........");
    }
    //最终通知
    @After(value = "execution(* com.yang.aopanno.User.add(..))")
    public void after() {
        System.out.println("after.........");
    }
    //后置通知(返回通知),有异常不执行
    @AfterReturning(value = "execution(* com.yang.aopanno.User.add(..))")
    public void afterReturning() {
        System.out.println("afterReturning....");
    }

    @AfterThrowing(value = "execution(* com.yang.aopanno.User.add(..))")
    public void afterThrowing() {
        System.out.println("afterThrowing....");
    }
    @Around(value = "execution(* com.yang.aopanno.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        System.out.println("环绕之前。。。");
        proceedingJoinPoint.proceed();
        System.out.println("环绕之后。。。");
    }
}

公共切入点提取

//相同切入点抽取
@Pointcut(value = "execution(* com.yang.aopanno.User.add(..))")
public void pointDemo(){

}
@Before(value = "pointDemo()")
public void before() {
    System.out.println("before........");
}

有多个增强类对同一个方法进行增强,设置增强类优先级

  1. 在增强类上面添加注解@Order(数字类型值),数字类型值越小,优先级越高

    @Component//对象创建
    @Aspect//代理对象创建
    @Order(1)
    public class PersonProxy {
        @Before(value = "execution(* com.yang.aopanno.User.add(..))")
        public void before(){
            System.out.println("Person before......");
        }
    }
    
  2. 
    @ComponentScan(basePackages = {"com.yang"})
    @Configuration
    @EnableAspectJAutoProxy(proxyTargetClass = true)
    public class MyConfig {
    
    }
    

AOP操作(AspectJ配置文件)

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

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

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

        <!--    创建对象-->
        <bean id="book" class="com.yang.aopxml.Book"/>
    
        <bean id="bookProxy" class="com.yang.aopxml.BookProxy"/>
    <!--    配置aop增强-->
        <aop:config>
    <!--        配置切入点-->
            <aop:pointcut id="p" expression="execution(* com.yang.aopxml.Book.buy(..))"/>
    <!--        配置切面-->
            <aop:aspect ref="bookProxy">
    <!--            增强在具体的方法上-->
                <aop:before method="before" pointcut-ref="p"/>
            </aop:aspect>
        </aop:config>
    

JDBCTemplate

概念和准备

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

  1. 引入依赖

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>org.springframework.orm</artifactId>
        <version>3.1.2.RELEASE</version>
    </dependency>
    
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.21</version>
    </dependency>
    
  2. 在Spring配置文件配置连接池

        <context:property-placeholder location="classpath:database.properties"/>
    <!--    数据库连接池-->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
            <property name="url" value="${prop.url}"/>
            <property name="username" value="${prop.userName}"/>
            <property name="password" value="${prop.password}"/>
            <property name="driverClassName" value="${prop.driverClass}"/>
        </bean>
    
  3. 配置JdbcTemplate对象,注入dataSource

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

    配置文件中开启组件扫描

    <context:component-scan base-package="com.yang"/>
    

    Dao层

    @Repository
    public class BookDaoImpl implements BookDao{
        //注入JDBCTemplate
        @Autowired
        private JdbcTemplate jdbcTemplate;
    }
    

    Service层

    @Service
    public class BookService {
        //注入Dao
        @Autowired
        private BookDao bookDao;
    }
    

JdbcTemplate操作数据库(添加)

  1. 创建实体类

    package com.yang.entity;
    
    public class User {
        private String id;
        private String name;
        private String pwd;
    
        @Override
        public String toString() {
            return "User{" +
                    "id='" + id + '\'' +
                    ", name='" + name + '\'' +
                    ", pwd='" + pwd + '\'' +
                    '}';
        }
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getPwd() {
            return pwd;
        }
    
        public void setPwd(String pwd) {
            this.pwd = pwd;
        }
    }
    
  2. 编写service和dao

    • 在dao进行数据库添加操作

      image-20201008115447064

      两个参数:1. sql语句

      ​ 2.可变长参数

      @Repository
      public class BookDaoImpl implements BookDao{
          //注入JDBCTemplate
          @Autowired
          private JdbcTemplate jdbcTemplate;
      
          public void add(Book book) {
      //        1.创建sql语句
              String sql = "insert into mybatis.book values(?,?,?)";
      //        2.调用方法实现
              Object[] args = {book.getId(), book.getName(), book.getPwd()};
              int update = jdbcTemplate.update(sql,args);
              System.out.println(update);
          }
      }
      

修改与删除操作

public void updateBook(Book book) {
    String sql = "update mybatis.book set name=?,pwd=? where id=?";
    Object[] args = {book.getName(),book.getPwd(),book.getId()};
    int update = jdbcTemplate.update(sql,args);
    System.out.println(update);
}

public void deleteBook(Integer id) {
    String sql = "delete from mybatis.book where id = ?";
    Object[] args = {id};
    int delete = jdbcTemplate.update(sql,args);
    System.out.println(delete);
}

查询返回某个值

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

  2. 使用JDBCTemplate实现查询返回某个值image-20201008132548814

    两个参数:sql语句,返回类型的Class

    public int findCount() {
        String sql = "select count(*) from mybatis.book";
        int count = jdbcTemplate.queryForObject(sql,Integer.class);
        return count;
    }
    

查询返回对象

image-20201008172818111

第一个参数:sql语句

第二个参数:RowMapper,是接口,返回不同类型的数据,使用这个接口里面的实现类可以完成数据封装

第三个参数:可变长参数

查询返回集合

  1. 查询图书列表分页
  2. 调用JDBCTemplate方法实现查询返回集合image-20201008174030883

批量操作

  1. JdbcTemplate实现批量添加的操作image-20201008181126843

    有两个参数

    第一个参数:sql语句

    第二个参数:List集合,添加多条记录数据

    public void batchAddBook(List<Object[]> batchArgs) {
        String sql = "insert into mybatis.book values (?,?,?)";
        int[] res = jdbcTemplate.batchUpdate(sql, batchArgs);
        System.out.println(Arrays.toString(res));
    }
    

实现批量修改操作

注意:在传值的时候要按照问号的顺序传

@Override
public void batchUpdate(List<Object[]> batchArgs) {
    String sql = "update mybatis.book set name=?,pwd=? where id=?";
    int[] res = jdbcTemplate.batchUpdate(sql, batchArgs);
    System.out.println(Arrays.toString(res));
}

实现批量删除操作

@Override
public void batchDelete(List<Object[]> batchArgs) {
    String sql = "delete from mybatis.book where id = ?";
    int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
    System.out.println(Arrays.toString(ints));
}

事务操作

概念

什么是事务

  1. 事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败,所有操作都失败
  2. 典型场景:银行转账

事务四个特性(ACID):

  1. 原子性:不可分割
  2. 一致性:总量不变
  3. 隔离性:两个人同时操作,不会对彼此产生影响
  4. 持久性:提交后表中数据发生真正变化

搭建环境

image-20201009080706605

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

  2. 创建service,搭建dao,完成对象的创建和注入。

    • service注入dao
    • dao注入jdbctemplate,在JDBCTemplate注入dataSource
    • 在dao创建两个方法,多钱和少钱,在service创建转账方法
    • 问题,在有异常时一半的方法执行

事务操作过程

        try {
            userDao.reduceMoney();
//        模拟异常
            int i = 1 / 0;
            userDao.addMoney();
            //没有发生异常,事务提交
        } catch (Exception e) {
            //出现了异常,事务回滚
            e.printStackTrace();
        }

Spring事务管理介绍

  1. 事务一般添加在javaee三层结构里的Service层
  2. 在Spring进行事务管理操作有两种方式
    • 编程式事务管理
    • 声明式事务管理(使用)基于注解方式,基于xml配置文件方式

声明式事务管理

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

Spring事务管理API

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

注解方式实现声明式事务管理

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

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

    • 在spring配置文件中引入名称空间tx

      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:context="http://www.springframework.org/schema/context"
             xmlns:tx="http://www.springframework.org/schema/tx"
             xsi:schemaLocation="http://www.springframework.org/schema/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/tx
             http://www.springframework.org/schema/tx/spring-tx.xsd">
      
    • 开启事务

      <!--    开启事务注解-->
      <tx:annotation-driven transaction-manager="transactionManager"/>
      
  3. 在Service类上面(或者service类方法上面)添加事务注解

    • @Transactional 加在类和方法上均可

    • 如果添加在类上面,代表类里面的所有方法都添加了事务

    • 如果放在方法上面,只是为这个方法添加事务

      @Service(value = "userServiceImpl")
      @Transactional //加在类和方法上均可
      public class UserServiceImpl implements UserService {
          @Autowired
          @Qualifier("userDaoImpl")
          private UserDao userDao;
      
          public void accountMoney() {
              userDao.reduceMoney();
              int i = 1 / 0;
              userDao.addMoney();
      
          }
      }
      

声明式事务管理参数配置

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

  2. propagation:事务传播行为

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

    事务方法:对数据库表进行变化的操作

    七种传播行为

    image-20201009104240840

  3. isolation:事务的隔离级别

    事务有特性:隔离性,多事务操作之间不会产生影响。不考虑隔离性会产生很多问题

    有三个读的问题,脏读,不可重复读,虚(幻)读

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

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

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

    通过设置事务隔离级别,就能解决三个读的问题image-20201009114356882

    @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ) 
    
  4. timeout超时时间。

    事务需要在一定时间内进行提交,如果不提交,就会回滚

    默认值是-1,设置时间以秒(s)为单位

  5. readonly:是否只读

    读:查询操作,写:添加修改删除

    readOnly默认为false

    设置为true后,只能查询

  6. rollbackfor,回滚

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

  7. norollbackfor:不回滚

    设置出现哪些异常不进行回滚

XML声明式事务管理

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

    第一步:配置事务管理器

    第二步:配置通知

    第三步:配置切入点和切面

    <!--    配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--    配置通知-->
    <tx:advice id="txadvice">
        <!--        配置事务相关参数-->
        <tx:attributes>
            <!--            指定在哪种规则的方法上添加事务-->
            <tx:method name="accountMoney" propagation="REQUIRED"/>
            <!--            <tx:method name="account*"/>-->
        </tx:attributes>
    </tx:advice>
    
    <!--    配置切入点和切面-->
    <aop:config>
        <!--        配置切入点-->
        <aop:pointcut id="pt" expression="execution(* com.yang.service.UserServiceImpl.*(..))"/>
        <!--        配置切面-->
        <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
    </aop:config>
    

完全注解开发

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

    @Configuration
    @ComponentScan(basePackages = "com.yang")
    @EnableTransactionManagement //开启事务
    public class TxConfig {
        //创建数据库连接池
        @Bean
        public DruidDataSource dataSource() {
            DruidDataSource dataSource = new DruidDataSource();
            dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
            dataSource.setUrl("jdbc:mysql://localhost:3306/mybatis?useSSL=true&serverTimezone=Asia/Shanghai&CharacterEncoding=utf-8");
            dataSource.setUsername("root");
            dataSource.setPassword("123456");
            return dataSource;
        }
    
        //创建jdbcTemplate模板对象
        @Bean
        public JdbcTemplate jdbcTemplate(DataSource dataSource) {
            //到ioc容器中根据类型找到dataSource
            JdbcTemplate jdbcTemplate = new JdbcTemplate();
            jdbcTemplate.setDataSource(dataSource);
            return jdbcTemplate;
        }
    
        //创建事务管理器
        @Bean
        public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource) {
            DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
            dataSourceTransactionManager.setDataSource(dataSource);
            return dataSourceTransactionManager;
        }
    }
    

Spring5框架新功能

  1. 整个Spring5框架基于java8,运行时兼容jdk9,许多不建议使用的类和方法在代码库中删除
  2. spring5框架自带了通用的日志封装
  3. Spring5已经移除了Log4jConfigListener,官方建议使用Log4j2

Spring5框架整合Log4j2

  1. 引入依赖

    <dependencies>
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-api</artifactId>
                <version>2.12.1</version>
            </dependency>
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-core</artifactId>
                <version>2.12.1</version>
            </dependency>
        </dependencies>
    
  2. 创建log4j2.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration>
        <!--<Configuration status="WARN" monitorInterval="30"> -->
        <Appenders>
            <!--*********************控制台日志***********************-->
            <console name="Console" target="SYSTEM_OUT">
                <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
            </console>
        </Appenders>
    
        <Loggers>
            <!-- 根日志设置 -->
            <root level="info">
                <appender-ref ref="Console"/>
            </root>
        </Loggers>
    
    </Configuration>
    

Spring5框架核心容器支持@Nullable注解

@Nullable可以使用在方法上,属性上面,参数上面

  1. 用在方法上,表示方法返回值可以为空
  2. 注解用在方法参数里面,表示参数值可以为空
  3. 用在属性上,属性值可以为空
  4. Spring5核心容器里面支持函数式风格

支持整合JUnit5

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

SpringWebFlux

posted @ 2020-12-12 10:13  void-white  阅读(66)  评论(0)    收藏  举报