超详细!!Spring5框架开发笔记

简介

spring特性

  • sping是轻量级的开源的JavaEE框架
  • Spring可以解决企业应用开发的复杂性
  • Sping两个核心的部分:IOC和AOCIOC:控制反转。把创建对象的过程交给sping进行管理,而不需要自己去new

    AOP:面向切面。不修改源代码进行功能增强

  • Sping特点方便接耦,简化开发。

    AOP编程支持

    方便程序的测试

    方便集成各种优秀框架

    方便进行事务操作

    降低API开发难度

简单使用

如果使用maven构建项目,导入spring核心依赖包就可以了:

  1.  

  2. org.springframework
  3. spring-beans
  4. 5.2.8.RELEASE


  5. org.springframework
  6. spring-context
  7. 5.2.8.RELEASE


  8. org.springframework
  9. spring-core
  10. 5.2.8.RELEASE


  11. org.springframework
  12. spring-expression
  13. 5.2.8.RELEASE


  14. commons-logging
  15. commons-logging
  16. 1.2

  17.  

随便创建一个User类,然后创建Spring配置文件,在配置文件配置创建的对象。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

  5. <bean id="user" class="com.atguigu.spring5.User">
  6.  

class是类路径

编写测试代码

  1. public class TestSpring5 {
  2. @Test
  3. public void testAdd(){
  4. //1 加载spring配置文件
  5. ApplicationContext context = new FileSystemXmlApplicationContext("/src/main/java/bean1.xml"); //方式1
  6. //BeanFactory context = new FileSystemXmlApplicationContext("/src/main/java/bean1.xml");//方式2
  7. //2 获取配置创建的对象
  8. User user = context.getBean("user",User.class);
  9. System.out.println(user);
  10. user.add();
  11. }
  12. }

此时,配置文件已经帮我们创建好了实例,我们直接从配置文件中获取就行了。

Spring IOC容器

IOC底层原理

什么是IOC

Inversion Of Control(控制反转)。把对象创建和对象之间的调用过程,交给Spring管理;使用IOC的目的是为了降低耦合度。

IOC底层主要用到三个技术:xml解析、工厂模式、反射。

  • 工厂模式其实就把把对象的创建交给第三方,降低耦合度(完全没有耦合度是不能的)
    1. class UserService{
    2. execute(){
    3. UserDao dao = new UserFactory.getDao();
    4. }
    5. }
    6. class UserDao{
    7. ...
    8. }
    9. class UserFactory{
    10. public static UserDao getDao(){
    11. return new UserDao();
    12. }
    13. }
  • 反射就是java代码可以动态获取类的字节码文件,从而可以得到类的属性和方法等。

IOC过程

  1. xml配置文件,配置创建的对象
  2. 有service类,dao类,创建工厂类
    1. class UserFactory{
    2. public static UserDao getDao(){
    3. String classValue = class属性值 //1 xml解析
    4. Class class = Class.forName(classValue);//2 通过反射创建对象
    5. return (UserDao) class.newInstance();
    6. }
    7. }

IOC接口

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

      一般把耗时耗资源的过程交给服务器启动的时候去完成,所以ApplicationContext更合适。

IOC操作Bean管理——XML方式

Bean管理是Spring的两个操作:Spring创建对象;Spring注入属性(不需要写set方法,这里的属性可以是基本类型,也可以是类对象)。

Bean管理操作有两种方式:基于xml配置文件方式实现;基于注解方式实现。

