Spring基础学习

一 Spring 框架概述

1.1 Spring 概述

轻量级开源的 JavaEE 框架。解决企业开发应用的复杂性。
Spring的核心部分:

  • IOC:控制反转(Inversion of Controll),将创建对象的过程交由 Spring 管理
  • AOP:面向切面编程(Aspect Orient Programming),不修改源代码的情况进行功能增强

特点:

  1. 解耦、简化开发
  2. Aop 编程支持
  3. 方便程序测试
  4. 方便集成框架
  5. 方便进行事物的操作
  6. 降低 API 开发难度

1.2 入门案例

版本:5.2.6
依赖:

<dependency>
  <groupId>org.springframeworkgroupId>
  <artifactId>spring-beansartifactId>
  <version>5.2.8.RELEASEversion>
dependency>
<dependency>
  <groupId>org.springframeworkgroupId>
  <artifactId>spring-contextartifactId>
  <version>5.2.8.RELEASEversion>
dependency>
<dependency>
  <groupId>org.springframeworkgroupId>
  <artifactId>spring-coreartifactId>
  <version>5.2.8.RELEASEversion>
dependency>
<dependency>
  <groupId>org.springframeworkgroupId>
  <artifactId>spring-expressionartifactId>
  <version>5.2.8.RELEASEversion>
dependency>
<dependency>
  <groupId>commons-logginggroupId>
  <artifactId>commons-loggingartifactId>
  <version>1.2version>
dependency>

demo01:创建普通类

public class User {

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

}

resources 目录下创建bean1.xml文件:


<beans
  xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
>
  <bean id="user" class="com.pyd.User"> bean>
beans>

进行测试代码编写:

    @Test
    public void testAdd() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        // 获取id为user的类
        User user = context.getBean("user", User.class);
        System.out.println(user);
        // 调用方法
        user.add();
    }

getBean方法的Bean是什么?

官方的解释是 Spring Bean是事物处理组件类和实体类(POJO)对象的总称,是能够被实例化、能够被spring容器管理的java对象。可以把bean看做是一个组件,这个组件用来具体实现某个业务功能。总结性的讲,Bean就是由IOC容器初始化、装配及管理的对象,除此之外,和程序中的其他对象没有区别。简单来说,就是Java对象,但是能够被Spring容器管理。

二 IOC

2.1 IOC 概述

控制反转:交给Spring管理

控制反转:

  • 面向对象的设计原则,降低耦合度
  • 将对象创建和之间的调用过程,交给 Spring 管理

底层原理

底层原理:

  • xml 解析、工厂模式、反射
  • IOC 的思想基于 IOC 容器完成,IOC 的容器底层就是对象工厂

IOC 的过程

IOC 的过程:

  1. XML 配置文件,配置创建的对象
  2. service 和 dao 类,创建工厂类
    通过 xml 文件获取类的路径
    通过反射创建对象

Spring提供的IOC接口

  1. Spring 提供 IOC 容器的两种方式:
  • BeanFactory:
    • IOC 容器的基本实现,是 Spring 内部的使用接口,不允许开发人员进行实现
    • 加载配置文件时,不创建对象,使用时创建对象
  • ApplicationContext:
    • BeanFactory 的子接口,面向开发人员使用
    • 加载配置文件时,创建对象

二者区别:是否在加载配置文件时,创建对象。

ApplicationContext的实现类

ApplicationContext 的实现类Spring基础学习_第1张图片
绝对路径和类路径的区别

2.2 IOC 操作 Bean 管理

Bean 管理:

  • Spring 创建
  • Spring 注入属性

Bean 管理的两种方式:

  • 基于 xml 配置文件
  • 基于注解的实现

创建对象
使用 bean 标签,在标签中添加属性,创建对象,常用属性:

  • id:唯一标识
  • class 属性:类全路径
  • name:可以加特殊符号

创建对象时,默认执行无参方法

注入属性
DI:依赖注入(Dependency Injection),是IOC的另一种表述方式,即组件以一些预先定义好的方式(如:getter方法)来接收来自容器的资源注入,即注入属性

2.2.1 方法一:使用 set 方法实现对Bean的属性注入

  1. 创建属性,创建属性对应的 set 方法。
public class Book {

    String author;
    String title;

    public void setAuthor(String author) {
        this.author = author;
    }

    public void setTitle(String title) {
        this.title = title;
    }

}

  1. 在 spring 配置文件中,配置属性的注入
