zoukankan      html  css  js  c++  java
  • Spring5笔记

    Spring5

    视频地址

    1,Spring概念

    Spring框架概述

    1. Spring是轻量级的开源的JavaEE框架
    2. Spring可以解决企业应用开发的复杂性
    3. Spring有两个核心部分:IOCAOP
      • IOC:控制反转,把创建对象过程交给Spring管理。
      • AOP:面向切面,不修改源代码的情况下进行功能增强。
    4. Spring特点:
      • 方便解耦,简化开发。
      • AOP编程的支持。
      • 方便程序测试。
      • 方便和其他框架进行整合。
      • 降低JavaEE APL开发难度。
      • 方便进行事务操作。
    5. 现在课程中,选取Spring5版本5.X

    入门案例

    1. 下载Spring5:https://spring.io/projects/spring-framework#learn

      左侧选择Spring framework,右侧选择learn, 选择GA版本,GA是稳定版本。

      下一步选择GetHub,去GetHub下载,下拉选择Access to Binaries

      下拉选择Downloading a Distribution

      到新网页,选择Artifacts -》libs-release-》org-》springframeword-》spring

      右侧赋值Repository Path 地址,在地址栏上 https://repo.spring.io/追加到后面

      下载地址:https://repo.spring.io/libs-release/org/springframework/spring/

    2. 用idea创建一个普通的module

    3. 导入Spring5的包

      beans core context expression

      还需要一个commons logging 日志包

      日志包网址:http://commons.apache.org/proper/commons-logging/download_logging.cgi 选择二进制文件

    4. 创建普通类,在这个类创建普通方法。

      public class User {
      	public void add() {
      		System.out.println("add...");
      	}
      }
      
    5. spring中创建对象可以通过配置文件或者注解。

      创建spring配置文件,在配置文件使用xml格式,在src下

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
          <!--配置User对象创建-->
          <bean id="user" class="com.zhiyou100.User"></bean> <!--在bean中可以写两个属性 id:别名 class:类的全路径-->
      </beans>
      
    6. 进行测试代码编写

          @Test
          public void testAdd() {
              //1. 加载spring配置文件
              //创建类路径Xml应用程序上下文
             ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
              //2. 获取配置创建的对象
              //根据xml中的id传入,和要传入的类字节码文件
              User user = context.getBean("user", User.class);
              System.out.println(user);
              user.add();
          }
      

    2,IOC容器

    IOC概念原理

    1. 什么是IOC?

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

      • 使用IOC目的:为了耦合度降低

      • 做的入门案例就是IOC实现

    2. IOC底层原理

      xml解析,工厂模式,反射

    3. 图画讲解IOC底层原理

      image

      image

    IOC接口

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

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

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

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

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

        特点:加载配置文件的时候就会把配置文件对象进行创建。

      • 他们都能进行IOC容器实现,功能都可以做到。

    3. ApplicationContext 接口的实现类

      image

      FileSystemXmlApplicationContext配置文件就要写盘符里面的--绝对路径

      ClassPathXmlApplicationContextsrc下类路径--相对路径

    4. BeanFactory 接口的实现类

      image

    Bean管理

    1. 什么是Bean管理?
      Bean管理指的是两个操作
      1.Spring创建对象
      2.Spring注入属性

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

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

    IOC操作Bean管理(基于xml)

    1. 基于xml方式创建对象

      <bean id="user" class="com.atguigu.spring5.User"></bean>
      
      1. 在Spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建。

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

        • id属性:唯一标识

        • class属性:类全名路径(包类路径)

        • name属性: 和id属性一样

          id和name属性的区别是:id不能加特殊符号,name可以。

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

    2. 基于xml方式注入属性

      1. DI:依赖注入,就是注入属性。di是ioc的一种具体实现,就是依赖输入。必须式在创建对象的前提下。

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

        • 创建类,定义属性和对应的set方法

          public class Book {
              private String bName;
              private String bAuthor;
              public void setbName(String bName) {this.bName = bName;}
              public void setbAuthor(String bAuthor) {this.bAuthor = bAuthor;}
              public void show() {System.out.println(bName+"的作者是:"+bAuthor);}
          }
          
        • 在spring配置文件配置对象创建,配置属性注入

          <!--1. 配置book对象创建-->
          <!--在bean中可以写两个属性 id:别名 class:类的全路径-->
          <bean id="book" class="com.zhiyou100.Book">
              <!--2. set方法注入属性-->
              <!--使用property完成属性注入
                      name:类里面属性名称
                      value:向属性注入的值-->
              <property name="bName" value="《西游记》"></property>
              <property name="bAuthor" value="吴承恩"></property>
          </bean>
          
        • 测试类

          @Test
          public void testBook1() {
              //1. 加载spring配置文件
              BeanFactory context = new ClassPathXmlApplicationContext("bean1.xml");
              //2. 获取配置创建的对象
              Book book = context.getBean("book", Book.class);
              book.show();
          }
          
      3. 第二种方式:使用有参构造方法进行注入

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

          public class Orders {
              private String oName;
              private String address;
              public Orders(String oName, String address) {this.oName = oName;this.address = address;}
              public void show() { System.out.println("名字:" + oName + ",地址:" + address); }
          }
          
        • 在spring配置文件中配置

          <bean id="order" class="com.zhiyou100.Orders">
              <!--使用constructor-arg标签进行构造方法赋值
                  name:属性
                  value:值  -->
              <constructor-arg name="address" value="电脑"></constructor-arg>
              <constructor-arg name="oName" value="China"></constructor-arg>
              <!--或者使用index=属性 value=值 也可以-->
            	<!--<constructor-arg index="0" value="电脑"></constructor-arg>
            	<constructor-arg index="1" value="China"></constructor-arg> -->
          </bean>
          
        • 测试:

          @Test
          public void testOrder() {
              //创建类路径Xml应用程序上下文,加载spring配置文件
              ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
              //根据方法传入属性创建对象
              Orders order = context.getBean("order", Orders.class);
              order.show();
          }
          
    3. p名称空间注入(set方法注入)

      • 使用p名称空间注入,可以简化xml配置方式
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             第一步:添加p名称空间在配置文件中
             xmlns:p="http://www.springframework.org/schema/p"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
          	第二步:进行属性注入,在bean标签里面进行操作
             <bean id="book" class="com.zhiyou100.Book" p:bAuthor="金庸" p:bName="《笑傲江湖》"></bean>
      </beans>
      

      测试:之前那个就可以(o)!

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

    1. xml注入其他类型属性

      1. 字面量:

        • null值

          <!--根据set方法赋值-->
          <property name="address" ><null/></property>
          
        • 属性值包含特殊符号:

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

      1. 创建两个类service类和dao类

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

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

        public interface UserDao {
            public void update();
        }
        public class UserDaoImpl implements UserDao {
            @Override
            public void update() {System.out.println("dao update ......");}
        }
        public class UserService {
            //属性注入
            private UserDao userDao;
            public void setUserDao(UserDao userDao) {this.userDao = userDao;}
        
            public void add() {
                System.out.println("service add ......");
                //调用方法
                userDao.update();}
        }
        
        <!--创建service和dao对象-->
        <bean id="userService" class="com.zhiyou100.service.UserService">
            <!--注入userDao对象
                ref属性:创建userDao对象bean标签id值
                ref=bean标签id值(传入对象)
            -->
            <property name="userDao" ref="userDaoImp"></property>
        </bean>
        <bean id="userDaoImp" class="com.zhiyou100.dao.UserDaoImpl"></bean>
        
    3. 注入属性 --内部bean

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

        一个部门有对个员工,一个员工属于一个部门

        部门是一,员工是多。

      2. 在实体类之间标识一对多关系

        public class Dept {
            private String dName;
            public void setdName(String dName) {this.dName = dName;}
            public String getdName() {return dName;}
        }
        public class Emp {
            private  String eName;
            private  String gender;
            //员工属于一个部门,使用对象形式表示
            private Dept dept;
            public void setDept(Dept dept) {this.dept = dept;}
            public void seteName(String eName) {this.eName = eName;}
            public void setGender(String gender) {this.gender = gender;}
            public void show() {System.out.println(eName + "," + gender + "," + dept.getdName());}
        }
        
        <!--内部bean-->
        <bean id="emp" class="com.zhiyou100.bean.Emp">
            <!--设置两个不同普通属性-->
            <property name="eName" value="lucy"></property>
            <property name="gender" value="男"></property>
            <!--设置对象属性-->
            <property name="dept">
                <!--在内部创建对象-->
                <bean id="dept" class="com.zhiyou100.bean.Dept">
                    <property name="dName" value="保安部"></property>
                </bean>
            </property>
        </bean>
        
    4. 注入属性 --级联赋值

      • 第一种:(和3一样只是xml配置不一样)

            <!--级联赋值-->
            <bean id="emp" class="com.zhiyou100.bean.Emp">
                <!--设置两个不同普通属性-->
                <property name="eName" value="lucy"></property>
                <property name="gender" value="男"></property>
                <!--级联赋值-->
                <property name="dept" ref="dept"></property>
            </bean>
            <bean id="dept" class="com.zhiyou100.bean.Dept">
                <property name="dName" value="财务部"></property>
            </bean>
        
      • 第二种:

        需要做一件事情,在emp中属性dept生成get方法,如果取不到就不能赋值

        <!--级联赋值-->
        <bean id="emp" class="com.zhiyou100.bean.Emp">
            <!--设置两个不同普通属性-->
            <property name="eName" value="lucy"></property>
            <property name="gender" value="男"></property>
            <!--级联赋值-->
            <property name="dept" ref="dept"></property>
            <property name="dept.dName" value="技术部"></property>
        </bean>
        <bean id="dept" class="com.zhiyou100.bean.Dept">
        </bean>
        

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

    1. 注入数组,list集合,map集合,set集合,对象集合,类型属性

      public class Stu {
          //1. 数组属性
          private String[] course;
          //2. list集合类型属性
          private List<String> list;
          //3. map集合类型属性
          private Map<String, String> map;
          //4. set集合类型属性
          private Set<String> set;
          //5. 对象集合
          private List<Course> courses;
          //省略set方法toString方法
      }
      
      <bean id="stu" class="com.zhiyou100.collectionType.Stu">
              <!--数组-->
              <property name="course">
                  <!--list和array都是可以的-->
                  <array>
                      <value>语文</value>
                      <value>数学</value>
                      <value>英语</value>
                  </array>
              </property>
              <!--list集合-->
              <property name="list">
                  <list>
                      <value>张三</value>
                      <value>李四</value>
                      <value>王五</value>
                  </list>
              </property>
              <!--map集合-->
              <property name="map">
                  <map>
                      <entry key="java" value="java"></entry>
                      <entry key="php" value="php"></entry>
                  </map>
              </property>
              <!--set集合-->
              <property name="set">
                  <set>
                      <value>北京</value>
                      <value>南京</value>
                  </set>
              </property>
              <!--在集合里面设置对象类型值-->
              <property name="courses">
                  <list>
                      <ref bean="course1"></ref>
                      <ref bean="course2"></ref>
                  </list>
              </property>
          </bean>
      	<!--创建对象给集合对象赋值-->
          <bean id="course1" class="com.zhiyou100.bean.Course">
              <property name="cName" value="孙子兵法"></property>
          </bean>
          <bean id="course2" class="com.zhiyou100.bean.Course">
              <property name="cName" value="中华小子"></property>
          </bean>
      

      测试省略。

    2. 把集合注入部分提取出来

      public class Book {
          private List<String> list;
          //省略set,toString方法	
      }
      
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"      
             xmlns:util="http://www.springframework.org/schema/util"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd   
                  http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
          <!--1. 在spring配置文件中引入名称空间util
          把 schemaLocation后面的地址赋值一份beans换成util加在后面-->
      
          <!-- 2. 使用util标签完成list集合注入提取。-->
          <!--2.1 提取list集合类型属性注入-->
          <util:list id="bookList">
              <!--如果是对象就用 ref标签bean属性-->
              <value>易筋经</value>
              <value>九阳神功</value>
              <value>八卦神功</value>
          </util:list>
          <!--2.2 提取list集合类型属性注入使用-->
          <bean id="book" class="com.zhiyou100.bean.Book">
              <property name="list" ref="bookList"/>
          </bean>
      </beans>
      

    IOC操作Bean管理(FactoryBean)

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

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

      之前写的就是普通bean。

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

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

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

        public class MyBean implements FactoryBean<Course> {
            //定义返回bean对象
            @Override
            public Course getObject() throws Exception {
                Course course = new Course();
                course.setcName("NIKE");
                return course;
            }
            @Override
            public Class<?> getObjectType() {return null;}
            @Override
            public boolean isSingleton() {return false;}
        }
        
        <bean id="myBean" class="com.zhiyou100.factoryBean.MyBean">
        
        @Test
        public void MyBeanTest() {
            ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
            Course course = context.getBean("myBean", Course.class);
            System.out.println(course);//Course{cName='NIKE'}
        }
        

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

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

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

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

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

      • scope属性值

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

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

      • singleton和prototype区别

      1. singleton单实例,prototype多实例。

      2. 设置scope 值是 singleton的时候,加载spring配置文件的时候就会创建单例对象。

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

      • request(一次请求):

      • session(一次会话):

    IOC操作Bean管理(声明周期基于注解)

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

    2. bean声明周期:

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

      public class Orders {
          private String oName;
          public void setoName(String oName) {this.oName = oName;System.out.println("第二分 调用set方法设置值"); }
          public Orders() {System.out.println("第一步 执行无参构造创建bean实例");}
          //创建执行初始化的方法 需要在xml配置
          public void initMethod() {System.out.println("第三步 执行初始化的方法");}
          //创建执行销毁的方法
          public void destroy() {System.out.println("第五步 执行销毁的方法");}
      }
      
      <!--init-method=类中方法名字 就是执行初始化的配置-->
      <!--destroy-method=类之方法名 就是执行销毁的配置-->
      <bean id="orders" class="com.zhiyou100.bean.Orders" init-method="initMethod" destroy-method="destroy">
          <property name="oName" value="摩托"/>
      </bean>
      
      @Test
      public void OrderTest() {
          BeanFactory context = new ClassPathXmlApplicationContext("bean4.xml");
          Orders orders = context.getBean("orders", Orders.class);
          System.out.println("第四步 获取创建bean实例对象");
          System.out.println(orders);
          //销毁需要手动销毁bean实例
          //close()方法是子类的方法,父类没有所有需要使用强转来使用
          ((ClassPathXmlApplicationContext)context).close();
         	   /*
              * 第一步 执行无参构造创建bean实例
              * 第二分 调用set方法设置值
              * 第三步 执行初始化的方法
              * 第四步 获取创建bean实例对象
              * com.zhiyou100.bean.Orders@396e2f39
              * 第五步 执行销毁的方法
              * */
      }
      
    4. bean的后置处理器,bean的声明周期有七步操作

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

      1. 创建类,实现类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;
            }
        }
        
        <!--init-method=类中方法名字 就是执行初始化的配置-->
        <!--destroy-method=类之方法名 就是执行销毁的配置-->
        <bean id="orders" class="com.zhiyou100.bean.Orders" init-method="initMethod"
        destroy-method="destroy">
        <property name="oName" value="摩托"/>
        </bean>
        
        <!--配置后置处理器-->
        <!--过程:当你加载配置文件的时候,会把配置文件中的对象创建,并且把
          后置处理器创建(当一个类作为BeanPostProcessor的实现类时就把它作为后置处理器执行)
          ,而后置处理器会对你当前配置文件的所有bean都添加后置处理器的处理
        -->
        <bean id="myBeanPost" class="com.zhiyou100.bean.MyBeanPost"></bean>
        
        @Test
        public void OrderTest() {
          ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
            Orders orders = context.getBean("orders", Orders.class);
            System.out.println("第四步 获取创建bean实例对象");
            System.out.println(orders);
            //销毁需要手动销毁bean实例
            //close()方法是子类的方法,父类没有所有需要使用强转来使用
            context.close();
           /* 第一步 执行无参构造创建bean实例
            * 第二分 调用set方法设置值
            * 在初始化之前执行的方法
            * 第三步 执行初始化的方法
            * 在初始化之后执行的方法
            * 第四步 获取创建bean实例对象
            * com.zhiyou100.bean.Orders@365185bd
            * 第五步 执行销毁的方法
            * */
        }
        

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

    1. 什么时自动装配

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

    2. 演示自动装配

      public class Dept {
          //省略toStirng
      }
      public class Emp {
          private Dept dept;
          //省略set,get,toString
      }
      
      <!--实现自动装配
          bean标签autowire,配置自动装配
          autowire属性常用两个值:
              byName根据属性名称注入,注入值bean的id值和类属性名称一样
              byType根据属性类型注入,相同类型的bean不能在xml中定义多个
      -->
      <bean id="emp" class="com.zhiyou100.autowire.Emp" autowire="byName">
          <!--<property name="dept" ref="dpt"/>-->
      </bean>
      <bean id="dept" class="com.zhiyou100.autowire.Dept"></bean>
      

    IOC操作Bean管理(引入外部属性文件 基于xml)

    1. 直接配置数据库信息

      1. 配置德鲁伊连接池

      2. 引入德鲁伊连接依赖jar

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

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

        url=jdbc:mysql:///test
        username=root
        password=root
        driverClassName=com.mysql.jdbc.Driver
        
      2. 把外部properties属性文件引入到spring配置文件中,先引入名称空间context

        <?xml version="1.0" encoding="UTF-8"?>
        <beans xmlns="http://www.springframework.org/schema/beans"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:context="http://www.springframework.org/schema/context"
               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">
        	<!--1. 引入名称空间↑ context-->
            <!--2. 引入外部属性文件-->
            <context:property-placeholder location="classpath:druid.properties"/>
            <!--3. 配置连接池-->
            <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
                <property name="driverClassName" value="${driverClassName}"/>
                <!--4. 这里value=${属性文件中的key}-->
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </bean>
        </beans>
        

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

    1. 什么是注解

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

      • @Component
      • @Service
      • @Controller
      • @Repository >用在持久层的接口上
      • 上面四个注解功能是一样的,都可以用来创建bean实例。
    3. 基于注解方式实现对象创建

      • 第一步 引入依赖:spring-aop-5.3.4.jar 架包。

      • 第二部 开启组件扫描

      • 创建类,在类上面添加对象注解

        <?xml version="1.0" encoding="UTF-8"?>
        <beans xmlns="http://www.springframework.org/schema/beans"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               
               xmlns:context="http://www.springframework.org/schema/context"
               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">
            <!--引入名称空间context-->
            <!--开启组件扫描
                1. 入库给扫描多个包,多个包使用逗号隔开
                2. 扫描包上层目录
            -->
            <context:component-scan base-package="com.zhiyou100"/>
        </beans>
        
          //在创建对象注解里面value属性值可以省略不写
          //默认就是类名称,首字母小写
          //UserService --> userService
          @Component(value = "userService") //<bean id="userService" class=".."/ > value和bean里面的id是等价的
          //别的创建对象注解也可以实现效果
          public class UserService {
              public void add() {System.out.println("service add ...");}
          }
          //测试
          @Test
          public void test1() {
              //加载配置文件					类路径应用程序上下文
              ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
              UserService user = context.getBean("userService", UserService.class);
              System.out.println(user);
              user.add();
              /*
                  * 第一部分加载配置文件,开启资源扫描,
                  * 在xml找组件扫描中的路径,如果类中有相关的注解
                  * 根据相关的注解,就把对象创建
                  * */
          }
        
    4. 开启组件扫描细节配置

      <!--示例1
          标签context:component-scan,属性:use-default-filters="false" 表示现在不适用默认filter,自己配置filter
          context:include-filter标签,设置扫描包含那些内容
      -->
      <context:component-scan base-package="com.zhiyou100" use-default-filters="false">
          <!--这句话的意思是只在com.zhiyou100下的包中只扫描Controller注解的类,别的注解别的类不扫描-->
          <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
      </context:component-scan>
      
      <!--示例2
          context:exclude-filter标签,设置扫描不包含那些内容
      -->
      <context:component-scan base-package="com.zhiyou100">
          <!--这句话的意思是不扫描注解是Controller-->
          <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
      </context:component-scan>
      
    5. 基于注解方式实现属性注入

      • @Autowired:根据类型自动注入进行自动装配

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

        第二步把 service 和 dao包中对象,在 service 类添加到dao类型属性,在属性上面使用注解

        public interface UserDao {public void add();}
        @Repository //创建对象 可以根据类型或者名称进行注入
        public class UserDaoImp implements UserDao {
            @Override
            public void add() {System.out.println("dao ad......");}
        }
        @Service //创建对象
        public class UserService {
            //定义dao类型属性
            //不需要添加set方法,因为在里面已经包我们封装了
            //添加注入属性注解
            @Autowired //根据类型注入
            private UserDao userDao;
            public void add() { System.out.println("service add ...");userDao.add();}
        }
        
        <?xml version="1.0" encoding="UTF-8"?>
        <beans xmlns="http://www.springframework.org/schema/beans"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:context="http://www.springframework.org/schema/context"
               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">
            <!--1. 引入名称空间context-->
            <!--2. 开启组件扫描
                2.1. 入库给扫描多个包,多个包使用逗号隔开
                2.2. 扫描包上层目录
            -->
            <context:component-scan base-package="com.zhiyou100"/>
        </beans>
        
      • @Qualifier:根据属性名称进行注入

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

        @Repository(value = "userDaoImpl1") //可以根据类型或者名称进行注入
        public class UserDaoImpl implements UserDao {
            @Override
            public void add() {System.out.println("dao ad......");}
        }
        @Service//创建对象
        public class UserService {
            //定义dao类型属性
            //不需要添加set方法,因为在里面已经包我们封装了
            //添加注入属性注解
            @Autowired //根据类型注入
            //@Autowired是为了找类型,但是可以有两种或多中相同类型的bean对象,所以需要搭配@Qualifier
            @Qualifier(value = "userDaoImpl1")//根据名称进行注入
            private UserDao userDao;
            public void add() {System.out.println("service add ..."); userDao.add();}
        }
        
      • @Resource:可以根据类型注入,可以根据名称注入

        所在:javax.annotation.Resource ,Java扩展包中的注解,jdk11中取消javax扩展。spring官方推荐使用前两个,因为是spirng的

        @Service
        public class UserService {
            //@Resource//根据类型进行注入
            @Resource(name = "userDaoImpl1")//根据名称进行注入
            private UserDao userDao;
            public void add() {System.out.println("service add ...");userDao.add();}
        }
        
      • @Value:注入普通类型属性

        @Value(value = "李白 ")//这就可以将name设置为abc
        private String name;
        
    6. 完全注解开发

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

        @Configuration//该注解表示当前类作为配置类,代替xml配置文件
        //该注解表示开去组件扫描,属性basePackages是一个数组形式。改注解和xml中开启组件扫描是等价的
        @ComponentScan(basePackages = {"com.zhiyou100"})
        public class SpringConfig {
        }
        
      2. 编写测试类

        @Test
        public void test2() {
            //创建注释配置应用程序上下文, 加载配置类,使用配置类中的内容		
            ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
            //获取配置创建对象
            UserService userService = context.getBean("userService", UserService.class);//此类在上面
            System.out.println(userService);
            userService.add();
        }
        

    3,Aop

    什么是AOP

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

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

    • 使用登陆例子

      image

    AOP(底层原理)

    1. AOP底层使用动态代理

      • 有两种情况的动态代理

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

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

        image

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

        • 创建子类的代理对象,增强类的方法

        image

    AOP(jdk动态代理)

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

      java.langObject

      ​ java.lang.reflect.Proxy

      (1)调用newProxyInstance方法

      static Object newProxyInstance​(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 返回指定接口的代理实例,该代理实例将方法调用分派给指定的调用处理程序。 
      //参数:
      loader - 类加载器来定义代理类 
      interfaces - 增强方法所在的类,这个实现的接口,支持多个接口
      h - 实现这个接口InvocationHandler,创建代理对象,写增强的方法
      
    2. 代码:

      1. 创建接口,定义方法

      2. 创建接口实现类,实现方法

      3. 使用Proxy类创建接口代理对象

        public interface UserDao {
            public int add(int a, int b);
            String update(String id);
        }
        public class UserDaoImpl implements UserDao {
            @Override
            public int add(int a, int b) {return a + b;}
            @Override
            public String update(String id) {return id; }
        }
        public class JDKProxy {
            public static void main(String[] args) {
                //创建接口实现类代理对象
                Class[] interfaces = {UserDao.class};
                /*Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        return null;
                    }
                }); */
                UserDaoProxy proxy = new UserDaoProxy(new UserDaoImpl());
                UserDao dao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, proxy);
                System.out.println(dao.add(12, 11));
            }
        }
        
        //创建代理对象代码
        class UserDaoProxy implements InvocationHandler {
            //1. 把创建的是谁的代理对象,把谁传递过来
            private Object o;
            //通过有参构造传递
            public UserDaoProxy(Object o) {this.o = o;}
            //增强的逻辑
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //在方法之前
                System.out.println("方法之前执行" + method.getName() + ",传递的参数" + Arrays.toString(args));
                //被增强的方法执行
                Object invoke = method.invoke(o, args);
                //方法之后
                System.out.println("invoke = " + invoke);
                System.out.println("方法之后执行" + o);
                return invoke;
            }
        }
        

    AOP(术语)

    1. 连接点

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

    2. 切入点

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

    3. 通知(增强)

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

      • 通知有多种类型

        前置通知: @Before

        后置通知(返回值通知): @AfterReturning

        环绕(在被增强类的增强方法之前之后进行通知): @Around

        异常: @AfterThrowing

        最终(类似finally): @After

      • 特点:

        前置,当有异常就不执行

        最终,有没有异常都执行

    4. 切面

      时动作,把通知应用到切入点过程。


    AOP操作(准备)

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

      • 什么是AspectJ?

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

    2. 基于AspectJ实现AOP操作

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

      在原来的jar包上在引入一个spring-aspects-5.3.4.jar / spring-aspects-5.2.6.RELEASE.jar

      和aop其他架包com.springsource.net.sf.cglib-2.2.0.jar,com.springsource.org.aopalliance-1.0.0.jar,com.springsource.org.aspectj.weaver-1.6.8.release.jar

      image

    4. 切入点表达式

      • 作用:对符合切入点表达式的类,会自动生成代理对象。

      • 语法:该语句在spring框架下载的解压包-》docs-》reference-》core.html-》5.4.3 Declaring a Pointcut-》Examples

                    访问修饰符        返回值类型(必填)     包和类                  方法(必填)			  异常
        execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?namepattern(param-pattern) throws-pattern?)
        execution(<修饰符模式>? <返回类型模式> <方法名模式>(<参数模式>) <异常模式>?)
        
      • 表达式通配符:

        *:匹配所有字符

        ..:一般用于匹配多个包,多个参数

        +:表示类及其子类

      • 支持逻辑运算符:&&,||,!

      • 注意:

        除了返回类型模式,方法名模式和参数模式外,其他项都是可选的

      • 示例:

        1.最全的写法
        拦截返回void,指定类的指定方法,参数必须有两个:int、String
        execution(public void com.sunny.service.Impl.AccountServiceImpl.save(int,java.lang.String))
        2.省略访问修饰符,返回值任意的指定类的save方法,无参数
        execution(*  com.sunny.service.Impl.AccountServiceImpl.save())
        3.拦截com包下所有的类、以及其子包下所有的类的save()方法
        execution(void  com..*.save())  包名与类名或方法名称都可以使用*
        4.拦截save()方法/拦截所有方法
        execution(* save())  //拦截save()
        execution(* *())  //拦截所有方法
        5.不拦截save()方法
        !execution(* save())
          not execution(* save())  注意not前面要有空格
        6.拦截save()方法或者update()方法
        execution(* save()) || execution(* update()))
        execution(* save()) or execution(* update()))
        7.拦截所有方法,参数任意,但必须有参数
        execution(* *(*))
        8.拦截所有方法,参数任意,参数可有可无
        execution(* *(..))
        9.对IOC容器中以Service结尾的类,生成代理对象
        bean(*Service)
        10.最常用
        execution(* com.sunny..*ServiceImpl.*(..))
        表示com.sunny包及其子包下所有的以ServiceImpl结尾的类生成代理对象
        

    AOP操作(AspectJ注解)

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

    2. 创建增强类(编写增强逻辑)

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

    1. 进行通知的配置

      • 在spring配置文件中,开启注解扫描(配置文件或者配置类)
      • 使用注解创建UserUserProxy对象
      • 在增强类上面添加注解@Aspect 生成代理对象
      • 在spring配置文件中开启生成代理对象
      • 配置不同类型的通知
        1. 在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置
    2. 代码

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:context="http://www.springframework.org/schema/context"
             xmlns:aop="http://www.springframework.org/schema/aop"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
              http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
              http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
          <!--1. 使用名称空间context,aop ↑-->
          <!--2. 开启组件扫描-->
          <context:component-scan base-package="com.zhiyou100.aopanno"></context:component-scan>
          <!--3. 开启Aspect生成代理对象。在类中找@Aspect注解,如果有就把这个对象生成代理对象-->
          <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
      </beans>
      
      //被增强的类
      @Component //创建对象
      public class User {
          //int i=10/0;//创建异常
          public void add() {System.out.println("add......");}
      }
      //增强的类
      @Component //创建对象
      @Aspect //生成代理对象
      public class UserProxy {
          //让这个方法作为前置通知
          //@Before 注解表示作为前置通知,value可以省略 里面使用切入点表达式
          @Before(value = "execution(* com.zhiyou100.aopanno.User.add(..))")
          public void before() {System.out.println("before......");}
      }
      //测试
          @Test
          public void test1() {
              //加载配置文件,创建类路径Xml应用程序上下文
              ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
              //根据该类.getBean()方法得到该对象
              User user = context.getBean("user", User.class);//前面创建对象注解value可以不写,默认是类首字母小写
              //调用方法
              System.out.println(user);
              user.add();
          }
      /*before......
      add......*/
      

      在原来的增强的类中添加别的通知类型

      //增强的类
      @Component //创建对象
      @Aspect //生成代理对象
      public class UserProxy {
          //让这个方法作为前置通知
          //@Before 注解表示作为前置通知,value可以省略
          @Before("execution(* com.zhiyou100.aopanno.User.add(..))")
          public void before() {
              System.out.println("before......");
          }
          //最终通知
          @After("execution(* com.zhiyou100.aopanno.User.add(..))")
          public void after() {
              System.out.println("after......");
          }
          //后置通知 或者 在方法返回值之后执行
          @AfterReturning("execution(* com.zhiyou100.aopanno.User.add(..))")
          public void afterReturning() {
              System.out.println("AfterReturning......");
          }
          //异常通知
          @AfterThrowing("execution(* com.zhiyou100.aopanno.User.add(..))")
          public void afterThrowing() {
              System.out.println("AfterThrowing......");
          }
          //环绕通知
          @Around("execution(* com.zhiyou100.aopanno.User.add(..))")
          public void around(ProceedingJoinPoint point) throws Throwable {//通过参数可以让被增强方法在里面执行
              System.out.println("环绕之前。。。");
              //被增强的方法执行
              point.proceed();
              System.out.println("环绕之后");
          }
      }
              //没有异常执行
              环绕之前。。。
              before......
              add......
              AfterReturning......
              after......
              环绕之后
              //有异常执行
              环绕之前。。。
              before......
              AfterThrowing......
              after......
      
    3. 两个细节问题

      • 公共切入点抽取

        在增强的类里面

        //相同切入点抽取
        @Pointcut(value = "execution(* com.zhiyou100.aopanno.User.add(..))") //切入点的注解
        public void pointDemo() {}
        //@Before 注解表示作为前置通知,value可以省略
        @Before("pointDemo()")//通过方法在里面调用,方法上面定义了切入点表达式
        public void before() {
            System.out.println("before......");
        }
        
      • 有多个增强类对同一个方法进行增强,设置增强类优先级

      1. 在增强类上面添加注解 @Order(数字类型值) ,数字类型值越小优先级越高。
      @Component //创建对象
      @Aspect   //生成代理对象
      @Order(-10) //设置优先级
      public class PersonProxy {
          //让这个方法作为前置通知
          //@Before 注解表示作为前置通知,value可以省略
          @Before("execution(* com.zhiyou100.aopanno.User.add(..))")
          public void before() {System.out.println("1 Person before......");}
      }
      
    4. 完全注解开发

      • 创建配置类,不需要创建xml配置文件按

        @Configuration //表示当前类是配置类
        @ComponentScan(basePackages = {"com.zhiyou100"}) //开启组件扫描
        @EnableAspectJAutoProxy(proxyTargetClass = true)//开启Aspect生成代理对象。在类中找@Aspect注解,如果有就把这个对象生成代理对象
        public class ConfigAop {
        }
        

    AOP(AspectJ配置文件)

    1. 创建两个类,增强类和被增强类,创建方法
    2. 在spring配置文件中使用名称空间,创建两个类
    3. 在spring配置文件中配置切入点
    //被增强的类
    public class Book {
        public void buy(){System.out.println("buy ...");}
    }
    //增强的类
    public class BookProxy {
        //前置通知
        public void before() {System.out.println("before ...");}
    }
    
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!--使用名称空间aop ↑-->
        <!--创建对象-->
        <bean id="book" class="com.zhiyou100.aopXml.Book"></bean>
        <bean id="bookProxy" class="com.zhiyou100.aopXml.BookProxy"></bean>
        <!--配置切入点:aop增强-->
        <aop:config>
            <!--切入点-->
            <aop:pointcut id="p" expression="execution(* com.zhiyou100.aopXml.Book.buy(..))"/>
            <!--配置切面:把增强或通知应用到切入点过程-->
            <aop:aspect ref="bookProxy">
                <!--增强作用在具体的方法上-->
                <!--aop:通知类型 method="增强方法" pointcut-ref="应用的切入点"-->
                <aop:before method="before" pointcut-ref="p"></aop:before><!--这句话的意思是把before方法应用在buy上面-->
            </aop:aspect>
        </aop:config>
    </beans>
    
    @Test
    public void test2() {
        //加载配置文件 创建类路径Xml应用程序上下文
        ApplicationContext context =
                new ClassPathXmlApplicationContext("bean.xml");
        Book book = context.getBean("book", Book.class);
        book.buy();
    } 
    

    4,JdbcTemplate

    4.1 概念和准备

    1. 什么是JdbcTemplate?

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

    2. 准备工作

      • 引入相关的jar包

        Spring框架里的 spring-jdbc-5.3.4.jar(对jdbc进行了封装),spring-tx-5.3.4.jar(针对事务相关操作),spring-orm-5.3.4.jar(整合其他框架需要的)

        image

      • 在spring配置文件配置数据库连接池

      • 配置JdbcTemplate 对象,注入 DataSource

      • 创建service类注入dao对象,创建dao类,在dao注入JdbcTemplate对象

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:context="http://www.springframework.org/schema/context"
             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">
      
          <!--使用名称空间 context-->
          <!--开启组件扫描-->
          <context:component-scan base-package="com.dalao"/>
      
          <!--使用德鲁伊配置连接池-->
          <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
              <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
              <property name="url" value="jdbc:mysql:///test"/>
              <property name="username" value="root"/>
              <property name="password" value="root"/>
          </bean>
      
          <!--创建JdbcTemplate对象注入 DataSource-->
          <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
              <!--注入数据源,父类的set方法注入-->
              <!--property name=属性 ref=数据源信息-->
              <property name="dataSource" ref="dataSource"></property>
          </bean>
      </beans>
      
      public interface BookDao {}
      
      @Repository //创建对象
      public class BookDaoImp implements BookDao {
          //注入JdbcTemplate
          @Autowired //根据类型自动注入进行自动装配
          private JdbcTemplate jdbcTemplate;
          public JdbcTemplate getJdbcTemplate() {return jdbcTemplate;}
          public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}
      }
      
      @Service //创建对象
      public class BookService {
          //注入dao
          @Autowired //根据类型自动注入进行自动装配
          private BookDao bookDao;
          public BookDao getBookDao() {return bookDao;}
          public void setBookDao(BookDao bookDao) {this.bookDao = bookDao;}
      }
      

    4.2 JdbcTemplate操作数据库

    添加

    1. 对应数据库创建实体类

    2. 编写service和dao

      • 编写dao进行数据库添加操作

      • 调用JdbcTemplate对象里面update方法实现添加操作

        int update(String sql, @Nullable Object... args)
        参数:
            sql:sql语句
            args:可变参数,设置sql语句值
        
    //实体类
    public class Book {
        private Integer id;
        private String userName;
        private String ustatus;
        //省略set,get,toString,无参,有参方法
    }	
    //dao层实现类代码实现
    @Repository //创建对象
    public class BookDaoImp implements BookDao {
        //注入JdbcTemplate
        @Autowired //根据类型自动注入进行自动装配
        private JdbcTemplate jdbcTemplate;
        @Override//重写接口方法
        public void add(Book book) {
            String sql = "insert into t_book (username,ustatus) values(?,?) ";//id自增
            Object[] args = {book.getUserName(), book.getUstatus()};
            int update = jdbcTemplate.update(sql, args);//返回受影响行数
        }
    }
    //方法测试
        @org.junit.Test
        public void testAdd() {
            //创建对象
            ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
            BookService book = context.getBean("bookService", BookService.class);
            book.addUser(new Book(100, "赵六", "吃饭"));
        }
    

    修改和删除

     @Override
    public void delete(int id) {
        String sql = " delete from t_book where user_id =?";
        int update = jdbcTemplate.update(sql, id);
    }
    
    @Override
    public void modify(Book book) {
        String sql = "update t_book set username=?,ustatus=? where user_id=?";
        Object[] args = {book.getUserName(), book.getUstatus(), book.getId()};
        int update = jdbcTemplate.update(sql, args);
    }
    

    查询返回某个值

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

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

      <T> T queryForObject(String sql, Class<T> requiredType)
      参数:
          sql:sql语句
          requiredType:返回类型Class
      
    @Override
    public Integer selectCount() {
        String sql = "select count(*) from t_book";
        Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
        return count;
    }
    

    查询返回一个对象

    1. 场景:查询图书详情

    2. JdbcTemplate实现查询返回对象

      <T> T queryForObject(String sql, RowMapper<T> rowMapper, @Nullable Object... args)
      参数
          sql:sql语句
          rowMapper:RowMapper是接口,针对返回不同类型数据,使用这个接口里面实现类完成数据封装
          args:sql语句值
      
    @Override
    public Book queryOne(Integer id) {
        String sql = "select user_id as id,username as userName,ustatus from t_book where user_id=? ";
        Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), id);
        return book;
    }
    

    查询返回多个对象

    1. 场景:查询图书列表分页。。。

    2. 调用JdbcTemplate方法实现查询返回集合

      <T> List<T> query(String sql, RowMapper<T> rowMapper)
      参数:
          sql:sql语句
          rowMapper:RowMapper是接口,针对返回不同类型数据,使用这个接口里面实现类完成数据封装
          args:sql语句值
      
    @Override
    public List<Book> queryAll() {
        String sql = "select user_id id,username as userName,ustatus from t_book";
        List<Book> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Book.class));
        return list;
    }
    

    批量操作

    1. 批量插座:操作表里面多条记录

    2. JdbcTemplate实现批量添加操作

      int[] batchUpdate(String sql, List<Object[]> batchArgs)
      参数:
          sql:sql语句
          batchArgs:list集合,添加多条记录数据
      
    //dao层
    @Override
    public void batchAdd(List<Object[]> batchArgs) {
        String sql = "insert into t_book (username,ustatus) values(?,?) ";
        int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);//把list集合遍历,把每个数组值执行sql语句进行添加
        System.out.println(Arrays.toString(ints));
    }
    //测试
    @org.junit.Test
        public void testBatchAdd() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        BookService books = context.getBean("bookService", BookService.class);
    
        ArrayList<Object[]> batchArgs = new ArrayList<>();
        Object[] o1 = {"小华", "b"};
        Object[] o2 = {"小明", "a"};
        batchArgs.add(o1);
        batchArgs.add(o2);
    
        books.batchAdd(batchArgs);//[1, 1]
    }
    

    批量修改

    //dao层
    @Override
    public void batchUpdate(List<Object[]> batchArgs) {
        String sql = "update t_book set username=?,ustatus=? where user_id=?";
        int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
        System.out.println(Arrays.toString(ints));
    }
    //测试
    @org.junit.Test
        public void testBatchUpdate() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        BookService books = context.getBean("bookService", BookService.class);
        ArrayList<Object[]> list = new ArrayList<>();
        Object[] o1 = {"理想", "吃饭", 2};
        Object[] o2 = {"辉煌", "打豆豆", 3};
        list.add(o1);
        list.add(o2);
        books.batchUpdate(list);
    }
    

    批量删除

    //dao层
    @Override
    public void batchDelete(List<Object[]> batchArgs) {
        String sql = " delete from t_book where user_id =?";
        int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
        System.out.println(Arrays.toString(ints));
    }
    //测试
    @org.junit.Test
        public void testBatchDelete() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        BookService books = context.getBean("bookService", BookService.class);
        ArrayList<Object[]> list = new ArrayList<>();
        Object[] o1 = {2};
        Object[] o2 = {4};
        list.add(o1);
        list.add(o2);
        books.batchDelete(list);
    }
    

    5,事务管理

    事务介绍

    1. 什么是事务?
      • 事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败。
      • 典型场景:银行转账
    2. 事务四个特性(ACID)
      1. 原子性:
      2. 一致性:
      3. 隔离性:
      4. 持久性:

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

    • Service:业务操作

      创建转账的方法

      ​ 调用dao两个的方法

    • Dao:数据库操作不写业务

      创建两个方法

      ​ 少钱的方法

      ​ 多钱的方法

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

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

      • service注入dao,在dao注入JdbcTemplate,在JdbcTemplate注入DataSource
    3. 在dao创建两个方法:多钱和少钱的方法,在service创建方法(转账的方法)

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

      • 上面的问题需要使用--事务解决

      • 事务操作过程

      • //        try {
        //        	第一步 开启事务
        //        	第二部 进行业务操作
        //        	第三步 没有异常提交
        //        } catch (Exception e) {
        //            第四步 出现异常回滚
        //            e.printStackTrace();
        //        } finally {
        //        }
        

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

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

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

      有两种方式:编程事务管理try-case-finally,声明式事务管理(使用)

    3. 声明事务管理

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

    5. Spring事务管理api

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

        image

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

    1. 在Spring配置文件配置事务管理器 DataSourceTransactionManager,注入属性dataSource

    2. 在Spring配置文件,开启事务注解

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

      • 开启事务注解

      • 在service类上面(或者 service类里面方法上面)添加事务注解

        1. @Transactional,这个注解添加到类上面,也可以添加方法上面
        2. 如果注解在类上面,这个类里面所有的方法都添加事务
        3. 如果注解在方法上面,为这个方法添加事务
        <?xml version="1.0" encoding="UTF-8"?>
        <beans xmlns="http://www.springframework.org/schema/beans"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:context="http://www.springframework.org/schema/context"
               xmlns:tx="http://www.springframework.org/schema/tx"
               xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
        
            <!--使用名称空间 context,tx-->
            <!--开启组件扫描-->
            <context:component-scan base-package="com.dalao"/>
        
            <!--使用德鲁伊配置连接池-->
            <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
                <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///test"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </bean>
        
            <!--创建JdbcTemplate对象注入 DataSource-->
            <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
                <!--注入数据源,父类的set方法注入-->
                <!--property name=属性 ref=数据源信息-->
                <property name="dataSource" ref="dataSource"></property>
            </bean>
        
            <!--创建事务管理-->
            <bean id="transaction" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
                <!--注入数据源dataSource  使用set方法-->
                <property name="dataSource" ref="dataSource"/>
            </bean>
             <!--开启事务注解 transaction-manager=指定的事务管理 -->
            <tx:annotation-driven transaction-manager="transaction"></tx:annotation-driven>
        </beans>
        
        public interface AccountDao {
            void modifyAccount(Account account);
        }
        @Repository//创建对象
        public class AccountDaoImpl implements AccountDao {
            @Autowired //根据类型自动装配
            private JdbcTemplate jdbcTemplate;
            @Override
            public void modifyAccount(Account account) {
                String sql = "update t_account set username=?,money=? where id=?";
                int update = jdbcTemplate.update(sql, account.getUsername(), account.getMoney(), account.getId());
                System.out.println(update);
            }
        }
        @Service //创建对象
        @Transactional //添加事务注解
        public class AccountService {
            //注入dao
            @Autowired //根据类型自动装配
            private AccountDao accountDao;
            public void modifyAccount() {
        //        try {
        //        第一步 开启事务
        //        第二部 进行业务操作
                //转账
                accountDao.modifyAccount(new Account(1, "lucy", 800));
                //模拟异常
                int a = 1 / 0;
                accountDao.modifyAccount(new Account(2, "mary", 1200));
        //            第三步 没有异常提交
        //        } catch (Exception e) {
        //            第四步 出现异常回滚
        //            e.printStackTrace();
        //        } finally {
        //        }
            }
        }
        //测试
            @org.junit.Test
            public void test1() {
                //加载spring配置文件
                ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
                //获取配置创建对象 默认v是类名小写
                AccountService service = context.getBean("accountService", AccountService.class);
                //service.modifyAccount();
                service.queryAll().forEach(System.out::println);
            }
        

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

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

      image

    2. propagation:事务传播行为

      • 多事务方法直接进行调用,这个过程中事务是如何进行管理的

        image

        image

        //添加事务注解 可以在类上,或者类中方法外
        @Transactional(propagation = Propagation.REQUIRED) //默认不写就是 REQUIRED
        
    3. isolation:事务隔离级别

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

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

      3. 脏读:一个未提交事务读取到另一个未提交事务的数据

        image

      4. 不可重复读:一个未提交事务读取到了另一个提交事务修改的数据

        image

      5. 幻读:一个未提交事务读取到了另一个提交事务添加的数据

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

        image

        @Service //创建对象
        //添加事务注解 REPEATABLE_READ -->mysql默认隔离级别
        @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
        
    4. timeout:超时时间

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

      1. 读:查询操作。写:添加修改删除操作
      2. readOnly默认值false,表示可以查询,可以添加修改删除操作
      3. 设置readOnly值为true,表示只能查询
    6. rollbackFor:回滚

      • 设置出现那些异常进行事务回滚
    7. noRollbackFor:不回滚

      • 设置出现那些异常不进行事务回滚

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

    1. 在spring配置文件中配置

      第一步配置事务管理器

      第二部配置通知

      第三步配置切面和切入点

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:context="http://www.springframework.org/schema/context"
             xmlns:tx="http://www.springframework.org/schema/tx"
             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/tx http://www.springframework.org/schema/tx/spring-tx.xsd
      http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
      
          <!--使用名称空间 context,tx,aop-->
          <!--开启组件扫描-->
          <context:component-scan base-package="com.dalao"/>
      
          <!--使用德鲁伊配置连接池-->
          <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
              <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
              <property name="url" value="jdbc:mysql:///test"/>
              <property name="username" value="root"/>
              <property name="password" value="root"/>
          </bean>
      
          <!--创建JdbcTemplate对象注入DataSource(连接池配置信息)-->
          <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
              <!--注入数据源,父类的set方法注入-->
              <!--property name=属性 ref=连接池数据源信息-->
              <property name="dataSource" ref="dataSource"/>
          </bean>
      
          <!--1. 创建事务管理器-->
          <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
              <!--注入数据源dataSource  使用set方法-->
              <property name="dataSource" ref="dataSource"/>
          </bean>
          <!--2. 配置(事务)通知-->
          <tx:advice id="txadvice">
              <!--配置事务相关的参数-->
              <tx:attributes>
                  <!--name属性:指定哪种规则的方法上面添加事务。后面可跟其他事务参数
                  第一种写法:方法名字
                  第二部写法:方法名字* (只要是方法名字开头添加事务操作)
                  -->
                  <tx:method name="modifyAccount" propagation="REQUIRED"/>
              </tx:attributes>
          </tx:advice>
          <!--3. 配置切入点和切面-->
          <aop:config>
              <!--配置切入点 使用表达式-->
              <aop:pointcut id="pt" expression="execution(* com.dalao.service.AccountService.*(..))"/>
              <!--配置切面-->
              <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
          </aop:config>
      </beans>
      
      @org.junit.Test
      public void test2() {
          //加载spring配置文件
          ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
          //获取配置创建对象 默认v是类名小写
          AccountService service = context.getBean("accountService", AccountService.class);
          //service.modifyAccount();
          service.queryAll().forEach(System.out::println);
      }
      

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

    1. 创建配置类,使用配置类代替xml配置文件
    @Configuration  //表示当前类式配置类
    @ComponentScan(basePackages = "com.dalao")//开启组件扫描
    @EnableTransactionManagement //开启事务
    public class TxConfig {
        //创建数据库连接池
        @Bean
        public DruidDataSource getDruidDataSource() {
            DruidDataSource source = new DruidDataSource();
            source.setDriverClassName("com.mysql.jdbc.Driver");
            source.setUrl("jdbc:mysql:///test");
            source.setUsername("root");
            source.setPassword("root");
            return source;
        }
        //创建JdbcTemplate对下个你
        @Bean
        public JdbcTemplate getJdbcTemplate(DataSource source) {//ioc容器中已经存在了数据源,根据它的类型找到它的对象
            JdbcTemplate jdbcTemplate = new JdbcTemplate();
            //注入dataSource
            jdbcTemplate.setDataSource(source);
            return jdbcTemplate;
        }
        //创建事务管理器
        @Bean
        public DataSourceTransactionManager getDataSourceTransactionManager(DataSource source) {//从ioc容器中找到,然后传入
            DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
            //注入datasource
            transactionManager.setDataSource(source);
            return transactionManager;
        }
    }
    
        @org.junit.Test
        public void test3() {
            //创建注释配置应用程序上下文
            ApplicationContext context =
                    new AnnotationConfigApplicationContext(TxConfig.class);
            //获取配置创建对象 默认v是类名小写
            AccountService service = context.getBean("accountService", AccountService.class);
    //        service.modifyAccount();
            service.queryAll().forEach(System.out::println);
        }
    

    6,Spring5新特性

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

    2. Spring 5.0框架自带了通用的日志封装。

      • Spring5已经移除了Log4jConfigListener,官方建议使用Log4j2, 如果还想使用Log4j需要spring降到4的版本

      • Spring5框架整合Log4j2(http://logging.apache.org/log4j/2.x/download.html)

        第一步引入jar包

        image

        第二部创建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>
        

        手动输出日志

        class UserLog {
            //都是 org.slf4j 包下的
            private static final Logger LOGGER= LoggerFactory.getLogger(UserLog.class);
            public static void main(String[] args) {
                LOGGER.info("hello 1");
                LOGGER.warn("hello 2"); }
        }
        
    3. spring5支持@Nullable注解

      • @Nullable:可以使用在方法,属性,参数上面,表示方法返回值可以为空,属性值可以为空,参数值可以为空。
    4. spring5支持函数式风格GenericApplicationContext / AnnotationConfigApplicationContext

      @org.junit.Test
      public void test4() {
          //创建GenericApplicationContext 对象
          GenericApplicationContext context = new GenericApplicationContext();
          //调用context的方法对象注册
          context.refresh();//把里面内容清空,然后注册
          //context.registerBean(User.class, () -> new User());
          context.registerBean("user1",User.class, User::new);//简化上面
          //3. 获取在spring注册的对象
          //User user = (User) context.getBean("com.dalao.bean.User");
          User user = (User) context.getBean("user1");
          System.out.println(user);
      }
      
    5. Spring支持JUnit 5's Juptier编程和拓展模块在Spring TestContext框架

      • 整合JUnit4

        第一步 引入Spring相关针对测试依赖:spring-test-5.3.4.jar,JUnit4的jar包

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

        @RunWith(SpringJUnit4ClassRunner.class)//里面设置相关的单元测试版本
        @ContextConfiguration("classpath:bean1.xml")//加载配置文件
        public class TestJ {
            @Autowired //自动注入属性
            private AccountService accountService;
            @Test
            public void test1() {accountService.queryAll();}
        }
        
      • 整合JUnit5 导入junit的jar包

      第一步 导入junit5的jar包

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

      @ExtendWith(SpringExtension.class) //注解引用
      @ContextConfiguration("classpath:bean1.xml")//加载配置文件
      public class TestJ5 {
          @Autowired
          private AccountService accountService;
          @Test
          public void test1() {accountService.queryAll();}
      }
      
      • 使用 @SpringJunitConfig:一个复合注解,来代替上面两个注解完成整合

        @SpringJUnitConfig(locations = "classpath:bean1.xml")//
        public class TestJ5 {
            @Autowired
            private AccountService accountService;
            @Test
            public void test1() {accountService.queryAll();}
        }
        

    Spring5新模块--WebFlux

    1. spring webflux介绍

      • (web --》 WebSocket,webMVC,Web,WebFlux)

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

      • 使用传统web框架,比如SpringMVC,这些基于 Servvlet 容器,Webflux 是一种异步非阻塞的框架,异步非阻塞的框架在Servlet3.1以后才支持,核心是居于 Reactor(响应式编程) 的相关api实现的。

      • 什么是异步非阻塞:针对对象不一样。

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

        阻塞,非阻塞针:针对被调用者。被调用者收到请求之后, 做完请求任务之后才给出反馈就是阻塞;收到请求之后马上给出反馈然后再去做其他事情就是非阻塞。(简单来说阻塞需要等待,非阻塞不需要等待)

      • Webflux特点

        第一非阻塞式:在有限资源下,提高系统吞吐量和伸缩性,以JReactor为基础实现响应式编程。

        第二函数式编程:Spring5框架基于java8,Webflux使用Java8函数式编程方式实现路由请求。

      • 比较SpringMVC

        image

        第一,两个框架都可以使用注解方式,都运行在Tomcat等容器中。

        第二,SpringMVC采用命令式编程,Webflux采用异步相应式编程。

        一般项目使用mvc就可以,如果使用远程服务调用不妨使用webflux。

    2. 响应式编程

      • 什么是响应式编程:

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

      • Java8及其之前的版本。提供了观察者模式两个类 ObserverObservable,在java9及之后的版本取代了这两个类(Flow )。

        //创建spring initializer项目
        public class ObservableDemo extends Observable {
            public static void main(String[] args) {
                //创建对象
                ObservableDemo demo = new ObservableDemo();
                //添加观察者模式
                demo.addObserver((o, arg) -> {
                    System.out.println("发生了变化");
                });
                demo.addObserver((o, arg) -> {
                    System.out.println("手动被观察者通知,准备改变");
                });
                demo.setChanged();//监控数据是否发生变化
                demo.notifyObservers();//通知
            }
        }
        
    3. 响应式编程( Reactor实现)(有订阅才会有输出)

      • 响应式编程操作中,Reactor 是满足 Reactive规范框架。

      • Reactor 有两个核心类,Mono 和 Flux,这两个类实现接口 Publisher,提供丰富操作符。Flux对象实现发布者,返回N个元素;Mono实现翻发者,返回0或者1个元素。

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

      • 代码演示Flux 和 Mono

        第一步引入依赖

        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-bom</artifactId>
            <version>2020.0.5</version>
        </dependency>
        

        第二部编写代码(有订阅才会有输出)

        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流
            Stream<Integer> stream = list.stream();
            Flux.fromStream(stream);
            //错误信号
            //Flux.error(Throwable error);
        }
        
      • 调用 just 或者其他方法只是声明数据流,数据流并没有发出,只有进行订阅之后才会触发数据流,不订阅什么都不会发生的。

        Flux.just(1, 2, 3, 4).subscribe(System.out::println) ;
        Mono.just(1).subscribe(System.out::println);
        
      • 操作符:对数据流进行一道道操作,成为操作符,比如工厂流水线。

        • 第一 map 元素映射为新元素

          image

        • 第二 flatMap 元素映射为流

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

          image

    4. WebFlux执行流程和核心API

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

      • Netty

        • BIO(阻塞状态)

          image

        • NIO(非阻塞)

          image

      • SpringjWebflux执行过程和SpringMVC相似的

        SpringWebflux核心控制器DispatchHandler,实现接口WebHandler

        接口WebHandler有一个方法

        修改导入

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        
        public interface WebHandler {
            Mono<Void> handle(ServerWebExchange var1);
        }
        public class DispatcherHandler implements WebHandler, PreFlightRequestHandler, ApplicationContextAware {
            public Mono<Void> handle(ServerWebExchange exchange) {//放http请求响应信息
                if (this.handlerMappings == null) {
                    return this.createNotFoundError();//如果为空创建一个notfound一个错误
                } else {
                    return CorsUtils.isPreFlightRequest(exchange.getRequest()) ? this.handlePreFlight(exchange) : Flux.fromIterable(this.handlerMappings).concatMap((mapping) -> {
                        return mapping.getHandler(exchange);//根据对应请去地址,获取对应mapping
                    }).next().switchIfEmpty(this.createNotFoundError()).flatMap((handler) -> {
                        return this.invokeHandler(exchange, handler);//执行业务 
                    }).flatMap((result) -> {
                        return this.handleResult(exchange, result);//把处理结果进行返回
                    });
                }
            }
        }
        
      • SpringWebflux里面 DispatcherHandler,负责请求的处理

        HandlerMapping:根据客户端请求查询处理请求的方法

        HandlerAdapter:具体的业务方法

        HandlerResultHandler:响应结果进行处理

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

      • WebHandler的体系

        image

    SpringWebFlux(基于注解编程模型)

    SpringWebflux实现方式有两种:注解编程模型,函数式编程模型。

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

    • 第一步 创建SpringBoot工程,引入webfux 依赖

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-webflux</artifactId>
      </dependency>
      
    • 第二步 配置启动端口号

      image

    • 第三步 创建包和相关类

      image

      //创建实体类
      public class User {
          private String name;
          private String gender;
          private Integer age;
          //省略set,get,toString,无参构造,有参构造方法
      }
      
      //用户操作接口
      public interface UserService {
          //根据id查询用户
          Mono<User> getUserById(int id);
          //查询所有用户
          Flux<User> getAllUser();
          //添加用户
          Mono<Void> saveUserInfo(Mono<User> user);
      }
      
      //操作接口实现类
      @Repository //创建对象并交给spring管理
      public class UserServiceImpl implements UserService {
          //创建map集合存储数据,充当数据库
          private final Map<Integer, User> map = new HashMap<>();
          public UserServiceImpl() {
              this.map.put(1, new User("张三", "男", 18));
          }
          @Override
          public Mono<User> getUserById(int id) {
              return Mono.justOrEmpty(this.map.get(id));
          }
          @Override
          public Flux<User> getAllUser() {
              return Flux.fromIterable(this.map.values());
          }
          @Override
          public Mono<Void> saveUserInfo(Mono<User> userMono) {
              //doOnNext取值(遍历) ; person是起名字
              return userMono.doOnNext(person -> {
                  //向map集合中放值
                  int id = map.size() + 1;
                  map.put(id, person);
              }).thenEmpty(Mono.empty());//thenEmpty,控制处理。Mono.empty操作之后把里面内容清空。
          }
      }
      

      创建controller

      @RestController //交给Spring管理,并返回相关数据
      public class UserController {
          //注入属性 UserService
          @Autowired
          private UserService userService;
          //id查询
          @GetMapping("/user/{id}")//查询一般是get请求
          public Mono<User> getUserId(@PathVariable int id) {//@PathVariable:获取路径中的id
              return userService.getUserById(id);
          }
          //查询所有
          @GetMapping("/user")
          public Flux<User> getUsers() {
              return userService.getAllUser();
          }
          //添加
          @PostMapping("/saveuser")
          public Mono<Void> saveUser(@RequestBody User user) {//@RequestBody:使用json形式传递对象
              //变成mono形式
              Mono<User> userMono = Mono.just(user);
              return userService.saveUserInfo(userMono);
          }
      }
      

      启动在main方法中启动,浏览器中打开:localhost:8081/user/1

    • 说明: SpringMVC方式实现,同步阻塞的方式,基于SpringMVC+Servlet+Tomcat

      SpringWebflux方式实现,异步非阻塞方式,基于SpringWebflux+Reactor+Netty

    SpringWebFlux(基于函数式编程模型)

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

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

    3. SpringWebflux请求和响应不再是Servlet Request 和 Servlet Response,而是ServiceRequest 和 ServerResponse。

    4. 第一步把注解编程模型工程复制一份

    5. 第二部创建Handler(具体方法)

      public class UserHandler {
          private final UserService userService;
          public UserHandler(UserService userService) {
              this.userService = userService;
          }
          //根据id查询
          public Mono<ServerResponse> getUserId(ServerRequest request) {
              //获取id值
              int userId = new Integer(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> getUserAll(ServerRequest request) {
              //调用service得到结果
              Flux<User> allUser = this.userService.getAllUser();
              return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(allUser, User.class);
          }
          //添加
          public Mono<ServerResponse> saveUser(ServerRequest request) {
              //得到user对象
              Mono<User> userMono = request.bodyToMono(User.class);//得到请求中的值转成Mono的形式
              return ServerResponse.ok().build(this.userService.saveUserInfo(userMono));//build执行
          }
      }
      
    6. 第三步初始化服务器,编写Router

      public class Server {//创建服务器
          //1. 创建Router路由
          public RouterFunction<ServerResponse> routerFunction() {
              //创建hanler对象
              UserService userService = new UserServiceImpl();
              UserHandler handler = new UserHandler(userService);
              //设置路由
              return RouterFunctions.route(
                      //请求的路径                      接收类型的数据              掉handler的方法
                      GET("/users/{id}").and(accept(APPLICATION_JSON)), handler::getUserId)
                      .andRoute(GET("/users").and(accept(APPLICATION_JSON)), handler::getUserAll);
          }
          //2. 创建服务器完成适配
          public void createReactorServer() {
              //2.1 路由和handler适配
              //调用路由方法,获取路由对象
              RouterFunction<ServerResponse> route = routerFunction();
              //创建 HttpHandler ->http请求和存储请求相关的信息
              HttpHandler httpHandler = toHttpHandler(route);
              //适配
              ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
              //2.2 创建服务器
              //创建http服务
              HttpServer httpServer = HttpServer.create();
              httpServer.handle(adapter).bindNow();//bindNow:现在进行构建
          }
          //测试调用
          public static void main(String[] args) throws IOException {
              Server server = new Server();
              server.createReactorServer();//服务启动
              System.out.println("enter to exit");
              System.in.read();
          }
      }
      
    7. 使用WebClient调用

      public class Client {
          public static void main(String[] args) {
              //WebClient.create("指定需要调的那个服务器的地址")
              WebClient webClient = WebClient.create("http:127.0.0.1:6436");
              //根据id查询
              String id = "1";
              //uri(路径,参数)                        accept(指定接收的类型)                 retrieve初始化操作
              User user = webClient.get().uri("/users/id{}", id).accept(MediaType.APPLICATION_JSON).retrieve().
                      bodyToMono(User.class).block();//block()执行
              //bodyToMono(returnBean.class):得到数据
              System.out.println(user.getName());
      
              //查询所有
              Flux<User> userFlux = webClient.get().uri("/users")
                      .accept(MediaType.APPLICATION_JSON).retrieve().bodyToFlux(User.class);
              //输出
              //userFlux.map(stu -> stu.getName()).buffer().doOnNext(System.out::println);
              userFlux.map(User::getName).buffer().doOnNext(System.out::println).blockFirst();//blockFirst订阅
          }
      }
      
  • 相关阅读:
    JavaScript与多线程的不解之缘!
    CSS居中的常用方式以及优缺点
    聊一聊Axios与登录机制
    熟悉而陌生API:Promise
    Cassandra数据类型:
    Cassandra 键空间(keyspace),表(table)
    Cassandra 配制 cassandra.yaml
    Linux 环境变量PS1设置
    添加sudo权限
    ssh免密码认证
  • 原文地址:https://www.cnblogs.com/zk2020/p/15351703.html
Copyright © 2011-2022 走看看