基于xml方式

  • 创建对象
    1. <bean id="user" class="com.atguigu.spring5.User">

  • 注入属性—基本数据类型DI:依赖注入,就是注入属性。
    1. <bean id="user" class="com.atguigu.spring5.User">

    2. <property name="userName" value="xing">

    3.  

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


    1. <bean id="user" class="com.atguigu.spring5.User" p:userName="xing">

    xml注入其他属性


    1. <property name="userName">


    2. <property name="userName">
    3. >]]>
    4.  
  • 注入属性—外部bean创建一个service和dao对象,其中service里面有一个dao作为属性
    1. public class UserService {
    2. //创建UserDao类型属性,生成set方法
    3. private UserDao userDao;
    4. public void setUserDao(UserDao userDao) {
    5. this.userDao = userDao;
    6. }
    7. public void add(){
    8. userDao.update();
    9. }
    10. }

    配置文件


    1. <bean id="userService" class="com.atguigu.spring5.service.UserService">


    2. <property name="userDao" ref="userDaoImpl">
    3.  

    4. <bean id="userDaoImpl" class="com.atguigu.spring5.dao.UserDaoImpl">
  • 注入属性—内部bean内部bean可以理解在一个bean里面嵌套定义另外一个bean,实际应用中,更常用外部bean,因为更加清晰。
    1. <bean id="userService" class="com.atguigu.spring5.service.UserService">
    2. <property name="userDao">
    3. <bean id="userDaoImpl" class="com.atguigu.spring5.dao.UserDaoImpl">

    4.  
  • 注入属性—级联赋值级联赋值就是注入外部bean的时候,在外部bean里面赋值,比如:
    1. <bean id="userService" class="com.atguigu.spring5.service.UserService">
    2. <property name="userDao" ref="userDaoImpl">
    3. <bean id="userDaoImpl" class="com.atguigu.spring5.dao.UserDaoImpl">
    4. <property name="userDaoProperty" value="value">
    5.  

    还有一种方式:这种方式要在userService中设置userDaoImpl的set方法,因为userDaoImpl.userDaoProperty要通过set方法来获取

    1. <bean id="userService" class="com.atguigu.spring5.service.UserService">
    2. <property name="userDao" ref="userDaoImpl">
    3. <property name="userDaoImpl.userDaoProperty" value="value2">
    4.  
  • 注入属性—集合属性
    1. public class Stu {
    2. private String[] courses;
    3. private List<String> list;
    4. private Map<String,String> map;
    5. //对象list
    6. private List<Course> courseList;
    7. public void setCourses(String[] courses) {
    8. this.courses = courses;
    9. }
    10. public void setList(List<String> list) {
    11. this.list = list;
    12. }
    13. public void setMap(Map<String, String> map) {
    14. this.map = map;
    15. }
    16. public void setCourseList(List<Course> courseList) {
    17. this.courseList = courseList;
    18. }
    19. }
    1. <bean id="stu" class="com.atguigu.spring5.collection.Stu">

    2. <property name="courses">

    3. java课程
    4. 数据库标签



    5. <property name="list">

    6. 张三
    7. 李四



    8. <property name="map">

    9.  

    10. <entry key="Java" value="java">



    11. <property name="courseList">

    12. <ref bean="course1">
    13. <ref bean="course2">


    14.  
    15. <bean id="course1" class="com.atguigu.spring5.collection.Course">
    16. <property name="cname" value="Spring5框架">
    17.  
    18. <bean id="course2" class="com.atguigu.spring5.collection.Course">
    19. <property name="cname" value="Mybatis框架">
    20.  

    还可以把集合注入部分提取出来,方法是在spring配置文件中引入新的名称空间比如util,模版如下:

    1. <beans xmlns="http://www.springframework.org/schema/beans"
    2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    3. xmlns:util="http://www.springframework.org/schema/util"
    4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    5. http://www.springframework.org/schema/util http://www.springframework.org/schema/utils/spring-utils.xsd">

    6. <util:list id="bookList">
    7. java
    8. c
    9. <bean id="book" class="com.atguigu.spring5.collection.Book">
    10. <property name="list" ref="bookList">

    11.  

FactoryBean

Spring有两种类型的bean,一种普通bean,另一种工厂bean(FactoryBean)。

普通bean在Spring配置文件中定义什么类型,返回就是什么类型;而工厂bean定义的类型和返回的类型可以不一致。

做法:

  • 创建类,让这个类作为工厂bean,实现接口FactoryBean
  • 实现接口里面的方法,在实现的方法中定义返回的bean类型
    1. public class MyBean implements FactoryBean<Course> {
    2. //定义返回bean
    3. public Course getObject() throws Exception {
    4. Course course = new Course();
    5. course.setCname("java");
    6. return course;
    7. }
    8. public Class<?> getObjectType() {
    9. return null;
    10. }
    11. }
    1. <bean id="myBean" class="com.atguigu.spring5.factorybean.MyBean">
    1. public class TestSpring5 {
    2. ApplicationContext context = new FileSystemXmlApplicationContext("/src/main/java/bean1.xml");
    3. Course course = context.getBean("myBean",Course.class);
    4. }