<bean id="book" class="com.pyd.Book">
  
  <property name="author" value="张浩瀚">property>
  <property name="title" value="武汉理工优秀学生">property>
bean>

使用 p 名称空间注入(需要有 set 方法):


<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"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
>
  
  <bean
    id="book"
    class="com.pyd.Book"
    p:author="张浩瀚"
    p:title="武汉理工优秀学生"
  >
  bean
>beans>

2.2.2 方法二:有参数的构造注入

public class Order {
    String name;
    String id;

    public Order(String name, String id) {
        this.name = name;
        this.id = id;
    }
}

在 xml 中配置:

<bean id="order" class="com.pyd.Order">
  <constructor-arg index="0" value="电脑" />
  <constructor-arg name="id" value="1" />
bean>

2.3 IOC 设置属性其他类型

2.3.1 字面量

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

转义:

<property name="address" value="<>">
property>

CDATA:

<property>
  <value> >]]> value>
property>

2.3.2 注入属性:外部 bean

  1. 创建 service 和 dao
public interface UserDao {
    public void update();
}
public class UserDaoImp implements UserDao {

    @Override
    public void update() {
        System.out.println("userDaoImp update() called");
    }
}
  1. serivce 调用 dao 的方法
public class User {
    private UserDao userDao;

    public void setUserDao(UserDaoImp userDao) {
        this.userDao = userDao;
    }

    public void test() {
        this.userDao.update();
    }

}
  1. 在 xml 中实现注入外部 bean
<bean id="userSvc" class="com.pyd.service.User">
  <property name="userDao" ref="userDaoImp" />
bean>

<bean id="userDaoImp" class="com.pyd.dao.UserDaoImp" />

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

  1. 一对多的关系:部门和员工
    一个部门有多个员工,一个员工属于一个部门。

  2. 创建 emp 和 dept 类

public class Dept {

    String name;

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

    @Override
    public String toString() {
        return "dept{" +
                "name='" + name + '\'' +
                '}';
    }
}
public class Emp {
    String name;
    String gender;
    Dept dept;

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

    public void setGender(String gender) {
        this.gender = gender;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }
}
  1. 编写 xml

方式一:

<bean id="emp" class="com.pyd.bean.Emp">
  <property name="name" value="张浩瀚" />
  <property name="gender" value="" />
  <property name="dept">
    <bean id="dept" class="com.pyd.bean.Dept">
      <property name="name" value="帅哥部" />
    bean>
  property>
bean>

方式二:级联赋值

<bean id="emp" class="com.pyd.bean.Emp">
  <property name="name" value="张浩瀚" />
  <property name="gender" value="" />
  <property name="dept" ref="dept"> property>
bean>

<bean id="dept" class="com.pyd.bean.Dept">
  <property name="name" value="帅哥部" />
bean>

需要 get 方法的写法:

<bean id="emp" class="com.pyd.bean.Emp">
  <property name="name" value="张浩瀚" />
  <property name="gender" value="" />
  <property name="dept" ref="dept" />
  
  <property name="dept.name" value="大帅哥部" /> 
bean>

<bean id="dept" class="com.pyd.bean.Dept"/>

2.4 xml 注入集合属性

集合类型属性的创建:

public class Stu {

    private String[] courses;
    private List<String> list;
    private Map<String, String> map;

    private Set<String> set;

    public void setSet(Set<String> set) {
        this.set = set;
    }

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

    public void setMap(Map<String, String> map) {
        this.map = map;
    }

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

2.4.1 数组类型属性

<property name="courses">
  <array>
    <value>1value>
    <value>2value>
    <value>3value>
  array>
property>

2.4.2 List 集合类型属性

<property name="list">
  <list>
    <value>1value>
    <value>2value>
    <value>3value>
  list>
property>

2.4.3 Map 集合类型属性

<property name="map">
  <map>
    <entry key="zhh" value="天下第一帅" />
  map>
property>

2.4.4 在集合中设置对象类型的值

<bean id="course1" class="com.pyd.bean.Course">
  <property name="name" value="张浩瀚帅哥入门课上" />
  <property name="id" value="1" />
bean>

<bean id="course2" class="com.pyd.bean.Course">
  <property name="name" value="张浩瀚帅哥入门课下" />
  <property name="id" value="2" />
bean>
<property name="courseList">
  <list>
    <ref bean="course1" />
    <ref bean="course2" />
  list>
property>

2.4.5 将集合注入部分提取出来

  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">
beans>
  1. 提取 list 集合属性注入
<util:list id="bookList">
  <value>book1value>
  <value>book2value>
util:list>

<bean name="book" class="com.pyd.bean.Book">
  <property name="books" ref="bookList" />
bean>

2.5 IOC 操作 Bean 管理(FactoryBean)

