当前位置:   article > 正文

Spring-尚硅谷-学习笔记_尚硅谷spring笔记

尚硅谷spring笔记

Spring概念

Spring框架是一个轻量级的开源的JavaEE框架。
Spring可以解决企业应用开发的复杂性。

Spring有两个核心部分:IOC和AOP

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

Spring特点

  1. 方便解耦,简化开发
  2. AOP编程支持
  3. 方便程序测试
  4. 方便和其他框架进行整合

入门案例
第一步,下载jar包(GA表示稳定版本)
在这里插入图片描述
下载地址:https://repo.spring.io/ui/native/release/org/springframework/spring/
在这里插入图片描述

第二步,创建普通的java项目
在这里插入图片描述

第三步、导入相关jar包
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

第四步,创建普通类
在这里插入图片描述

第五步,创建spring配置文件,在配置文件配置对象的创建
在这里插入图片描述

第六步,测试

@Test
public void testAdd() {
    //1 加载spring配置文件
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    //2 获取配置创建的对象
    User user = context.getBean("user", User.class); //参数"user"是配置文件中的id
    System.out.println(user);
    user.add();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

IOC容器


IOC底层原理

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

使用IOC目的,是为了降低耦合度。

工厂模式:解耦合
在这里插入图片描述

IOC过程解耦合,通过 xml解析 + 反射 实现
在这里插入图片描述

IOC接口

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

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

  • BeanFactory:IOC容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用。加载配置文件的时候不会创建对象,在获取对象使用时才去创建
  • ApplicationContext:BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用。加载配置文件的时候就会把在配置文件对象进行创建

ApplicationContext接口有两个实现类,区别是ClassPathXmlApplicationContext的参数写xml的文件名,而FileSystemXmlApplicationContext的参数是写磁盘路径。
在这里插入图片描述

IOC操作Bean管理

创建对象和注入属性(xml方式)

Bean管理指的是两个操作:

  • Spring创建对象
  • Spring注入属性

Bean管理操作有两种方式:

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

基于xml方式创建对象
在这里插入图片描述
在spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建。

在bean标签有很多属性,常用属性:

  • id属性:唯一标识
  • class属性:类全路径(包类路径)

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

基于xml方式注入属性

DI:依赖注入,就是注入属性
IOC和DI有什么区别:DI是IOC中的具体实现,DI表示依赖注入或注入属性,注入属性要在创建对象的基础之上完成。

  • 第一种注入方式:使用set方法进行注入
    public class Book {
        private String bookName;
        private String bAuthor;
        // set方法注入
        public void setBookName(String bookName) {
            this.bookName = bookName;
        }
        public void setbAuthor(String bAuthor) {
            this.bAuthor = bAuthor;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    <!--set方法注入属性-->
    <bean id="book" class="com.company.spring5.Book">
        <!--使用property完成属性注入
            name:类里面属性名称
            value:向属性注入的值
        -->
        <property name="bookName" value="白鹿原"></property>
        <property name="bAuthor" value="陈忠实"></property>
    </bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • 第二种注入方式:使用有参构造进行注入
    public class Order {
        private String name;
        private String address;
    
        public Order(String name, String address) {
            this.name = name;
            this.address = address;
        }
    
        public void test() {
            System.out.println(name + "--" + address);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    <!--有参构造注入属性-->
    <bean id="order" class="com.company.spring5.Order">
        <constructor-arg name="name" value="电脑"></constructor-arg>
        <constructor-arg name="address" value="中国"></constructor-arg>
    </bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 第三种注入方式:p名称空间注入,可以简化基于xml配置方式
    第一步,在配置文件中添加p名称空间
    在这里插入图片描述
    第二步,进行属性注入
    <bean id="book" class="com.company.spring5.Book" p:bookName="平凡的世界" p:bAuthor="路遥">
    </bean>
    
    • 1
    • 2

1、xml注入其他类型属性
注入null值和特殊符号

<bean id="book" class="com.company.spring5.Book">
    <!--设置属性为空-->
    <property name="bookName">
        <null/>
    </property>
    <!-- 属性值包含特殊符号
        1. 把<>进行转义 &lt; &gt;
        2. 把带特殊符号内容写到CDATA
    -->
    <property name="bAuthor">
        <value><![CDATA[<<南京>>]]></value>
    </property>
</bean>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2、注入属性-外部bean
(1)创建两个类service类和dao类
(2)在service调用dao里面的方法
(3)在spring配置文件中进行配置

public class UserService {
    private UserDao userDao;

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

    public void add() {
        System.out.println("add..............");
        userDao.update();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
public class UserDaoImpl implements UserDao {
    @Override
    public void update() {
        System.out.println("update...........");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在这里插入图片描述

3、注入属性-内部bean和级联赋值
(1)一对多关系:部门和员工。一个部门有多个员工。
(2)在实体类之间表示一对多关系。

//部门类
public class Dept {
    private String dname;

    public void setDname(String dname) {
        this.dname = dname;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
//员工类
public class Emp {
    private String ename;
    private String gender;
    //员工属于某一个部门,使用对象形式表示
    private Dept dept;

    public void setEname(String ename) {
        this.ename = ename;
    }

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

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    public void test() {
        System.out.println(ename+"::"+gender+"::"+dept);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

内部bean注入
在这里插入图片描述

级联赋值注入

  • 第一种写法
    在这里插入图片描述
  • 第二种写法
    前提是Emp的dept属性要加上get方法,不然dept.dname获取不到。
    在这里插入图片描述

4、注入集合类型属性
(1)注入数组类型属性
(2)注入List集合类型属性
(3)注入Map集合类型属性

public class Student {
    // 数组类型
    private String[] courses;
    //list集合类型
    private List<String> list;
    //map集合类型
    private Map<String, String> maps;

	//编写set方法
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在这里插入图片描述

list集合中存放对象类型

public class Student {
    private List<Course> courseList;
    public void setCourseList(List<Course> courseList) {
        this.courseList = courseList;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在这里插入图片描述

把集合注入部分提取出来,让别人也能使用这个集合
(1)在spring配置文件中引入名称空间util
在这里插入图片描述
(2)使用util标签完成list集合注入提取
在这里插入图片描述

FactoryBean工厂bean

Spring有两种类型bean,一种普通bean,另一种工厂bean

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

  • 工厂bean:在配置文件定义bean类型可以和返回类型不一样
    第一步,创建类,这个类作为工厂bean,实现接口FactoryBean
    在这里插入图片描述

    第二步,实现接口里面的方法,在实现的方法中定义返回的bean类型
    在这里插入图片描述
    在这里插入图片描述

Bean的作用域

在Spring中,设置创建bean实例默认是单实例对象。
在这里插入图片描述

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

scope属性值
singleton,表示单实例对象。
prototype,表示多实例对象。
在这里插入图片描述
在这里插入图片描述

singleton和prototype的区别

  • 设置scope值是singleton的时候,加载spring配置文件就会创建单实例对象
  • 设置scope值是prototype的时候,在调用getBean方法创建多实例对象。

Bean的声明周期

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

bean的生命周期:
(1)通过构造器创建bean实例(无参构造)
(2)为bean的属性设置值和对其他bean引用(调用set方法)
(3)调用bean的初始化方法(配置初始化方法)
(4)使用bean(对象获取到了)
(5)当容器关闭,调用bean的销毁方法(需要配置销毁的方法)
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

如果加上后置处理器,有七个步骤:
(1)通过构造器创建bean实例(无参构造)
(2)为bean的属性设置值和对其他bean引用(调用set方法)
(3)把bean实例传递bean后置处理器的方法
(4)调用bean的初始化方法(配置初始化方法)
(5)把bean实例传递bean后置处理器的方法
(6)使用bean(对象获取到了)
(7)当容器关闭,调用bean的销毁方法(需要配置销毁的方法)

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

public class MyBeanPost implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化之前执行的方法");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化之后执行的方法");
        return bean;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在这里插入图片描述
配置好后,会为所有对象配置上后置处理器,分别在调用bean初始化之前和初始化之后执行。
在这里插入图片描述

Bean的自动装配

自动装配:指 Spring 容器在不使用 和 标签的情况下,可以自动装配(autowire)相互协作的 Bean 之间的关联关系,将一个 Bean 注入其他 Bean 的 Property 中。

在这里插入图片描述
注意,使用byType注入,相同类型的bean不能定义多个
在这里插入图片描述

引入外部属性文件

配置数据库信息
(1)配置德鲁伊连接池,引入德鲁伊依赖jar包
(2)引入外部属性文件配置数据库连接池
在这里插入图片描述

(3)把外部properties属性文件引入到Spring配置文件
引入名称空间
在这里插入图片描述
引入外部属性文件,用标签赋值
在这里插入图片描述

创建对象和注入属性(注解实现)

Spring针对Bean管理中创建对象提供注解
(1)@Component
(2)@Service
(3)@Controller
(4)@Repository
上面四个注解功能是一样的,都可以用来创建bean实例。

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

第一步,引入依赖
在这里插入图片描述
第二步,开启组件扫描,看哪个类上有注解,有注解就进行创建
在这里插入图片描述

第三步,创建类,在类上面添加创建对象注解
注解中的value属性可以省略不写,不写默认值是类名首字母小写(StudentService —— studentService)
在这里插入图片描述

@Test
public void test2() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean8.xml");
    StudentService studentService = context.getBean("studentService", StudentService.class);
    studentService.add();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

组件扫描配置
扫描包下所有加了注解的类
在这里插入图片描述

use-default-filters:表示是否使用默认filter,false表示使用自己配置的filter
context:include-filter:设置扫描哪些内容
在这里插入图片描述

context:exclude-filter:表示设置哪些内容不进行扫描
在这里插入图片描述

基于注解方式实现属性注入
(1)@Autowired:根据属性类型进行自动装配
第一步,把service和dao对象创建,在service和dao类添加注解
第二步,在service注入dao对象,在service类添加dao类型属性,在属性上面使用注解
在这里插入图片描述

(2)@Qualified:根据属性名称进行注入
@Qualified 注解要和 @Autowired 一起使用。
@Qualified表示以名称注入,@Autowired表示以类型注入,如果一个接口有多个实现类,那么要用@Qualified指定名称进行注入。
在这里插入图片描述
在这里插入图片描述

(3)@Resource:可以根据类型注入,可以根据名称注入
@Resource:根据类型进行注入
@Resource(name=“userDaoImpl1”):根据名称进行注入

(4)@Value:注入普通类型属性

@Value(value="abc")
private String name;
  • 1
  • 2

完全注解开发
在这里插入图片描述
在这里插入图片描述

AOP


面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发效率。
通俗描述:不通过修改源码方式,在主干功能里面添加新功能。
在这里插入图片描述

AOP底层原理

AOP底层使用动态代理。

  • 第一种,有接口的情况,使用JDK动态代理
  • 第二种,没有接口的情况,使用CGLIB动态代理

JDK动态代理的实现:使用Proxy类里面的方法创建代理对象
现在想要增强这个add方法
在这里插入图片描述
调用 newProxyInstance方法,方法有三个参数:

  • 第一个参数:类加载器
  • 第二个参数:增强方法所在的类,这个类实现的接口,支持多个接口
  • 第三个参数:实现这个接口InvocationHandler,创建代理对象,写增强方法
    在这里插入图片描述
    执行
    在这里插入图片描述

AOP操作

术语:

  1. 连接点:类中可以被增强的方法,称为连接点。
  2. 切入点:实际被真正增强的方法,称为切入点。
  3. 通知(增强):实际增强的逻辑部分,称为通知。
    通知有多种类型:前置通知、后置通知、环绕通知、异常通知、最终通知。
  4. 切面:是一个动作,把通知应用到切入点的过程。

Spring框架一般基于AspectJ实现AOP操作。AspectJ不是Spring组成部分,一般把AspectJ和Spring框架一起使用,进行AOP操作。

引入依赖
在这里插入图片描述
切入点表达式
利用切入点表达式,可以让Spring知道对哪个类里面的哪个方法进行增强。
语法: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.*.*(..))

进行通知的配置
(1)在Spring配置文件中,开启注解扫描
在这里插入图片描述
在这里插入图片描述

(2)使用注解创建User和UserProxy对象
在这里插入图片描述

在这里插入图片描述

(3)在增强类上面添加注解@Aspect
在这里插入图片描述

(4)在Spring配置文件中开启生成代理对象
如果找到类上有@Aspect注解,将这个类生成代理对象。
在这里插入图片描述

AspectJ注解:配置不同类型的通知
在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置。
在这里插入图片描述

测试:执行被增强的方法
在这里插入图片描述
结果
在这里插入图片描述

配置其他类型通知
在这里插入图片描述
测试,此时没有执行异常通知
在这里插入图片描述

我们给被增强的方法中加一个异常
在这里插入图片描述
结果
在这里插入图片描述

相同的切入点抽取
在这里插入图片描述

有多个增强类对同一个方法进行增强,设置增强类的优先级
在增强类上面添加注解@Order(数字类型值)
在这里插入图片描述
PersonProxy优先级高,先进行增强

完全注解开发
在这里插入图片描述

配置文件实现AOP

<bean id="user" class="com.company.spring5.aop.User"></bean>
<bean id="userProxy" class="com.company.spring5.aop.UserProxy"></bean>

<!--配置aop增强-->
<aop:config>
        <!--切入点-->
        <aop:pointcut id="p" expression="execution(* com.company.spring5.aop.User.add(..))"/>
        <!--配置切面-->
        <aop:aspect ref="userProxy">
                <!--增强作用在具体方法上-->
                <aop:before method="before" pointcut-ref="p"/>
        </aop:aspect>
</aop:config>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

JdbcTemplate


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

1. 引入依赖
在这里插入图片描述
2. 配置数据库连接池
在这里插入图片描述
3. 配置JdbcTemplate对象,注入DataSource

<!--JdbcTemplate对象-->
<bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <!--注入dataSource-->
    <property name="dataSource" ref="dataSource"></property>
</bean>
  • 1
  • 2
  • 3
  • 4
  • 5

4. 创建类和接口
在这里插入图片描述
在这里插入图片描述

5. 创建数据库表和对应实体类

用JdbcTemplate进行添加操作
在这里插入图片描述
用update方法进行添加操作,方法有两个参数:
第一个参数:sql语句
第二个参数:可变参数,sql语句的参数

dao层

@Override
public void add(Book book) {
    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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

service层

//添加
public void addBook(Book book) {
    bookDao.add(book);
}
  • 1
  • 2
  • 3
  • 4

测试

@Test
public void test1() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean7.xml");
    BookService bookService = context.getBean("bookService", BookService.class);
    Book book = new Book();
    book.setUserId("1");
    book.setUsername("java");
    book.setUstatus("有效");
    bookService.addBook(book);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

用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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

用JdbcTemplate进行删除操作

@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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

用JdbcTemplate进行查询操作
查询表里有多少条记录,返回某个值
在这里插入图片描述
第一个参数:sql语句
第二个参数:返回类型Class

@Override
public int selectCount() {
    String sql = "select count(*) from t_book";
    Integer integer = jdbcTemplate.queryForObject(sql, Integer.class);
    return integer;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

查询返回对象
在这里插入图片描述
有三个参数:
第一个参数: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.class), id);
    return book;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

查询返回集合
在这里插入图片描述

@Override
public List<Book> findAllBook() {
    String sql = "select * from t_book";
    List<Book> books = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Book.class));
    return books;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

JdbcTemplate批量操作数据

//批量添加
@Override
public void batchAdd(List<Object[]> batchArgs) {
    String sql = "insert into t_book values (?,?,?)";
    int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
    System.out.println(Arrays.toString(ints));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
@Test
public void test2() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean7.xml");
    BookService bookService = context.getBean("bookService", BookService.class);
    List<Object[]> batchArgs = new ArrayList<>();
    Object[] o1 = {"3", "java", "a"};
    Object[] o2 = {"4", "c++", "b"};
    Object[] o3 = {"5", "Mysql", "c"};
    batchArgs.add(o1);
    batchArgs.add(o2);
    batchArgs.add(o3);
    bookService.batchAdd(batchArgs);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

事务管理


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

环境搭建:实现转账的操作

1. 创建数据库表,添加记录
在这里插入图片描述
2. 创建service,搭建dao,完成对象创建和注入关系
在这里插入图片描述
在这里插入图片描述
3. 编写dao的两个方法,加钱和减钱

//减钱
@Override
public void addMoney() {
    String sql = "update t_account set money = money - ? where username = ?";
    jdbcTemplate.update(sql, 100, "lucy");
}
//加钱
@Override
public void reduceMoney() {
    String sql = "update t_account set money = money + ? where username = ?";
    jdbcTemplate.update(sql, 100, "mary");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

4. 在service层调用这两个方法,实现转账

//转账的方法
public void accountMoney() {
    personDao.reduceMoney();
    personDao.addMoney();
}
  • 1
  • 2
  • 3
  • 4
  • 5

上面代码,如果正常执行没有问题,但是如果代码执行过程中出现异常,则有问题。

Spring事务管理

事务应该添加到三层结构中的Service层(业务逻辑层)

Spring进行事务管理操作有两种方式

  • 编程式事务
    try {
        //第一步,开启事务
        
        //第二步,进行业务操作
        personDao.reduceMoney(); // lucy少100
        
        //可能出现异常
        int a = 1/0;
        
        personDao.addMoney(); // mary多100
        
        //第三步,没有发生异常,提交事务
        
    } catch (Exception e) {
        //第四步,出现异常,事务回滚
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
  • 声明式事务管理
    • 基于注解方法
    • 基于xml配置文件方式

Spring的声明式事务管理,底层使用了AOP原理。

Spring事务管理提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类。
在这里插入图片描述

Spring声明式事务注解实现
1、在Spring配置文件配置事务管理器

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

2、在spring配置文件中,开启事务注解
(1)在Spring配置文件引入名称空间tx
在这里插入图片描述
(2)开启事务的注解

<!--开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
  • 1
  • 2

3、在service类上面添加事务注解
(1)@Transactional,这个注解添加到类上面,也可以添加方法到方法上
(2)如果把这个注解添加在类上,这个类所有的方法都添加事务
(3)如果在方法上添加注解,只是给这个方法添加事务

声明式事务管理参数配置
1、propagation:事务传播行为
当一个事务方法被另外一个事务方法调用的时候,这个事务方法如何进行。

  • REQUIRED:如果当前(上层调用者方法)没有事务,则自己新建一个事务,如果当前(上层调用者方法)存在事务,则加入这个事务。spring默认使用该传播类型。
    在这里插入图片描述
  • REQUIRES_NEW:无论当前有没有事务,都会新建事务
    在这里插入图片描述
  • SUPPORTS:如果有事务在运行,当前方法就在这个事务内运行,否则它可以不运行在事务中。
    在这里插入图片描述

2、isolation:事务隔离级别
有三个读问题:脏读、不可重复读、幻读。
解决:通过设置事务隔离级别,解决读问题。
在这里插入图片描述
在这里插入图片描述

3、其他参数

  • timeout:超时时间,事务需要在一定时间内进行提交,如果不提交进行回滚。默认值为-1,时间以秒单位进行计算。
  • readOnly:是否只读。默认值为false,表示可以查询,可以增删改。设置为true之后,只能查询。
  • rollbackFor:回滚。设置出现哪些异常进行事务回滚。
  • noRollbackFor:不回滚。设置出现哪些异常不进行事务回滚。

事务操作(XML声明式事务管理)
第一步,配置事务管理器

第二步,配置通知

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

<!--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="accountMoney" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>
<!--3 配置切入点和切面-->
<aop:config>
    <!--配置切入点-->
    <aop:pointcut id="pt" expression="execution(* com.company.spring5.service.PersonService.*(..))"/>
    <!--配置切面-->
    <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
</aop:config>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

完全注解配置

@Configuration //配置类
@ComponentScan(basePackages = "com.company")  //组件扫描
@EnableTransactionManagement //开启事务
public class TxConfig {

    //创建数据库连接池
    @Bean
    public DruidDataSource getDruidDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/db4?useSSL=false&characterEncoding=utf-8");
        dataSource.setUsername("root");
        dataSource.setPassword("qwer`123");
        return dataSource;
    }

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

    //创建事务管理器
    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

Spring5新特性


日志框架整合

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

Spring5移除了Log4jConfigListener,官方建议使用Log4j2.

第一步,引入依赖
在这里插入图片描述
第二步,创建log4j2.xml(文件名固定)

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,可以看到log4j2内部各种详细输出-->
<configuration status="INFO">
    <!--先定义所有的appender-->
    <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>
    <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
    <!--root:用于指定项目的根日志,如果没有单独指定Logger,则会使用root作为默认的日志输出-->
    <loggers>
        <root level="info">
            <appender-ref ref="Console"/>
        </root>
    </loggers>
</configuration>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

@Nullable注解和函数式风格

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

(1)注解用在方法上面,方法返回值可以为空。

(2)注解用在方法参数里面,方法参数可以为空。

(3)注解使用在属性上面,属性值可以为空。

函数式风格GenericApplicationContext
Spring5核心容器支持函数式风格创建对象,交给Spring进行管理。

@Test
public void test5() {
    //1 创建GenericApplicationContext对象
    GenericApplicationContext context = new GenericApplicationContext();
    //2 调用context的方法对象注册
    context.refresh();
    context.registerBean("user1", User.class, ()->new User());
    //3 获取在spring注册的对象
    User user = (User) context.getBean("user1");
    System.out.println(user);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

JUnit5测试

Spring5整合Junit5
第一步,添加依赖
在这里插入图片描述
在这里插入图片描述

第二步,测试

@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:bean7.xml")
public class Test5 {

    @Autowired
    private PersonService personService;

    @Test
    public void test() {
        personService.accountMoney();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

这样省去了之前创建ApplicationContext对象和getBean的操作。

简化,使用一个复合注解替代上面两个注解
在这里插入图片描述

WebFlux

1. SpringWebflux介绍
(1)WebFlux是Spring5添加的新模块,用于web开发的,功能和SpringMVC类似的,WebFlux使用当前一种比较流行的响应式编程出现的框架。
(2)使用传统web框架,比如SpringMVC,这些基于是Servlet容器的,Webflux是一种异步非阻塞的框架,异步非阻塞的框架在Servlet3.1以后才支持,核心是基于Reactor的相关API实现的。
在这里插入图片描述
(3)Webflux的特点:异步非阻塞,在有限资源下,提高系统吞吐量和伸缩性,以Reactor为基础实现响应式编程;函数式编程,Spring5框架基于Java8,Webflux使用java8函数式编程方式实现路由请求。
(4)比较SpringMVC:两个框架都可以使用注解方式,都运行在Tomcat等容器;SpringMVC采用命令式编程,Webflux采用异步响应式编程。
在这里插入图片描述

2. 响应式编程
什么是响应式编程:
响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。
例如,对于 a=b+c 这个表达式的处理,在命令式编程中,会先计算 b+c 的结果,再把此结果赋值给 变量a,因此 b,c 两值的变化不会对 变量a 产生影响。但在响应式编程中,变量a 的值会随时跟随 b,c 的变化而变化。

Java实现响应式编程(观察者模式)

public class ObserverDemo extends Observable {
    public static void main(String[] args) {
        ObserverDemo observer = new ObserverDemo();
        observer.addObserver((o, arg) -> {
            System.out.println("数据发送变化");
        });
        observer.addObserver((o, arg) -> {
            System.out.println("手动被观察者通知,准备变化");
        });
        observer.setChanged();  //数据变化
        observer.notifyObservers(); //通知
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

没有最后两句代码,不会打印结果
在这里插入图片描述

响应式编程(Reactor实现)
(1)响应式编程操作中,Reactor是满足Reactive规范框架
(2)Reactor有两个核心类,Mono和Flux,这两个类实现接口Publisher,提供丰富的操作符。Flux对象实现发布者,返回N个元素;Mono实现发布者,返回0或1个元素。
(3)Flux和Mono都是数据流的发布者,使用Flux和Mono都可以发出三种数据信号:元素值,错误信号,完成信号,错误信号和完成信号都代表终止信号,终止信号用于告诉订阅者数据流结束了,错误信号终止数据流同时把错误信息传递给订阅者。

<dependency>
    <groupId>io.projectreactor</groupId>
    <artifactId>reactor-core</artifactId>
    <version>3.2.3.RELEASE</version>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
public class TestReactor {
    public static void main(String[] args) {
        //just方法直接声明
        Flux.just(1,2,3,4);
        Mono.just(1);

        //其他方法
        Integer[] array = {1,2,3,4};
        Flux.fromArray(array);

        List<Integer> list = Arrays.asList(array);
        Flux.fromIterable(list);

        Stream<Integer> stream = list.stream();
        Flux.fromStream(stream);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

三种信号特点

  • 错误信号和完成信号都是终止信号,它们不能共存。
  • 如果没有发送任何元素值,而是直接发送错误或者完成信号,表示是空数据流。
  • 如果没有错误信号,没有完成信号,表示是无限数据流

调用just或者其他方法只是声明数据流,数据流并没有发出,只有进行订阅之后才会触发数据流,不订阅什么都不会发生。
在这里插入图片描述
调用subscribe进行订阅
在这里插入图片描述

操作符
对数据流进行一道道操作,称为操作符,比如工厂流水线。
1、map元素映射为新元素
在这里插入图片描述
2、flatMap元素映射为流。把每个元素转换为流,把转换后多个流合并成大的流。
在这里插入图片描述

3. WebFlux执行流程和核心API
SpringWebFlux是基于Reactor,默认使用容器是Netty,Netty是高性能的NIO框架,异步非阻塞的框架。

Netty
BIO
在这里插入图片描述

NIO
在这里插入图片描述

SpringWebflux执行过程和SpringMVC相似:
SpringWebflux核心控制器DispatcherHandler,实现接口WebHandler,接口WebHandler有一个方法。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

SpringWebflux里面DispatcherHandler,负责请求的处理。
HandlerMapping:请求查询到处理的方法
HandlerAdapter:真正负责请求处理
HandlerResultHandler:响应结果处理

SpringWebflux实现函数式编程,两个接口:RouterFunction(路由处理)和HandlerFunction(处理函数)

4. SpringWebflux(基于注解编程)
第一步,引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
  • 1
  • 2
  • 3
  • 4

第二步,创建包和实体类
在这里插入图片描述
第三步,编写service接口和实现接口

//用户操作接口
public interface UserService {
    //根据id查询用户
    Mono<User> getUserById(int id);
    
    //查询所有用户
    Flux<User> getAllUser();
    
    //添加用户
    Mono<Void> saveUserInfo(Mono<User> user);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
@Repository
public class UserServiceImpl implements UserService{

    //创建map集合存储数据
    private final Map<Integer,User> users = new HashMap<>();

    public UserServiceImpl() {
        this.users.put(1, new User("lucy", "nan", 20));
        this.users.put(2, new User("mary", "nv", 30));
        this.users.put(3, new User("jack", "nv", 50));
    }
    //根据id查询
    @Override
    public Mono<User> getUserById(int id) {
        return Mono.justOrEmpty(this.users.get(id));
    }
    //查询多个用户
    @Override
    public Flux<User> getAllUser() {
        return Flux.fromIterable(this.users.values());
    }
    //添加用户
    @Override
    public Mono<Void> saveUserInfo(Mono<User> user) {
        return user.doOnNext(person -> {
            //向集合里面放值
            int id = users.size() + 1;
            users.put(id, person);
        }).thenEmpty(Mono.empty());
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

第四步,编写controller

@RestController
public class UserController {
    @Autowired
    private UserService userService;

    //id查询
    @GetMapping("/user/{id}")
    public Mono<User> getUserId(@PathVariable int id) {
        return userService.getUserById(id);
    }
    //查询所有
    @GetMapping("/user")
    public Flux<User> getUsers() {
        return userService.getAllUser();
    }
    //添加
    @PostMapping("/saveUser")
    public Mono<Void> saveUser(@RequestBody User user) {
        Mono<User> userMono = Mono.just(user);
        return userService.saveUserInfo(userMono);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

说明:
SpringMVC:同步阻塞的方式,基于SpringMVC+Servlet+Tomcat
SpringWebflux:异步非阻塞方式,基于SpringWebflux+Reactor+Netty

5. SpringWebflux(基于函数式编程)
(1)在使用函数式编程操作的时候,需要自己初始化服务器。
(2)有两个核心接口:RouterFunction(实现路由功能,请求转发给对应的handler)和HandlerFunction(处理请求生成响应的函数)。核心任务定义两个函数式接口的实现并且启动需要的服务器。
(3)SpringWebflux请求和响应不再是ServletRequest和ServletResponse,而是ServerRequest和ServerResponse。

第一步,创建Handler(具体实现方法)

public class UserHandler {
    private final UserService userService;

    public UserHandler(UserService userService) {
        this.userService = userService;
    }

    //根据id查询
    public Mono<ServerResponse> getUserById(ServerRequest request) {
        //获取id值
        int userId = Integer.valueOf(request.pathVariable("id"));
        //空值处理
        Mono<ServerResponse> notFound = ServerResponse.notFound().build();
        //调用service方法得到数据
        Mono<User> userMono = this.userService.getUserById(userId);
        //把userMono转换成流返回
        //使用Reactor操作符flatMap
        return userMono.flatMap(person -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
                                .body(fromObject(person)))
                                .switchIfEmpty(notFound);
    }

    //查询所有
    public Mono<ServerResponse> getAllUsers() {
        //调用service得到结果
        Flux<User> users = this.userService.getAllUser();
        return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(users,User.class);
    }

    //添加
    public Mono<ServerResponse> saveUser(ServerRequest request) {
        //得到user对象
        Mono<User> userMono = request.bodyToMono(User.class);
        return ServerResponse.ok().build(this.userService.saveUserInfo(userMono));
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

第二步,初始化服务器,编写Router,创建适配完成服务,完成最终调用
在这里插入图片描述

public class Server {

    public static void main(String[] args) throws Exception {
        Server server = new Server();
        //调用
        server.createReactorServer();
        System.out.println("enter to exit");
        System.in.read();
    }

    //1 创建Router路由
    public RouterFunction<ServerResponse> routingFunction() {
        UserService userService = new UserServiceImpl();
        UserHandler handler = new UserHandler(userService);
        return RouterFunctions.route(
                GET("/users/{id}").and(accept(APPLICATION_JSON)), handler::getUserById)
                .andRoute(GET("/users").and(accept(APPLICATION_JSON)), handler::getAllUsers);

    }

    //2 创建服务器完成适配
    public void createReactorServer() {
        //路由和handler适配
        RouterFunction<ServerResponse> route = routingFunction();
        HttpHandler httpHandler = toHttpHandler(route);
        ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
        //创建服务器
        HttpServer httpServer = HttpServer.create();
        httpServer.handle(adapter).bindNow();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
public class Client {
    public static void main(String[] args) {
        //调用服务器地址
        WebClient webClient = WebClient.create("http://127.0.0.1:5794");
        //根据id查询
        String id = "1";
        User userResult = webClient.get().uri("/users/{id}", id)
                .accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(User.class)
                .block();
        System.out.println(userResult.getName());
        
        //查询所有
        Flux<User> results = webClient.get().uri("/users")
                .accept(MediaType.APPLICATION_JSON).retrieve().bodyToFlux(User.class);
        results.map(stu -> stu.getName())
                    .buffer().doOnNext(System.out::println);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/天景科技苑/article/detail/792885
推荐阅读
相关标签
  

闽ICP备14008679号