Bean的作用域

bean的作用域是指:在Spring里面,设置创建bean实例是单实例还是多实例(每次getBean都获取新对象),默认情况下是单实例对象。

在bean标签里面scope属性用来设置单实例还是多实例,默认值singletonprototype表示多实例。

两个值的区别:singleton时候,加载spring配置文件时候就会创建单实例对象,而prototype在调用getBean方法时候才会去创建。

Bean的生命周期

  1. 通过构造器创建bean实例(无参数构造)
  2. 为bean的属性设置值和对其他bean引用(调用set方法)
  3. *把bean实例传递bean后置处理器(实现BeanPostProcessor的类对象)的方法
  4. 调用bean的初始化方法(需要配置初始化的方法 bean标签的init-method属性
  5. *把bean实例传递bean后置处理器的方法
  6. bean可以使用了(对象获取到了)
  7. 当容器关闭的时候(context.close()),调用bean的销毁方法(需要配置销毁的方法 bean标签的destroy-method属性

xml自动装配

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


  1. <bean id="emp" class="com.atguigu.spring5.autowire.Emp" autowire="byName">
  2. <bean id="dept" class="com.atguigu.spring5.autowire.Dept">

基于xml的自动装配很少用到,基本都是使用注解

引入外部属性文件

以配置数据库连接池为例:

  1. 创建外部属性文件.properties格式文件,写数据库信息
    1. prop.driverClass=com.mysql.jdbc.Driver
    2. prop.url=jdbc:mysql://localhost:3306/userDb
    3. prop.userName=root
    4. prop.passWord=root
    5. #等号左边可以随便写,但建议不要写某一个单词,容易冲突
  2. 把外部properties属性文件引入到spring配置文件中首先引入context名称空间,先后在spring配置文件中使用标签引入外部属性文件

    1. <context:property-placeholder location="classpath:jdbc.properties">

    2. <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    3. <property name="driverClassName" value="${prop.driverClass}">
    4. ...
    5.  

IOC操作Bean管理——注解方式

注解可以作用在类、属性、方法上面,可以简化我们的xml配置。

Spring针对Bean管理中创建对象提供的注解四种,他们都作用在类上面:

  • @Component
  • @Service
  • @Controller
  • @Repository

*** 这四个注解功能是一样的,都可以用来创建bean实例,只不过在开发中为了清晰的表明这个bean用在哪一个层上面**

使用注解创建对象

  1. 引入AOP依赖
    1.  
    2. org.springframework
    3. spring-aop
    4. 5.2.8.RELEASE
    5.  
  2. 开启组件扫描指定要扫描的包,这个包中的类有注解就回去创建对象。

    首先引入context名称空间


    1. <context:component-scan base-package="com.atguigu.spring5">
  3. 创建类,在类上面添加创建对象注解即可
    1. //value属性可以省略不写,默认值是类名称首字母小写
    2. @Component(value = "userService") //
    3. public class UserService{
    4. //...
    5. }

开启组件扫描细节配置

这个表示只扫描包里面带Controller注解的类


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

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

基于注解方式实现属性注入

  • @AutoWired:根据属性类型进行自动装配(byType),不需要添加set方法,如果接口有多个实现类的话,@AutoWired就不知道找哪个了,这个时候就要配合@Qualifier根据名称进行自动装配了
  • @Qualifier:根据属性名称进行自动装配(byName),要和@AutoWired一起使用
    1. @AutoWired
    2. @Qualifier(value = "userDaoImpl")
    3. private UserDao userDao;
  • @Resource:可以根据类型注入,也可以根据名称注入。这个注解在javax.annotation.Resource包中,不是Spring本身提供的,因此官方建议使用前两者。
  • @Value:注入普通类型属性
    1. @Value(value="abc")
    2. private String name;

完全注解开发

  1. 创建配置类,替代xml配置文件
    1. @Configuration //Spring才能识别为配置类,替代xml文件
    2. @ComponentScan(basePackages = {"com.atguigu.spring5"})
    3. public class SpringConfig {
    4. }
  2. 加载配置类
    1. ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);

Spring AOP

概念

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

通俗点说就是:在不修改源代码的情况下增强类的功能。

底层原理

AOP底层使用动态代理。有两种情况动态代理:

  • 有接口情况:使用JDK动态代理,创建接口实现类代理对象来增强接口功能
  • 和没有接口情况:使用CGLIB动态代理,创建当前类子类的代理对象来增强类功能

使用JDK动态代理:

  1. 使用java.lang.reflect.Proxy类里面的newProxyInstance方法创建代理对象,首先我们创建接口以及实现类
    1. static Object newProxyInstance(ClassLoader loader,class<?>[] interfaces,InvocationHandler h);
    2. //返回指定接口的代理类实现,该接口将方法调用分派给指定的调用处理程序
    3. //第一个参数:类加载器
    4. //第二个参数:增强类所实现的接口,可以有多个
    5. //第三个参数:实现InvocationHandler接口的一个对象,这个对象里面通过实现invoke()方法来写增强的逻辑
    1. public interface UserDao {
    2. public int add(int a,int b);
    3. public String update(String id);
    4. }
    5. //-------
    6. public class UserDaoImpl implements UserDao{
    7. public int add(int a, int b) {
    8. System.out.println("add方法执行了。。。");
    9. return a b;
    10. }
    11. public String update(String id) {
    12. System.out.println("update方法执行了。。。");
    13. return id;
    14. }
    15. }
  2. 使用Proxy类创建接口实现类代理对象
    1. public class JDKProxy {
    2. public static void main(String[] args) {
    3. //创建接口实现类代理对象
    4. Class[] interfaces = {UserDao.class};
    5. // Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
    6. // public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    7. // return null;
    8. // }
    9. // });
    10. UserDaoImpl userDao = new UserDaoImpl();
    11. UserDao dao = (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(),interfaces,new UserDaoProxy(userDao));
    12. int result = dao.add(1,2);
    13. System.out.println("result:" result);
    14. }
    15. }
    16. class UserDaoProxy implements InvocationHandler{
    17. //1 创建的是谁的代理对象,就把谁传进来
    18. //有参构造传递
    19. private Object obj;
    20. public UserDaoProxy(Object obj){
    21. this.obj = obj;
    22. }
    23. //写增强逻辑
    24. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    25. //方法之前
    26. System.out.println("方法之前执行。。。" method.getName() "传递的参数:" Arrays.toString(args));
    27. //被增强的方法执行
    28. //这里也可以判断执行的方法去做不同的处理
    29. Object res = method.invoke(obj,args);
    30. //方法执行之后
    31. System.out.println("方法之后执行..." obj);
    32. return null;
    33. }
    34. }