  • 普通 bean:定义的类型和返回类型相同
  • 工厂 bean:定义的类型和返回类型可以不相同
  1. 创建类,作为工厂 bean,实现接口 FactoryBean
public class MyBean implements FactoryBean {

}
  1. 实现接口的方法,在方法中定义返回的 Bean 类型
    @Override
    public Object getObject() throws Exception {
        Course course = new Course();
        course.setName("张浩瀚");
        return course;
    }
  1. 配置文件 xml
<bean id="myBean" class="com.pyd.factoryBean.MyBean">bean>

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

2.6.1 单实例和多实例

创建的对象,默认是单实例。(比较地址是否相同)

设置多实例对象:

scope 属性:

  • singleton:默认值,加载配置文件时就会创建,在每个 Spring ioc 容器中只有一个实例。
  • prototype:多实例对象,在调用 getBean 方法时创建多实例对象
  • request:将单个 bean 定义的范围限定为单个 HTTP 请求的生命周期
  • session:将单个 bean 定义的作用域限定为 HTTP 会话的生命周期。
  • application:将单个 bean 定义的作用域限定为 ServletContext 的生命周期。
  • websocket:将单个 bean 定义的作用域限定为 WebSocket 的生命周期。
<bean id="myBean" class="com.pyd.factoryBean.MyBean" scope="prototype">bean>

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

从对象创建到销毁的过程

2.7.1 bean 声明周期

  1. 通过构造器创建 bean 的实例(无参数构造)
  2. 为 bean 的属性设置值和其他 bean 引用(调用 set 方法)
  3. 调用 bean 的初始化方法(需要配置初始化方法)
  4. 可以使用 bean,获取到了对象
  5. 容器关闭时,调用 bean 的销毁方法(需要配置销毁的方法)
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
        context.close();
1. 无参构造
2. 设置name
3. 执行初始化方法
4. 获取到了bean对象
5. 销毁对象方法执行

后置处理器:

  1. 通过构造器创建 bean 的实例(无参数构造)
  2. 为 bean 的属性设置值和其他 bean 引用(调用 set 方法)
  3. 把 bean 实例传递给 bean 后置处理器 (postProcessBeforeInitialization)
  4. 调用 bean 的初始化方法(需要配置初始化方法)
  5. 把 bean 实例传递给 bean 后置处理器 (postProcessAfterInitialization)
  6. 可以使用 bean,获取到了对象
  7. 容器关闭时,调用 bean 的销毁方法(需要配置销毁的方法)
<bean name="myPostBean" class="com.pyd.factoryBean.MyBeanPost">bean>
无参构造
设置name
bean后置处理器 postProcessBeforeInitialization
执行初始化方法
bean后置处理器 postProcessAfterInitialization
获取到了bean对象
销毁对象方法执行

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

实际工作中,用得很少

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

autowire 属性:

  • byName: 根据属性名称注入,bean 的 id 和属性值相同
  • byType: 根据属性类型注入,相同类型的不能定义多个

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

2.8.1 直接配置数据库信息

<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/fruitdb" />
  <property name="username" value="root" />
  <property name="password" value="zhanghaohanzuishuai" />
bean>

2.8.2 引入外部文件配置数据库连接池

数据库文件jdbc.property

username=root
password=zhanghaohanzuishuai
url=jdbc:mysql://localhost:3306/fruitdb?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
driverClass=com.mysql.cj.jdbc.Driver

引入 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"
>
beans>

引入外部属性文件:

<context:property-placeholder location="classpath:jdbc.properties" />
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
  <property name="driverClassName" value="${driverClass}" />
  <property name="url" value="${url}" />
  <property name="username" value="${username}" />
  <property name="password" value="${password}" />
bean>

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

依赖:

<dependency>
  <groupId>org.springframeworkgroupId>
  <artifactId>spring-aopartifactId>
  <version>5.2.8.RELEASEversion>
dependency>

2.9.1 注解

  1. 特殊的代码标记格式
  2. 可以作用于类、方法、属性
  3. 简化 XML 配置

2.9.2 Spring 针对 Bean 管理中创建对象提供的注解

  1. Component
  2. Service
  3. Controller
  4. Repositary

功能相同

2.9.3 实现对象的创建

  1. 开启组件扫描

扫描多个包:用逗号分隔
扫描包上层目录

<context:component-scan base-package="com.pyd.aop">context:component-scan>
  1. 创建类,并添加注解

注解的 value 默认是类的名称,首字母小写。

@Component("userService")
public class UserService {
    public void add() {
        System.out.println("user add");
    }
}
  1. 组件扫描的细节

<context:component-scan base-package="com.atguigu" use-default- filters="false">
  <context:include-filter
    type="annotation"
    expression="org.springframework.stereotype.Controller"
  />
context:component-scan>

<context:component-scan base-package="com.atguigu">
  <context:exclude-filter
    type="annotation"
    expression="org.springframework.stereotype.Controller"
  />
context:component-scan>

Spring 针对 Bean 中属性的注入提供的注解

  1. Autowried:根据属性类型自动装配
  2. Qualifier:根据属性名称自动注入
  3. Value: 注入普通类型

2.9.3 根据类型实现属性注入

  1. 创建 service 和 dao 对象,并添加创建对象的注解

接口对象:

public interface UserDao {
    public void print();
}

接口实现类:

@Repository
public class UserDaoImp implements UserDao {

    @Override
    public void print() {
        System.out.println("userDaoImp print");
    }
}
  1. 在 service 定义 dao 类型属性,并加上注解(不需要添加 set 方法)
@Component("userService")
public class UserService {

    @Autowired
    private UserDao userDao;

    public void add() {
        System.out.println("user add");
        userDao.print();
    }
}

2.9.4 根据属性名称进行注入

场景:一个接口有多个实现类

需要和 Autowired 一起使用

@Repository("userDaoImp1")
public class UserDaoImp1 implements UserDao {

    @Override
    public void print() {
        System.out.println("userDaoImp1 print");
    }
}

根据名称进行注入:

    @Autowired
    @Qualifier("userDaoImp1")
    private UserDao userDao;

2.9.5 根据类型注入或根据名称注入

Resource:根据类型注入或根据名称注入(javax 包中)

根据类型注入:

@Resource

根据名称注入:

@Resource("userService")

2.9.6 注入普通属性

    @Value(value = "zhh")
    public String name;

2.9.7 完全注解开发

不使用配置文件,完全使用注解的方式。

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

@Configuration:作为配置类
@ComponentScan: 需要扫描的包路径

@Configuration
@ComponentScan(basePackages = {"com.pyd.aop"})
public class SpringConfig {
}
  1. 加载配置类

AnnotationConfigApplicationContext

      ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);

三、AOP

3.1 概念

  1. 面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
  2. 通过不修改源码的方式,增加新的功能

基于 Java 的主要 AOP 实现有:AspectJ Spring AOP JBoss AOP

3.2 基本原理

3.2.1 动态代理

有两种情况的动态代理:

(1)有接口:使用 JDK 动态代理

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

(2)无接口:使用 CGLIB 动态代理

创建子类。
CGLIB:创建当前类子类的代理对象

3.2.2 JDK 动态代理

使用 Proxy 类的方法创建类的代理对象。

调用 newInstance 方法:

  • 参数 1:类加载器
  • 参数 2:增强方法锁在类实现的接口(支持多个)
  • 参数 3:实现接口 InvcationHandler,创建代理对象,写增强方法。
  1. 创建接口,定义方法
public interface UserDao {
    public int add(int a, int b);

    public String update(int id);
}
  1. 创建实现类,实现方法
public class UserDaoImp implements UserDao {
    @Override
    public int add(int a, int b) {
        System.out.println(a + b);
        return 0;
    }

    @Override
    public String update(int id) {
        return null;
    }
}
  1. 使用 proxy 类实现增强
public class JDKProxy {
    public static void main(String[] args) {
        UserDaoImp userDaoImp = new UserDaoImp();
        Class[] interfaces = {UserDao.class};
        new UserDaoImp();
        UserDao proxy = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDaoImp));
        proxy.add(10, 20);
    }
}

class UserDaoProxy implements InvocationHandler {
    public Object obj;

    public UserDaoProxy(Object obj) {
        this.obj = obj;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法执行之前");

        System.out.println("method.getName() = " + method.getName());
        // 增强方法执行
        Object res = method.invoke(obj, args);

        System.out.println("方法执行之后");

        return res;
    }
}