AOP术语

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

Spring框架中一般都是基于AspectJ实现AOP操作,需要引入AOP相关依赖


  1. org.springframework
  2. spring-aop
  3. 5.2.8.RELEASE



  4. org.springframework
  5. spring-aspects
  6. 5.2.8.RELEASE


  7. org.aspectj
  8. aspectjtools
  9. 1.9.5


  10. aopalliance
  11. aopalliance
  12. 1.0


  13. org.aspectj
  14. aspectjweaver
  15. 1.9.0


  16. cglib
  17. cglib
  18. 3.3.0


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

AOP操作

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

语法结构

  1. execution([权限修饰符][返回类型][类全路径][方法名称][参数列表])
  1. 对com.atguigu.dao.BookDao类里面的add方法进行增强
    1. execution(* com.atuguigu.dao.BookDao.add(..))

    *表示所有修饰符,返回类型可以省略

  2. 对com.atguigu.dao.BookDao类里面的所有方法增强
    1. execution(* com.atuguigu.dao.BookDao.*(..))
  3. 对com.atguigu.dao包里面所有类,类里面的所有方法进行增强
    1. execution(* com.atuguigu.dao.*.*(..))

AspectJ注解

  1. 创建类,在类里面定义方法
    1. //被增强类
    2. @Component
    3. public class User {
    4. public void add(){
    5. System.out.println("add....");
    6. }
    7. }
  2. 创建增强类(编写增强逻辑)在增强类里面创建方法,让不同的的方法代表不同的通知类型
    1. //增强类
    2. @Component
    3. @Aspect //生成代理对象
    4. public class UserProxy {
    5. //Before注解表示作为前置通知
    6. @Before(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    7. public void before(){
    8. System.out.println("before....");
    9. }
    10. @After(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    11. public void after(){
    12. System.out.println("after...");
    13. }
    14. //方法返回后执行,比after早,有异常不执行
    15. @AfterReturning(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    16. public void afterReturning(){
    17. System.out.println("afterReturning...");
    18. }
    19. //有异常执行
    20. @AfterThrowing(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    21. public void afterThrowing(){
    22. System.out.println("afterThrowing...");
    23. }
    24. //环绕通知:在方法之前和之后都通知
    25. @Around(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    26. public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
    27. System.out.println("环绕之前...");
    28. //被增强的方法执行
    29. proceedingJoinPoint.proceed();
    30. System.out.println("环绕之后...");
    31. }
    32. }
  3. 进行通知配置在spring配置文件中,开启注解扫描,开启生成代理的对象
    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <beans xmlns="http://www.springframework.org/schema/beans"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xmlns:context="http://www.springframework.org/schema/context"
    5. xmlns:aop="http://www.springframework.org/schema/aop"
    6. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    7. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
    8. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">


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

    10.  
  4. 配置不同类型的通知在增强类里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置。
  5. 测试
    1. @Test
    2. public void testAopAnno(){
    3. ApplicationContext context = new FileSystemXmlApplicationContext("/src/main/java/bean1.xml");
    4. User user = context.getBean("user", User.class);
    5. user.add();
    6. }
    7. /*
    8. output:
    9. 环绕之前...
    10. before....
    11. add....
    12. afterReturning...
    13. after...
    14. 环绕之后...
    15. */
  6. 相同的切入点抽取
    1. //相同切入点抽取
    2. @Pointcut(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    3. public void pointdemo(){
    4. }
    5. //Before注解表示作为前置通知
    6. @Before(value = "pointdemo()")
    7. public void before(){
    8. System.out.println("before....");
    9. }
  7. 有多个增强类对同一个方法进行增强,设置增强类的优先级。在增强类上面添加注解@Order(数字),数字值越小优先级越高

AspectJ配置文件

AspectJ也可以通过配置文件使用

JdbcTemplate

概念

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

准备工作

  1. 引入依赖

    1. com.alibaba
    2. druid
    3. 1.1.23


    4. mysql
    5. mysql-connector-java
    6. 5.1.47


    7. org.springframework
    8. spring-jdbc
    9. 5.2.10.RELEASE


    10. org.springframework
    11. spring-tx
    12. 5.2.10.RELEASE


    13. org.springframework
    14. spring-orm
    15. 5.2.6.RELEASE

  2. 在spring配置文件配置数据库连接池
  3. 配置JdbcTemplate对象,注入DataSource
    1. <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">

    2. <property name="dataSource" ref="dataSource">

  4. 创建dao类,注入jdbcTemplate对象;创建service类,注入dao对象

CRUD操作

  1. 增删改操作使用的是update(String sql, Object... args)函数
    1. @Repository
    2. public class BookDaoImpl implements BookDao{
    3. //注入jdbcTemplate
    4. private JdbcTemplate jdbcTemplate;
    5. //添加方法
    6. @Override
    7. public void add(Book book){
    8. //1 创建sql语句
    9. String sql = "insert into t_book values(?,?,?)";
    10. //2 调用方法实现
    11. Object[] args = {book.getUserId(),book.getUsername(),book.getUstatus};
    12. int update = jdbcTemplate.update(sql,args);
    13. }
    14. }
  2. 查询返回某一个值查询操作使用的是queryForObject(String sql, Class requiredType)方法,第二个参数表示查询操作的返回类型。
    1. String sql = "select count(*) from t_book";
    2. Integer count = jdbcTemplate.queryForObject(sql,Integer.class);
  3. 查询返回对象queryForObject(String sql,RowMapper rowMapper,Object... args)

    query(String sql,RowMapper rowMapper,Object... args):返回对象列表

    第二个参数rowMapper是接口,针对返回不同类型数据,使用这个接口里面实现类完成数据封装

    1. String sql = "select * from t_book where user_id=?";
    2. //调用方法
    3. Book book = jdbcTemplate.queryForObject(sql,new BeanPropertyRowMapper<Book>(Book.class),id);
  4. 批量操作batchUpdate(String sql,List<Object[]> batchArgs)
    1. public void batchAdd(List<Object[]> batchArgs){
    2. String sql = "insert into t_book values(?,?,?)";
    3. int[] ints = jdbcTemplate.batchUpdate(sql,batchArgs);
    4. }

Spring声明式事务

Sping事务管理介绍

  1. 事务添加到JavaEE三层结构里面Service层(业务逻辑层)
  2. 在Spring进行事务管理操作有两种方式:编程式和声明式编程式
    1. public void accountMoney(){
    2. try{
    3. //1 开启事务
    4. //2 进行业务操作
    5. userDao.reduceMoney();
    6. userDao.addMoney();
    7. //3 没有发生异常,提交事务
    8. }catch(Exception e){
    9. //4 出现异常 事务回滚
    10. }
    11. }
  3. 声明式事务管理有基于注解方式,也有基于xml配置文件方式
  4. 在Spring进行声明式事务管理,底层使用AOP原理
  5. Spring事务管理API提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类

注解方式

  1. 在配置文件中配置事务管理器,并开启事务注解

    1. <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

    2. <property name="dataSource" ref="dataSource">

    3. <tx:annotation-driven transaction-manager="transactionManager">
  2. 在service类上面添加事务注解@Transactional这个注解可以添加到类上面,也可以添加上方法上面,如果添加上类上面表示这个类里面所有的方法都添加事务。
  3. 这个注解里面可以配置事务相关的参数propagation:事务传播行为。多事务方法(对数据库表中的数据发生变化的操作)进行互相调用,这个过程中事务是如何进行管理的。Spring定义了7种传播行为

    isolation:事务的隔离级别,默认是可重复读

    timeout:超时时间。事务需要在一定时间内进行提交,如果不提交进行回滚。默认值-1(没有超时),单位秒

    readOnly:是否只读。默认false,true表示只能做查询操作

    rolllbackFor:回滚。设置出现哪些异常进行回滚

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

xml方式

  1. 配置事务管理器
  2. 配置通知
  3. 配置切入点和切面

  1. <tx:advice id="txadvice">



  2. <tx:method name="accountMoney" propagation="REQUIRED">

  3.  

  4. <aop:pointcut id="pt" expression="execution(* com.atguigu.spring5.service.UserService.*(..))"/>

  5. <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
  6.  

完全注解开发

  1. 创建配置类替代xml配置文件
    1. @Configuration
    2. @ComponentScan(basePackages="com.atguigu")
    3. @EnableTransactionManagement //开启事务
    4. public class TxConfig{
    5. //创建数据库连接池
    6. @Bean
    7. public DruidDataSource getDruidDataSource(){
    8. DruidDataSource dataSource = new DruidDataSource();
    9. dataSource.SetDriverClassName("");
    10. dataSource.SetUrl("");
    11. dataSource.SetUsername("");
    12. dataSource.SetPassword("");
    13. return dataSource;
    14. }
    15. //创建JdbcTemplate对象
    16. @Bean
    17. public JdbcTemplate getJdbcTemplate(DataSource dataSource){
    18. //到ioc容器中根据类型找到dataSource
    19. JdbcTemplate jdbcTemplate = new JdbcTemplate();
    20. //注入dataSource
    21. jdbcTemplate.setDataSource(dataSource);
    22. return jdbcemplate;
    23. }
    24. //创建事务管理器
    25. @Bean
    26. public DataSourceTransactionManager getDataSourceTransationManager(DataSource dataSource){
    27. DataSourceTransactionManager transactionManager = new DataSourceTransationManager();
    28. transactionManager.setDataSource(dataSource);
    29. return transactionManager;
    30. }
    31. }

Spring5新功能

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

整合日志框架

Spring5自带类通用的日志封装,已经移除了Log4jConfigListener,官方建议使用Log4j2。

Nullable注解和函数式注册对象

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

Spring5核心容器支持函数时风格GenericApplicationContext,通过lambda表达式注册对象

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

支持整合JUnit5

SpringWebFlux

简介

WebFlux是Spring5添加新的模块,用于web开发,功能和SpringMVC类似,WebFlux使用当前一种比较流行的响应式出现的框架。

传统的web框架比如SpringMVC,这些基于Servlet容器,WebFlux是一种异步非阻塞的框架,异步非阻塞的框架在Servlet3.1以后才支持,核心是基于Reactor的相关API实现。

同步异步针对调用者:调用者发送请求,如果等着对方回应之后才去做其他事情就是同步,如果发送请求之后不等着对方回应就去做其他事情就是异步

阻塞和非阻塞针对被调用者:被调用者收到请求之后,做完请求任务之后才给出反馈就是阻塞,收到请求之后马上给出反馈然后再去做其他事情就是非阻塞

WebFlux特点:

  • 非阻塞:在有限资源下,提高系统的吞吐量和伸缩性,以Reactor为基础实现响应式编程
  • 函数式编程:Spring5框架基于java8,WebFlux使用java8函数式编程方式实现路由请求

WebFlux VS SpringMVC

  • 都可以使用注解方式,都运行在Tomcat等容器中
  • SpringMVC采用命令式编程,WebFlux采用异步响应式编程

响应式编程

响应式编程是一种面向数据流和变化传播的编程范式,这意味着可以在编程语言中很方便的表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。

响应式编程使用观察者模式,在java8及之前的版本提供的观察者模式两个类Observer和Observerable。

在java8之后被Flow类取代

Reactor实现

响应式编程操作中,Reactor是满足Reactive规范框架,其有两个核心类Mono和Flux,这两个类实现接口Publisher,提供丰富操作符。Flux对象实现发布者,返回N个元素;Mono实现发布者返回0或1个元素。

Flux和Mono都是数据流的发布者,使用Flux和Mono都可以发出三种数据信号:元素值、错误信号、完成信号。后两个代表终止信号,用于告诉订阅者数据流结束了,错误信号在终止数据流的同时把错误信息传递给订阅者。

  1. 引入依赖
    1.  
    2. io.projectreactor
    3. reactor-core
    4. 3.3.5.RELEASE
    5.  
    1. public static void main(String[] args) {
    2. //just方法直接声明
    3. Flux.just(1,2,3,4);
    4. Mono.just(1);
    5. //其他方法
    6. Integer[] array = {1,2,3,4};
    7. Flux.fromArray(array);
    8. List<Integer> list = Arrays.asList(array);
    9. Flux.fromIterable(list);
    10. }
  2. 错误信号和完成信号都是终止,不能共存;如果没有发送任何元素值,而是直接发送错误或完成信号表示空数据流;如果没有错误信号,没有完成信号,表示无限数据流。
  3. 调用just或者其他方法只是声明数据流,数据流并没有发出,只有进行订阅之后才会触发数据流。
  4. 对数据流进行一道道操作,成为操作符,比如工厂流水线map:元素映射为新元素

    flatMap:把每个元素转换成流,把转换之后的多个流合并成大的流

WebFlux执行流程和核心API

WebFlux基于Reactor,默认容器是Netty,Netty是高性能的NIO框架,异步非阻塞的框架。

SpringWebFlux执行过程和SpringMVC相似:

  • SpringWebFlux核心控制器DispatchHandler,负责请求的处理,实现WebHandler接口
  • HandlerMapping:请求查询到处理的方法
  • HandlerAdapter:真正负责请求处理
  • HandlerResultHandler:响应结果处理

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

基于注解编程模型

使用注解编程模型方式,和之前SpringMVC使用相似,只需要把相关依赖配置到项目中,SpringBoot自动配置相关运行容器,默认情况下使用Netty服务器。

  1. 引入webflux相关依赖,创建一个SpringBoot工程

    1. org.springframework.boot
    2. spring-boot-starter-webflux
    3. 2.3.5.RELEASE

  2. 创建实体类User,以及service类
    1. @Service
    2. public class UserServiceImpl implements UserService {
    3. //创建map集合存储数据
    4. private final Map<Integer,User> users = new HashMap<Integer, User>();
    5. public UserServiceImpl(){
    6. this.users.put(1,new User("lucy","boy",20));
    7. this.users.put(2,new User("mary","girl",20));
    8. this.users.put(3,new User("jack","boy",20));
    9. }
    10. public Mono<User> getUserById(int id) {
    11. return Mono.justOrEmpty(this.users.get(id));
    12. }
    13. public Flux<User> getAllUser() {
    14. return Flux.fromIterable(this.users.values());
    15. }
    16. public Mono<Void> saveUserInfo(Mono<User> userMono) {
    17. return userMono.doOnNext(person ->{
    18. //向map集合里面放值
    19. int id = users.size();
    20. users.put(id,person);
    21. }).thenEmpty(Mono.empty());//Mone.empty()表示数据流结束
    22. }
    23. }
  3. 创建controller
    1. @RestController
    2. public class UserController {
    3. @Autowired
    4. private UserService userService;
    5. @GetMapping("/user/{id}")
    6. public Mono<User> getUserById(@PathVariable int id){
    7. return userService.getUserById(id);
    8. }
    9. @GetMapping("/user")
    10. public Flux<User> getUsers(){
    11. return userService.getAllUser();
    12. }
    13. @PostMapping("/saveuser")
    14. public Mono<Void> saveUser(@RequestBody User user){
    15. Mono<User> userMono = Mono.just(user);
    16. return userService.saveUserInfo(userMono);
    17. }
    18. }

虽然形式上和SpringMVC方式差不多,但是底层不一样了。前者基于SpringMVC Servelet Tomcat,后者基于SpringWebFlux Reactor Netty。

基于函数式编程模型

在使用函数式编程模型操作时,需要自己初始化服务器。

基于函数式编程模型的时候,有两个核心接口:RouterFunction(实现路由功能,请求转发给对应的Handler)和HandlerFunction(处理请求生成响应的函数)。核心任务定义两个函数式接口的实现并且启动需要的服务器。

SpringWebFlux请求和响应不再是ServletRequest和ServeltResponse,而是ServerRequest和ServerRespone。

  1. 创建Handler
    1. public class UserHandler {
    2. private final UserService userService;
    3. public UserHandler(UserService userService){
    4. this.userService = userService;
    5. }
    6. public Mono<ServerResponse> getUserById(ServerRequest serverRequest){
    7. int userID = Integer.valueOf(serverRequest.pathVariable("id"));
    8. //空值处理
    9. Mono<ServerResponse> notFount = ServerResponse.notFound().build();
    10. Mono<User> userMono = this.userService.getUserById(userID);
    11. //把userMono进行转换返回
    12. //使用Reactor操作符flatMap
    13. return userMono.flatMap(person -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
    14. .body(fromObject(person)))
    15. .switchIfEmpty(notFount);
    16. }
    17. public Mono<ServerResponse> getAllUsers(ServerRequest serverRequest){
    18. //调用service得到结果
    19. Flux<User> users = this.userService.getAllUser();
    20. return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(users,User.class);
    21. }
    22. public Mono<ServerResponse> saveUser(ServerRequest serverRequest){
    23. //得到user对象
    24. Mono<User> userMono = serverRequest.bodyToMono(User.class);
    25. return ServerResponse.ok().build(this.userService.saveUserInfo(userMono));
    26. }
    27. }
  2. 初始化服务器,编写Router和adapter
    1. public class Server {
    2. public static void main(String[] args) throws Exception {
    3. Server server = new Server();
    4. server.createReactorService();
    5. System.out.println("enter to exit");
    6. System.in.read();
    7. }
    8. //1 创建Router路由
    9. public RouterFunction<ServerResponse> routingFunction(){
    10. UserService userService = new UserServiceImpl();
    11. UserHandler userHandler = new UserHandler(userService);
    12. return RouterFunctions.route(
    13. GET("/users/{id}").and(accept(MediaType.APPLICATION_JSON)),userHandler::getUserById
    14. ).andRoute(
    15. GET("/users").and(accept(MediaType.APPLICATION_JSON)),userHandler::getAllUsers
    16. );
    17. }
    18. // 2 创建服务器完成适配
    19. public void createReactorService(){
    20. //路由和Handler适配
    21. RouterFunction<ServerResponse> route = routingFunction();
    22. HttpHandler httpHandler = toHttpHandler(route);
    23. ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
    24. //创建服务器
    25. HttpServer httpServer = HttpServer.create();
    26. httpServer.handle(adapter).bindNow();
    27. }
    28. }

文章来源参考-尹瑞星-Spring5开发教程

相关文章

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注