3.3 术语

  1. 连接点:类中可以被增强的方法

  2. 切入点:实际真正被增强的方法

  3. 通知(增强):实际增强的逻辑部分。
    通知有多种类型:

    • 前置
    • 后置
    • 环绕
    • 异常
    • 最终
  4. 切面:将通知应用到切入点的过程。

3.4 准备工作

导入相关 Jar 包:



<dependency>
  <groupId>org.springframeworkgroupId>
  <artifactId>spring-aspectsartifactId>
  <version>5.2.0.RELEASEversion>
dependency>


<dependency>
  <groupId>org.aspectjgroupId>
  <artifactId>aspectjweaverartifactId>
  <version>1.6.8version>
dependency>


<dependency>
  <groupId>aopalliancegroupId>
  <artifactId>aopallianceartifactId>
  <version>1.0version>
dependency>


<dependency>
  <groupId>net.sourceforge.cglibgroupId>
  <artifactId>com.springsource.net.sf.cglibartifactId>
  <version>2.2.0version>
dependency>

3.5 基于 AspectJ 实现 AOP 操作

不是 Spring 的组成部分,独立的 AOP 框架,一般将 AspectJ 和 Spring 框架一起使用。

  • 基于 xml 配置文件实现
  • 基于注解方式实现

3.5.1 切入点表达式

作用:知道对哪个类和哪个方法来增强。

语法结构:

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

例:

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

    execution(\* com.atguigu.dao.BookDao.add(..))
    
  • 举例 2:对 com.micah.dao.BookDao 类里面的所有的方法进行增强

    execution(_ com.atguigu.dao.BookDao._ (..))
    
  • 举例 3:对 com.atguigu.dao 包里面所有类,类里面的所有的方法进行增强

    execution(_ com.micah.dao._.\* (..))
    

3.5.2 AspectJ 注解

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

创建不同方法,代表不同的通知类型

public class UserProxy {
    public void before() {
        System.out.println("前置通知");
    }
}
  1. 进行通知的配置
    (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.pyd.aop"
      >context:component-scan>
    
      
      <aop:aspectj-autoproxy>aop:aspectj-autoproxy>
    beans>
    

    (2)使用注解创建 User 和 UserProxy 对象

    @Component
    

    (3)在增强类上添加注解 Aspect

    @Aspect
    

    (4)开启 ApsectJ 生成代理对象

  2. 配置不同类型的通知

添加通知的注解,填写切入点表达式:

@Component
@Aspect
public class UserProxy {
    @Before(value = "execution(* com.pyd.aop.annotation.User.add(..))")
    public void before() {
        System.out.println("before 前置通知");
    }


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

    @After(value = "execution(* com.pyd.aop.annotation.User.add(..))")
    public void after() {
        System.out.println("after 最终通知");
    }

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

    @Around(value = "execution(* com.pyd.aop.annotation.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕通知前");
        proceedingJoinPoint.proceed();
        System.out.println("环绕通知后");
    }
}
  1. 相同切入点抽取

Pointcut

    @Pointcut(value = "execution(* com.pyd.aop.annotation.User.add(..))")
    public void pointDemo() {
    }

    @Before(value = "pointDemo()")
    public void before() {
        System.out.println("before 前置通知");
    }
  1. 设置增强类的优先级

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

@Component
@Aspect
@Order(1)
public class PersonProxy

在 spring 配置文件中配置切入点:

<aop:config>
  
  <aop:pointcut
    id="p"
    expression="execution(*
com.atguigu.spring5.aopxml.Book.buy(..))"
  />
  
  <aop:aspect ref="bookProxy">
    
    <aop:before method="before" pointcut-ref="p" />
  aop:aspect>
aop:config>

3.5.3 AspectJ 完全注解开发

  1. 创建配置类
@Configuration
@ComponentScan(basePackages = {"com.pyd.annotation"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
class SpringAspectConfig {
}

四、JdbcTemplate

4.1 概述

Spring 框架中提供的一个对象,是对原始 JDBC API 对象的简单封装。

其他操作模板类:

  • 操作关系型数据的:JdbcTemplate 和 HibernateTemplate。
  • 操作 nosql 数据库的:RedisTemplate。
  • 操作消息队列的:JmsTemplate。

4.2 需要的依赖

<dependency>
  <groupId>mysqlgroupId>
  <artifactId>mysql-connector-javaartifactId>
  <version>8.0.22version>
dependency>
<dependency>
  <groupId>com.alibabagroupId>
  <artifactId>druidartifactId>
  <version>1.2.4version>
dependency>
<dependency>
  <groupId>org.springframeworkgroupId>
  <artifactId>spring-jdbcartifactId>
  <version>5.3.2version>
dependency>
<dependency>
  <groupId>org.springframeworkgroupId>
  <artifactId>spring-txartifactId>
  <version>5.3.2version>
dependency>
<dependency>
  <groupId>org.springframeworkgroupId>
  <artifactId>spring-ormartifactId>
  <version>5.3.2version>
dependency>

4.3 准备工具

4.3.1 Spring 配置文件


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

创建数据库连接池:

<context:property-placeholder location="classpath:jdbc.properties" />
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
  <property name="driverClassName" value="${driverClass}" />
  <property name="url" value="${url}" />
  <property name="username" value="${username}" />
  <property name="password" value="${password}" />
bean>

创建 JdbcTemplate 对象:

<bean id="jdbctemplate" class="org.springframework.jdbc.core.JdbcTemplate">
  
  <property name="dataSource" ref="dataSource">property>
bean>

开启组件扫描:

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

4.3.2 创建 service 和 dao 类,在 dao 注入 jdbcTmeplate 对象

public interface BookDao {}

注入配置文件中的 JdbcTemplate 对象

@Repository
public class BookDaoImp implements BookDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;
}
@Service
public class BookService {
    @Autowired
    private BookDao bookDao;
}

4.4 操作数据库

数据库:

SELECT * FROM  t_fruit;

CREATE DATABASE dbtest;

USE dbtest;

CREATE TABLE book(
    id int,
    name VARCHAR(128),
    status BOOLEAN
);

4.4.1 添加

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

    private int id;
    private String name;
    private boolean status;

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

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

    public void setStatus(boolean status) {
        this.status = status;
    }
}
  1. 在 dao 进行数据库添加操作
@Repository
public class BookDaoImp implements BookDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void add(Book book) {
        String sql = "insert into book values(?,?,?);";
        Object[] args = {book.getId(), book.getName(), book.isStatus()};
        int update = jdbcTemplate.update(sql, args);
        System.out.println(update);

    }
}
@Service
public class BookService {
    @Autowired
    private BookDaoImp bookDao;

    public void add(Book book) {
        bookDao.add(book);
    }

}

4.4.2 查询—返回某个值

查询表有多少条记录

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

4.4.3 查询—返回对象

public <T> T queryForObject(String sql, RowMapper<T> rowMapper, @Nullable Object... args)

参数 2:RowMapper,接口,返回不同类型的数据

查询图书的详细信息

    @Override
    public Book getBook(int id) {
        String sql = "select * from book where id = ?";
        return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), id);
    }

4.4.4 查询—返回集合

查询图书列表:

    @Override
    public List<Book> getBooksList() {
        String sql = "select * from book";
        return jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class));
    }

4.4.5 批量添加操作

betchUpdate

    @Override
    public void batchAdd(List<Object[]> books) {
        String sql = "insert into book values (?,?,?);";
        jdbcTemplate.batchUpdate(sql, books);
    }
        List<Object[]> books = new ArrayList<>();
        Object[] book1 = {3, "zhanghh", false};
        Object[] book2 = {4, "zhangh", false};
        Object[] book3 = {5, "zhanhh", false};
        books.add(book1);
        books.add(book2);
        books.add(book3);
        bookService.batchAdd(books);

4.5 事务

4.5.1 基本概念

数据库操作的基本单元,逻辑上一组的操作,要么都成功,要么都失败。

四个特性(ACID):

  • 原子性
  • 一致性
  • 隔离性
  • 持久性

4.5.2 环境搭建

  1. 数据库建表
CREATE TABLE user
(
    id    INT,
    name  VARCHAR(128),
    money INT
);
INSERT INTO user VALUES (1,'张浩瀚',200),(2,'章帅',300);
  1. 创建 service,搭建 dao,完成对象创建和注入关系

dao:

public interface UserDao {
    public void add(int money);

    public void reduce(int money);

}
@Repository
public class UserDaoImp implements UserDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;


    @Override
    public void add(int money) {
        System.out.println("id=1 : -" + money);
        String sql = "update user set money-? where id=?";
        jdbcTemplate.update(sql, money, 1);
    }

    @Override
    public void reduce(int money) {
        System.out.println("id=2 : +" + money);
        String sql = "update user set money+? where id=?";
        jdbcTemplate.update(sql, money, 2);

    }
}

service:

@Service
public class UserService {

    @Autowired
    private UserDaoImp userDao;

    public void transfer() {
        userDao.add(100);
        userDao.reduce(100);
    }
}
  1. 模拟异常
int i = 10/0;

4.5.3 事务操作过程

事务操作过程:

  1. 开启事务操作

  2. 捕获异常

  3. 出现异常,回滚

4.5.4 Spring 事务

  1. 将事务加到 Service 层上

  2. 在 spring 进行事务管理操作:编程式事务管理和声明式事务管理

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

  4. 相关 API
    接口:事务管理器,该接口针对不同框架提供不同实现类

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

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

引入名称空间:tx

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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
       http://www.springframework.org/schema/tx  http://www.springframework.org/schema/tx/spring-tx.xsd"
>

开启事务注解:

 <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
  1. 在 service 类上添加事务注解

Transactional:

  • 加在类上:当前类的所有方法添加事务
  • 加在方法:当前方法添加事务

4.5.6 声明式事务管理参数配置

当一个事务的方法被另一个事务方法调用时,该事务方法如何进行

事务传播行为:

传播行为 描述
PROPAGATION_REQUIRED Spring 默认的传播机制,能满足绝大部分业务需求,如果外层有事务,则当前事务加入到外层事务,一块提交,一块回滚。如果外层没有事务,新建一个事务执行
PROPAGATION_REQUES_NEW 该事务传播机制是每次都会新开启一个事务,同时把外层事务挂起,当当前事务执行完毕,恢复上层事务的执行。如果外层没有事务,执行当前新开启的事务即可
PROPAGATION_SUPPORT 如果外层有事务,则加入外层事务,如果外层没有事务,则直接使用非事务方式执行。完全依赖外层的事务
PROPAGATION_NEVER 如果外层有事务,则加入外层事务,如果外层没有事务,则直接使用非事务方式执行。完全依赖外层的事务
PROPAGATION_NOT_SUPPORT 该传播机制不支持事务,如果外层存在事务则挂起,执行完当前代码,则恢复外层事务,无论是否异常都不会回滚当前的代码
PROPAGATION_MANDATORY 与 NEVER 相反,如果外层没有事务,则抛出异常
PROPAGATION_NESTED 该传播机制的特点是可以保存状态保存点,当前事务回滚到某一个点,从而避免所有的嵌套事务都回滚,即各自回滚各自的,如果子事务没有把异常吃掉,基本还是会引起全部回滚的。

传播规则回答了这样一个问题:一个新的事务应该被启动还是被挂起,或者是一个方法是否应该在事务性上下文中运行。

事务隔离级别 isolation

多事务之间不会产生影响。
不考虑隔离性会出现三个读问题:

  • 脏读:一个未提交的事务读取到了另一个未提交事务的数据
  • 不可重复读: 一个未提交事务读到了另一个已提交事务的修改数据
  • 虚读(幻读):一个未提交的事务读到了新提交的数据

通过设置隔离级别来解决读问题。

脏读 不可重复读 虚读
READ UNCOMMIT 读未提交
READ COMMITTED 读已提交
REPEATABLE READ 可重复读 (MySQL 默认)
SERIALIZABLE 串行化
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ)

超时时间 timeout

设置事务需要在一定时间内进行提交,超时则回滚。
默认值为-1,单位为秒。

@Transactional(timeout = 5)

readOnly 是否只读
读:查询操作
写:添加修改删除操作
默认值为 false,可读写
设置为 true,只能查询

rollbackFor 回滚
设置出现哪些异常需要回滚

noRollbackFor 是否只读
设置出现哪些异常不需要回滚

4.5.6 XML 实现声明式事务管理

  1. 配置事务管理器

<bean
  class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
  id="dataSourceTransactionManager"
>
  
  <property name="dataSource" ref="dataSource">property>
bean>
  1. 配置通知(事务)

<tx:advice id="txadvice">
  <tx:attributes>
    
    <tx:method name="transfer*" propagation="REQUIRES_NEW" />
  tx:attributes>
tx:advice>
  1. 配置切入点和切面
<aop:config>
  
  <aop:pointcut
    id="px"
    expression="execution(* com.pyd.jdbctemplate.serivce.UserService.*(..))"
  />
  
  <aop:advisor advice-ref="txadvice" pointcut-ref="px">aop:advisor>
aop:config>

4.5.7 完全注解实现声明式事务管理

  1. 创建配置类,使用配置类替代 xml
@Configuration
@ComponentScan(basePackages = {"com.pyd.jdbctemplate"})
@EnableTransactionManagement  // 开启事务
public class TxConfig {

    // 创建数据库的连接池
    @Bean
    public DruidDataSource getDruidDataSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setUsername("root");
        druidDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/dbtest");
        druidDataSource.setPassword("");
        druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        return druidDataSource;
    }

    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
        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;
    }

}

Spring 5 框架新功能

5.1 概述

  1. 常规升级:

    • 对 JDK 9 运行时兼容性
    • 在 Spring Framework 代码中使用 JDK 8 特性
    • 响应式编程支持
    • 函数式 Web 框架
    • Jigsaw 的 Java 模块化
    • 对 Kotlin 支持
    • 舍弃的特性
  2. 基于 Java8 兼容 Java9

    • 核心 Spring 接口中的 Java 8 static 方法
    • 基于 Java 8 反射增强的内部代码改进
    • 在框架代码中使用函数式编程——lambdas 表达式和 stream 流
  3. 支持响应式编程,需要使用框架

    • Reactive Streams:尝试定义与语言无关的响应性 API。
    • Reactor:Spring Pivotal 团队提供的响应式编程的 Java 实现。
    • Spring WebFlux:启用基于响应式编程的 Web 应用程序的开发。 提供类似于 Spring MVC 的编程模型。

5.2 日志框架

自带日志封装

(1) Spring5 已经移除 Log4jConfigListener,官方建议使用 Log4j2
(2) Spring5 框架整合 Log4j2

  1. 导入 jar 包
<dependency>
  <groupId>org.apache.logging.log4jgroupId>
  <artifactId>log4j-apiartifactId>
  <version>2.11.2version>
dependency>
<dependency>
  <groupId>org.apache.logging.log4jgroupId>
  <artifactId>log4j-coreartifactId>
  <version>2.11.2version>
dependency>
<dependency>
  <groupId>org.slf4jgroupId>
  <artifactId>slf4j-apiartifactId>
  <version>1.7.30version>
dependency>
<dependency>
  <groupId>org.apache.logging.log4jgroupId>
  <artifactId>log4j-slf4j-implartifactId>
  <version>2.11.2version>
dependency>
  1. 创建 log4j2.xml



<configuration status="DEBUG">
  
  <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>
  1. 手动输出
public class Log {

    private static final Logger log = LoggerFactory.getLogger(Log.class);

    public static void main(String[] args) {
        log.info("hello log4j");
        log.warn("hello log4j");
    }

}

5.3 核心容器

5.3.1 支持 Nullable 注解

Nullable:

  • 用于方法:方法的返回值可以为空
  • 用于方法的参数:参数可以为空
  • 用于属性:属性值可以为空

5.3.2 支持函数式风格 GenericApplication Context

        GenericApplicationContext context = new GenericApplicationContext();
        // 调用context对象进行注册,先清空
        context.refresh();
        context.registerBean(User.class, () -> new User());
        context.registerBean("user1", User.class, () -> new User());
        // 获取对象 全路径
        User user = (User) context.getBean("com.pyd.User");
        // 获取对象:指定名称
        User user1 = (User) context.getBean("user1");

5.4 支持 JUnit5

  1. 整合 JUnit4
@RunWith(SpringJUnit4ClassRunner.class)
//单元测试框架
@ContextConfiguration("classpath:bean1.xml")
//加载配置文件
public class JTest4 {
    @Autowired
    private UserService userService;
    @Test
    public void test1() {
        userService.accountMoney();
    }
}
  1. 整合 JUnit5

需要引入 JUnit5 的 Jar 包

@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:bean1.xml")
public class JTest5 {
    @Autowired
    private UserService userService;
    @Test
    public void test1() {
        userService.accountMoney();
    }
}
  1. 复合注解
@SpringJUnitConfig(locations = "classpath:bean1.xml")
public class JTest5{
    @Autowired
    private UserService userService;
    @Test
    public void test1() {
        userService.accountMoney();
    }
}

5.4 SpringWebFlux

5.4.1 概述

是 Spring5 添加新的模块,用于 web 开发的,功能和 SpringMVC 类似的,Webflux 使用当前一种比较流程响应式编程出现的框架。
使用传统 web 框架,比如 SpringMVC,这些基于 Servlet 容器,WebFlux 是一种异步非阻塞的框架,异步非阻塞的框架在 Servlet 3.1 以后才支持,核心是基于 Reactor 的相关 API 实现的。

你可能感兴趣的:(spring,学习,java)