Spring学习笔记总结

2023-11-20

第一章 引言

1. EJB存在的问题

image-20200411104428457

2. 什么是Spring
Spring是一个轻量级的JavaEE解决方案,整合众多优秀的设计模式
  • 轻量级

    1. 对于运行环境是没有额外要求的
       开源 tomcat resion jetty 
       收费 weblogic  websphere 
    2. 代码移植性高
       不需要实现额外接口
    
  • JavaEE的解决方案

image-20200411111041836

  • 整合设计模式
1. 工厂
2. 代理
3. 模板
4. 策略
3. 设计模式
1. 广义概念
面向对象设计中,解决特定问题的经典代码
2. 狭义概念
GOF4人帮定义的23种设计模式:工厂、适配器、装饰器、门面、代理、模板...
4. 工厂设计模式
4.1 什么是工厂设计模式
1. 概念:通过工厂类,创建对象
        User user = new User();
        UserDAO userDAO = new UserDAOImpl();
2. 好处:解耦合
   耦合:指定是代码间的强关联关系,一方的改变会影响到另一方
   问题:不利于代码维护
   简单:把接口的实现类,硬编码在程序中
        UserService userService = new UserServiceImpl();
4.2 简单工厂的设计
package com.baizhiedu.basic;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class BeanFactory {
    private static Properties env = new Properties();

    static{
        try {
            //第一步 获得IO输入流
            InputStream inputStream = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
            //第二步 文件内容 封装 Properties集合中 key = userService value = com.baizhixx.UserServiceImpl
            env.load(inputStream);

            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }


    /*
        对象的创建方式:
           1. 直接调用构造方法 创建对象  UserService userService = new UserServiceImpl();
           2. 通过反射的形式 创建对象 解耦合
               Class clazz = Class.forName("com.baizhiedu.basic.UserServiceImpl");
               UserService userService = (UserService)clazz.newInstance();
     */
    public static UserService getUserService() {

        UserService userService = null;
        try {
                                         //com.baizhiedu.basic.UserServiceImpl
            Class clazz = Class.forName(env.getProperty("userService"));
            userService = (UserService) clazz.newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        return userService;

    }


    public static UserDAO getUserDAO(){

        UserDAO userDAO = null;
        try {
            Class clazz = Class.forName(env.getProperty("userDAO"));
            userDAO = (UserDAO) clazz.newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        return userDAO;

    }
}
4.3 通用工厂的设计
  • 问题

    简单工厂会存在大量的代码冗余
    

    image-20200411181701143

  • 通用工厂的代码

    创建一切想要的对象
    public class BeanFactory{
      
        public static Object getBean(String key){
             Object ret = null;
             try {
                 Class clazz = Class.forName(env.getProperty(key));
                 ret = clazz.newInstance();
             } catch (Exception e) {
                e.printStackTrace();
             }
             return ret;
         }
    
    }
    
4.4 通用工厂的使用方式
1. 定义类型 (类)
2. 通过配置文件的配置告知工厂(applicationContext.properties)
   key = value
3. 通过工厂获得类的对象
   Object ret = BeanFactory.getBean("key")
5.总结
Spring本质:工厂 ApplicationContext (applicationContext.xml)

第二章、第一个Spring程序

1. 软件版本
1. JDK1.8+
2. Maven3.5+
3. IDEA2018+
4. SpringFramework 5.1.4 
   官方网站 www.spring.io
2. 环境搭建
  • Spring的jar包

    #设置pom 依赖
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.1.4.RELEASE</version>
    </dependency>
    
  • Spring的配置文件

    1. 配置文件的放置位置:任意位置 没有硬性要求
    2. 配置文件的命名   :没有硬性要求  建议:applicationContext.xml
    
    思考:日后应用Spring框架时,需要进行配置文件路径的设置。
    

    image-20200413114751707

3. Spring的核心API
  • ApplicationContext

    作用:Spring提供的ApplicationContext这个工厂,用于对象的创建
    好处:解耦合
    
    • ApplicationContext接口类型

      接口:屏蔽实现的差异
      非web环境 : ClassPathXmlApplicationContext (main junit)
      web环境  :  XmlWebApplicationContext
      

      image-20200413142452724

    • 重量级资源

    ApplicationContext工厂的对象占用大量内存。
    不会频繁的创建对象 : 一个应用只会创建一个工厂对象。
    ApplicationContext工厂:一定是线程安全的(多线程并发访问)
    
4. 程序开发
1. 创建类型
2. 配置文件的配置 applicationContext.xml
   <bean id="person" class="com.baizhiedu.basic.Person"/>
3. 通过工厂类,获得对象
   ApplicationContext
          |- ClassPathXmlApplicationContext 
   ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
   Person person = (Person)ctx.getBean("person");
5. 细节分析
  • 名词解释

    Spring工厂创建的对象,叫做bean或者组件(componet)
    
  • Spring工厂的相关的方法

    //通过这种方式获得对象,就不需要强制类型转换
    Person person = ctx.getBean("person", Person.class);
    System.out.println("person = " + person);
            
    
    //当前Spring的配置文件中 只能有一个<bean class是Person类型
    Person person = ctx.getBean(Person.class);
    System.out.println("person = " + person);
            
    
    //获取的是 Spring工厂配置文件中所有bean标签的id值  person person1
    String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
    for (String beanDefinitionName : beanDefinitionNames) {
      System.out.println("beanDefinitionName = " + beanDefinitionName);
    }
            
    
    //根据类型获得Spring配置文件中对应的id值
    String[] beanNamesForType = ctx.getBeanNamesForType(Person.class);
    for (String id : beanNamesForType) {
      System.out.println("id = " + id);
    }
            
    
    //用于判断是否存在指定id值得bean
    if (ctx.containsBeanDefinition("a")) {
      System.out.println("true = " + true);
    }else{
      System.out.println("false = " + false);
    }
          
    
    //用于判断是否存在指定id值得bean
    if (ctx.containsBean("person")) {
      System.out.println("true = " + true);
    }else{
      System.out.println("false = " + false);
    }
    
  • 配置文件中需要注意的细节

    1. 只配置class属性
    <bean  class="com.baizhiedu.basic.Person"/>
    a) 上述这种配置 有没有id值 com.baizhiedu.basic.Person#0
    b) 应用场景: 如果这个bean只需要使用一次,那么就可以省略id值
                如果这个bean会使用多次,或者被其他bean引用则需要设置id值
    
    
    2. name属性
    作用:用于在Spring的配置文件中,为bean对象定义别名(小名)
    相同:
       1. ctx.getBean("id|name")-->object
       2. <bean id="" class=""
          等效
          <bean name="" class=""
    区别:
       1. 别名可以定义多个,但是id属性只能有一个值
       2. XML的id属性的值,命名要求:必须以字母开头,字母 数字 下划线 连字符 不能以特殊字符开头 /person
             name属性的值,命名没有要求 /person
          name属性会应用在特殊命名的场景下:/person (spring+struts1)
          
          XML发展到了今天:ID属性的限制,不存在 /person
       3. 代码
             //用于判断是否存在指定id值得bean,不能判断name值
            if (ctx.containsBeanDefinition("person")) {
                System.out.println("true = " + true);
            }else{
                System.out.println("false = " + false);
            }
    
    
            //用于判断是否存在指定id值得bean,也可以判断name值
            if (ctx.containsBean("p")) {
                System.out.println("true = " + true);
            }else{
                System.out.println("false = " + false);
            }
    
6. Spring工厂的底层实现原理(简易版)

Spring工厂是可以调用对象私有的构造方法创建对象

image-20200415113032782

7. 思考
问题:未来在开发过程中,是不是所有的对象,都会交给Spring工厂来创建呢?
回答:理论上 是的,但是有特例 :实体对象(entity)是不会交给Spring创建,它是由持久层框架进行创建。

第三章、Spring5.x与日志框架的整合

Spring与日志框架进行整合,日志框架就可以在控制台中,输出Spring框架运行过程中的一些重要的信息。
好处:便于了解Spring框架的运行过程,利于程序的调试
  • Spring如何整合日志框架

    默认
      Spring1.2.3早期都是于commons-logging.jar
      Spring5.x默认整合的日志框架 logback log4j2
    
    Spring5.x整合log4j 
      1. 引入log4j jar包
      2. 引入log4.properties配置文件
    
    • pom

      <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.25</version>
      </dependency>
      
      <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
      </dependency>
      
    • log4j.properties

      # resources文件夹根目录下
      ### 配置根
      log4j.rootLogger = debug,console
      
      ### 日志输出到控制台显示
      log4j.appender.console=org.apache.log4j.ConsoleAppender
      log4j.appender.console.Target=System.out
      log4j.appender.console.layout=org.apache.log4j.PatternLayout
      log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
      

第四章、注入(Injection)

1. 什么是注入
通过Spring工厂及配置文件,为所创建对象的成员变量赋值
1.1 为什么需要注入

通过编码的方式,为成员变量进行赋值,存在耦合

image-20200415185212664

1.2 如何进行注入[开发步骤]
  • 类的成员变量提供set get方法

  • 配置spring的配置文件

     <bean id="person" class="com.baizhiedu.basic.Person">
       <property name="id">
         <value>10</value>
       </property>
       <property name="name">
         <value>xiaojr</value>
       </property>
    </bean>
    
1.3 注入好处
解耦合
2. Spring注入的原理分析(简易版)

Spring通过底层调用对象属性对应的set方法,完成成员变量的赋值,这种方式我们也称之为set注入

image-20200415191157364

第五章、Set注入详解

针对于不同类型的成员变量,在<property>标签,需要嵌套其他标签

<property>
    xxxxx
</property>

image-20200416090518713

1. JDK内置类型
1.1 String+8种基本类型
<value>suns</value>
1.2 数组
<list>
  <value>suns@zparkhr.com.cn</value>
  <value>liucy@zparkhr.com.cn</value>
  <value>chenyn@zparkhr.com.cn</value>
</list>
1.3 Set集合
<set>
   <value>11111</value>
   <value>112222</value>
</set>

<set>
   <ref bean
   <set 
</set>
1.4 List集合
<list>
   <value>11111</value>
   <value>2222</value>
</list>

<list>
   <ref bean
   <set 
</list>
1.5 Map集合
注意: map -- entry  -- key有特定的标签  <key></key>
                       值根据对应类型选择对应类型的标签
<map>
  <entry>
    <key><value>suns</value></key>
    <value>3434334343</value>
  </entry>
  <entry>
    <key><value>chenyn</value></key>
    <ref bean
  </entry>
</map>
1.6 Properites
Properties类型 特殊的Map key=String value=String 
<props>
  <prop key="key1">value1</prop>
  <prop key="key2">value2</prop>
</props>
1.7 复杂的JDK类型 (Date)
需要程序员自定义类型转换器,处理。
2. 用户自定义类型
2.1 第一种方式
  • 为成员变量提供set get方法

  • 配置文件中进行注入(赋值)

    <bean id="userService" class="xxxx.UserServiceImpl">
       <property name="userDAO">
           <bean class="xxx.UserDAOImpl"/>
      </property>
    </bean>
    
2.2 第二种方式
  • 第一种赋值方式存在的问题

    1. 配置文件代码冗余
    2. 被注入的对象(UserDAO),多次创建,浪费(JVM)内存资源
    
  • 为成员变量提供set get方法

  • 配置文件中进行配置

    <bean id="userDAO" class="xxx.UserDAOImpl"/>
    
    <bean id="userService" class="xxx.UserServiceImpl">
       <property name="userDAO">
            <ref bean="userDAO"/>
      </property>
    </bean>
    
    #Spring4.x 废除了 <ref local=""/> 基本等效 <ref bean=""/>
    
    3. Set注入的简化写法
    3.1 基于属性简化
    JDK类型注入 
    <property name="name">
       <value>suns</value>
    </property>
    
    <property name="name" value="suns"/>
    注意:value属性 只能简化 8种基本类型+String 注入标签
    
    用户自定义类型
    <property name="userDAO">
       <ref bean="userDAO"/>
    </property>
    
    <property name="userDAO" ref="userDAO"/>
    
    3.2 基于p命名空间简化
    JDK类型注入 
    <bean id="person" class="xxxx.Person">
      <property name="name">
         <value>suns</value>
      </property>
    </bean>
    
    <bean id="person" class="xxx.Person" p:name="suns"/>
    注意:value属性 只能简化 8种基本类型+String 注入标签
    
    用户自定义类型
    <bean id="userService" class="xx.UserServiceImpl">
      <property name="userDAO"> 
        <ref bean="userDAO"/>
       </property>
    </bean>
    
    <bean id="userService" class="xxx.UserServiceImpl" p:userDAO-ref="userDAO"/>
    

第六章、构造注入

注入:通过Spring的配置文件,为成员变量赋值
Set注入:Spring调用Set方法 通过配置文件 为成员变量赋值
构造注入:Spring调用构造方法 通过配置文件 为成员变量赋值
1. 开发步骤
  • 提供有参构造方法

    public class Customer implements Serializable {
        private String name;
        private int age;
    
        public Customer(String name, int age) {
            this.name = name;
            this.age = age;
        }
    }
    
  • Spring的配置文件

    <bean id="customer" class="com.baizhiedu.basic.constructer.Customer">
      <constructor-arg>
        <value>suns</value>
      </constructor-arg>
      <constructor-arg>
        <value>102</value>
      </constructor-arg>
    </bean>
    
2. 构造方法重载
2.1 参数个数不同时
通过控制<constructor-arg>标签的数量进行区分 
2.1 构造参数个数相同时
通过在标签引入 type属性 进行类型的区分 <constructor-arg type="">
3. 注入的总结
未来的实战中,应用set注入还是构造注入?
答案:set注入更多 
       1. 构造注入麻烦 (重载)
       2. Spring框架底层 大量应用了 set注入

image-20200416155620897

第七章、反转控制 与 依赖注入

1. 反转(转移)控制(IOC Inverse of Control)
控制:对于成员变量赋值的控制权
反转控制:把对于成员变量赋值的控制权,从代码中反转(转移)到Spring工厂和配置文件中完成
   好处:解耦合
底层实现:工厂设计模式

image-20200416161127972

2. 依赖注入 (Dependency Injection DI)
注入:通过Spring的工厂及配置文件,为对象(bean,组件)的成员变量赋值

依赖注入:当一个类需要另一个类时,就意味着依赖,一旦出现依赖,就可以把另一个类作为本类的成员变量,最终通过Spring配置文件进行注入(赋值)。
   好处:解耦合

image-20200416162615816

第八章、Spring工厂创建复杂对象

image-20200416164044047

1. 什么是复杂对象
复杂对象:指的就是不能直接通过new构造方法创建的对象
  Connection
  SqlSessionFactory
2. Spring工厂创建复杂对象的3种方式
2.1 FactoryBean接口
  • 开发步骤

    • 实现FactoryBean接口
      image-20200416204458451

    • Spring配置文件的配置

      # 如果Class中指定的类型 是FactoryBean接口的实现类,那么通过id值获得的是这个类所创建的复杂对象  Connection
      <bean id="conn" class="com.baizhiedu.factorybean.ConnectionFactoryBean"/>
      
  • 细节

    • 如果就想获得FactoryBean类型的对象 ctx.getBean("&conn")
      获得就是ConnectionFactoryBean对象

    • isSingleton方法
      返回 true 只会创建一个复杂对象

      返回 false 每一次都会创建新的对象
      问题:根据这个对象的特点 ,决定是返回true (SqlSessionFactory) 还是 false (Connection)

    • mysql高版本连接创建时,需要制定SSL证书,解决问题的方式

      url = "jdbc:mysql://localhost:3306/suns?useSSL=false"
      
    • 依赖注入的体会(DI)

      把ConnectionFactoryBean中依赖的4个字符串信息 ,进行配置文件的注入 
      好处:解耦合
      <bean id="conn" class="com.baizhiedu.factorybean.ConnectionFactoryBean">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/suns?useSSL=false"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
      </bean>
      
      
  • FactoryBean的实现原理[简易版]

    接口回调
    1. 为什么Spring规定FactoryBean接口 实现 并且 getObject()?
    2. ctx.getBean("conn") 获得是复杂对象 Connection 而没有 获得 ConnectionFactoryBean(&)
    
    Spring内部运行流程
    1. 通过conn获得 ConnectionFactoryBean类的对象 ,进而通过instanceof 判断出是FactoryBean接口的实现类
    2. Spring按照规定 getObject() ---> Connection
    3. 返回Connection 
    

    image-20200417114723005

  • FactoryBean总结

    Spring中用于创建复杂对象的一种方式,也是Spring原生提供的,后续讲解Spring整合其他框架,大量应用FactoryBean
    
2.2 实例工厂
1. 避免Spring框架的侵入 
2. 整合遗留系统 
  • 开发步骤
 <bean id="connFactory" class="com.baizhiedu.factorybean.ConnectionFactory"></bean>

 <bean id="conn"  factory-bean="connFactory" factory-method="getConnection"/>
2.3 静态工厂
  • 开发步骤

    <bean id="conn" class="com.baizhiedu.factorybean.StaticConnectionFactory" factory-method="getConnection"/>
    
3. Spring工厂创建对象的总结

image-20200417152030222

第九章、控制Spring工厂创建对象的次数

1. 如何控制简单对象的创建次数
<bean id="account" scope="singleton|prototype" class="xxxx.Account"/>
sigleton:只会创建一次简单对象 默认值
prototype:每一次都会创建新的对象
2. 如何控制复杂对象的创建次数
FactoryBean{
   isSingleton(){
      return true  只会创建一次
      return false 每一次都会创建新的
   }

}
如没有isSingleton方法 还是通过scope属性 进行对象创建次数的控制
3. 为什么要控制对象的创建次数?
好处:节省不别要的内存浪费 
  • 什么样的对象只创建一次?

    1. SqlSessionFactory
    2. DAO
    3. Service
    
  • 什么样的对象 每一次都要创建新的?

    1. Connection
    2. SqlSession | Session
    3. Struts2 Action
    

第十章、对象的生命周期

image-20200418155427918

1. 什么是对象的生命周期
指的是一个对象创建、存活、消亡的一个完整过程
2. 为什么要学习对象的生命周期
由Spring负责对象的创建、存活、销毁,了解生命周期,有利于我们使用好Spring为我们创建的对象
3. 生命周期的3个阶段
  • 创建阶段

    Spring工厂何时创建对象
    
    • scope=“singleton”

      Spring工厂创建的同时,对象的创建
      
      注意:设置scope=singleton 这种情况下 也需要在获取对象的同时,创建对象 
      <bean lazy-init="true"/>
      
    • scope=“prototype”

      Spring工厂会在获取对象的同时,创建对象
      ctx.getBean("")
      
  • 初始化阶段
    /百知教育 — Spring系列课程 — 工厂.assets/

    Spring工厂在创建完对象后,调用对象的初始化方法,完成对应的初始化操作
    
    1. 初始化方法提供:程序员根据需求,提供初始化方法,最终完成初始化操作
    2. 初始化方法调用:Spring工厂进行调用
    
    • InitializingBean接口

      //程序员根据需求,实现的方法,完成初始化操作
      public void afterProperitesSet(){
        
      }
      
    • 对象中提供一个普通的方法

      public void myInit(){
        
      }
      
      <bean id="product" class="xxx.Product" init-method="myInit"/>
      
    • 细节分析

      1. 如果一个对象即实现InitializingBean 同时又提供的 普通的初始化方法 顺序

        1. InitializingBean 
        2. 普通初始化方法
        
      2. 注入一定发生在初始化操作的前面

      3. 什么叫做初始化操作

        资源的初始化:数据库 IO 网络 .....
        
  • 销毁阶段

    Spring销毁对象前,会调用对象的销毁方法,完成销毁操作
    
    1. Spring什么时候销毁所创建的对象?
       ctx.close();
    2. 销毁方法:程序员根据自己的需求,定义销毁方法,完成销毁操作
          调用:Spring工厂完成调用
    
    • DisposableBean

      public void destroy()throws Exception{
        
      }
      
    • 定义一个普通的销毁方法

      public void myDestroy()throws Exception{
      
      }
      <bean id="" class="" init-method="" destroy-method="myDestroy"/>
      
    • 细节分析

      1. 销毁方法的操作只适用于 scope=“singleton”

      2. 什么叫做销毁操作

        主要指的就是 资源的释放操作  io.close() connection.close();
        

第十一章、配置文件参数化

把Spring配置文件中需要经常修改的字符串信息,转移到一个更小的配置文件中

1. Spring的配置文件中存在需要经常修改的字符串?
   存在 以数据库连接相关的参数 代表
2. 经常变化字符串,在Spring的配置文件中,直接修改
   不利于项目维护(修改)
3. 转移到一个小的配置文件(.properties)
   利于维护(修改)
   
配置文件参数化:利于Spring配置文件的维护(修改)
1. 配置文件参数的开发步骤
  • 提供一个小的配置文件(.properities)

    名字:随便
    放置位置:随便
    
    jdbc.driverClassName = com.mysql.jdbc.Driver
    jdbc.url = jdbc:mysql://localhost:3306/suns?useSSL=false
    jdbc.username = root
    jdbc.password = 123456
    
  • Spring的配置文件与小配置文件进行整合

    applicationContext.xml
    <context:property-placeholder location="classpath:/db.properties"/>
    
  • 在Spring配置文件中通过${key}获取小配置文件中对应的值
    image-20200418171133796

第十二章、自定义类型转换器

1. 类型转换器
作用:Spring通过类型转换器把配置文件中字符串类型的数据,转换成了对象中成员变量对应类型的数据,进而完成了注入

image-20200418201732220

2. 自定义类型转换器
原因:当Spring内部没有提供特定类型转换器时,而程序员在应用的过程中还需要使用,那么就需要程序员自己定义类型转换器
  • 类 implements Converter接口

    public class MyDateConverter implements Converter<String, Date> {
       /*
           convert方法作用:String --->  Date
                          SimpleDateFormat sdf = new SimpleDateFormat();
                          sdf.parset(String) ---> Date
           param:source 代表的是配置文件中 日期字符串 <value>2020-10-11</value>
    
           return : 当把转换好的Date作为convert方法的返回值后,Spring自动的为birthday属性进行注入(赋值)
    
         */
    
      @Override
      public Date convert(String source) {
    
        Date date = null;
        try {
          SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
          date = sdf.parse(source);
        } catch (ParseException e) {
          e.printStackTrace();
        }
        return date;
      }
    }
    
  • 在Spring的配置文件中进行配置

    • MyDateConverter对象创建出来

      <bean id="myDateConverter" class="xxxx.MyDateConverter"/>
      
    • 类型转换器的注册

      目的:告知Spring框架,我们所创建的MyDateConverter是一个类型转换器
      <!--用于注册类型转换器-->
      <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
          <set>
            <ref bean="myDateConverter"/>
          </set>
        </property>
      </bean>
      
3. 细节
  • MyDateConverter中的日期的格式,通过依赖注入的方式,由配置文件完成赋值。

    public class MyDateConverter implements Converter<String, Date> {
        private String pattern;
    
        public String getPattern() {
            return pattern;
        }
    
        public void setPattern(String pattern) {
            this.pattern = pattern;
        }
    
        /*
           convert方法作用:String --->  Date
                          SimpleDateFormat sdf = new SimpleDateFormat();
                          sdf.parset(String) ---> Date
           param:source 代表的是配置文件中 日期字符串 <value>2020-10-11</value>
    
           return : 当把转换好的Date作为convert方法的返回值后,Spring自动的为birthday属性进行注入(赋值)
    
         */
    
        @Override
        public Date convert(String source) {
    
            Date date = null;
            try {
                SimpleDateFormat sdf = new SimpleDateFormat(pattern);
                date = sdf.parse(source);
            } catch (ParseException e) {
                e.printStackTrace();
            }
            return date;
        }
    }
    
    <!--Spring创建MyDateConverter类型对象-->
    <bean id="myDateConverter" class="com.baizhiedu.converter.MyDateConverter">
      <property name="pattern" value="yyyy-MM-dd"/>
    </bean>
    
  • ConversionSeviceFactoryBean 定义 id属性 值必须 conversionService

  • Spring框架内置日期类型的转换器

    日期格式:2020/05/01 (不支持 :2020-05-01)
    

第十三章、后置处理Bean

BeanPostProcessor作用:对Spring工厂所创建的对象,进行再加工。

AOP底层实现:

注意:BeanPostProcessor接口
          xxxx(){
             
          
          }
  • 后置处理Bean的运行原理分析
    image-20200420155053027
程序员实现BeanPostProcessor规定接口中的方法:

Object postProcessBeforeInitiallization(Object bean String beanName)
作用:Spring创建完对象,并进行注入后,可以运行Before方法进行加工
获得Spring创建好的对象 :通过方法的参数
最终通过返回值交给Spring框架 

Object postProcessAfterInitiallization(Object bean String beanName)
作用:Spring执行完对象的初始化操作后,可以运行After方法进行加工
获得Spring创建好的对象 :通过方法的参数
最终通过返回值交给Spring框架 

实战中:
很少处理Spring的初始化操作:没有必要区分Before After。只需要实现其中的一个After方法即可
注意:
    postProcessBeforeInitiallization
    return bean对象
  • BeanPostProcessor的开发步骤

    1. 类 实现 BeanPostProcessor接口

      public class MyBeanPostProcessor implements BeanPostProcessor {
      
          @Override
          public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
              return bean;
          }
      
          @Override
          public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
      
              Categroy categroy = (Categroy) bean;
              categroy.setName("xiaowb");
      
      
              return categroy;
          }
      }
      
    2. Spring的配置文件中进行配置

      <bean id="myBeanPostProcessor" class="xxx.MyBeanPostProcessor"/>
      
    3. BeanPostProcessor细节

      BeanPostProcessor会对Spring工厂中所有创建的对象进行加工。
      

第十四章、注解基础概念

1. 什么是注解编程
指的是在类或者方法上加入特定的注解(@XXX),完成特定功能的开发。
  
	@Component
public class XXX{}
2. 为什么要讲解注解编程
1. 注解开发方便
     代码简洁 开发速度大大提高
2. Spring开发潮流
     Spring2.x引入注解  Spring3.x完善注解 SpringBoot普及 推广注解编程 
3. 注解的作用
  • 替换XML这种配置形式,简化配置

    image-20200527164703807

  • 替换接口,实现调用双方的契约性

    通过注解的方式,在功能调用者和功能提供者之间达成约定,进而进行功能的调用。因为注解应用更为方便灵活,所以在现在的开发中,更推荐通过注解的形式,完成
    

    image-20200527171704953

4. Spring注解的发展历程
1. Spring2.x开始支持注解编程 @Component @Service @Scope..
     目的:提供的这些注解只是为了在某些情况下简化XML的配置,作为XML开发的有益补充。
2. Spring3.x @Configuration @Bean..
     目的:彻底替换XML,基于纯注解编程
3. Spring4.x SpringBoot 
     提倡使用注解常见开发
5. Spring注解开发的一个问题
Spring基于注解进行配置后,还能否解耦合呢?

在Spring框架应用注解时,如果对注解配置的内容不满意,可以通过Spring配置文件进行覆盖的。

第十五章、Spring的基础注解(Spring2.x)

这个阶段的注解,仅仅是简化XML的配置,并不能完全替代XML
1. 对象创建相关注解
  • 搭建开发环境

    <context:component-scan base-package="com.baizhiedu"/>
    
    作用:让Spring框架在设置包及其子包中扫描对应的注解,使其生效。
    
  • 对象创建相关注解

    • @Component

      作用:替换原有spring配置文件中的<bean标签 
      注意:
          id属性 component注解 提供了默认的设置方式  首单词首字母小写
          class属性 通过反射获得class内容 
      

      image-20200528112232356

    • @Component 细节

      • 如何显示指定工厂创建对象的id值

        @Component("u")
        
      • Spring配置文件覆盖注解配置内容

        applicationContext.xml
        
        <bean id="u" class="com.baizhiedu.bean.User"/>
        
        id值 class的值 要和 注解中的设置保持一值 
        
    • @Component的衍生注解

      @Repository  --->  XXXDAO
        @Repository
        public class UserDAO{
        
        }
      @Service
        @Service
        public class UserService{
        
        }
      @Controller 
        @Controller 
        public class RegAction{
        
        }
      注意:本质上这些衍生注解就是@Component 
           作用 <bean  
           细节 @Service("s")
      
      目的:更加准确的表达一个类型的作用
      
      注意:Spring整合Mybatis开发过程中 不使用@Repository @Component
           
      
  • @Scope注解

    作用:控制简单对象创建次数
    注意:不添加@Scope Spring提供默认值 singleton
    <bean id="" class="" scope="singleton|prototype"/>
    
  • @Lazy注解

    作用:延迟创建单实例对象
    注意:一旦使用了@Lazy注解后,Spring会在使用这个对象时候,进行这个对象的创建
    <bean id="" class="" lazy="false"/>
    
  • 生命周期方法相关注解

    1. 初始化相关方法 @PostConstruct
       InitializingBean
       <bean init-method=""/>
    2. 销毁方法 @PreDestroy
       DisposableBean
       <bean destory-method=""/>
    注意:1. 上述的2个注解并不是Spring提供的,JSR(JavaEE规范)520
         2. 再一次的验证,通过注解实现了接口的契约性
    
2. 注入相关注解
  • 用户自定义类型 @Autowired

    image-20200601114751016

    @Autowired细节
    1. Autowired注解基于类型进行注入 [推荐]
       基于类型的注入:注入对象的类型,必须与目标成员变量类型相同或者是其子类(实现类)
    
    2. Autowired Qualifier 基于名字进行注入 [了解]
       基于名字的注入:注入对象的id值,必须与Qualifier注解中设置的名字相同
    
    3. Autowired注解放置位置 
        a) 放置在对应成员变量的set方法上 
        b) 直接把这个注解放置在成员变量之上,Spring通过反射直接对成员变量进行注入(赋值)[推荐]
    
    4. JavaEE规范中类似功能的注解
        JSR250 @Resouce(name="userDAOImpl") 基于名字进行注入
               @Autowired()
               @Qualifier("userDAOImpl")
               注意:如果在应用Resource注解时,名字没有配对成功,那么他会继续按照类型进行注入。
        JSR330 @Inject 作用 @Autowired完全一致 基于类型进行注入 ---》 EJB3.0
              <dependency>
                <groupId>javax.inject</groupId>
                <artifactId>javax.inject</artifactId>
                <version>1</version>
              </dependency>
    
  • JDK类型

    @Value注解完成
    1. 设置xxx.properties 
       id = 10
       name = suns
    2. Spring的工厂读取这个配置文件 
       <context:property-placeholder location=""/>
    3. 代码 
       属性 @Value("${key}")
    
    • @PropertySource

      1. 作用:用于替换Spring配置文件中的<context:property-placeholder location=""/>标签
      2. 开发步骤 
          1. 设置xxx.properties 
             id = 10
             name = suns
          2. 应用@PropertySource
          3. 代码
             属性 @Value()
      
    • @Value注解使用细节

      • @Value注解不能应用在静态成员变量上

        如果应用,赋值(注入)失败
        
      • @Value注解+Properties这种方式,不能注入集合类型

        Spring提供新的配置形式 YAML YML (SpringBoot)
        
3. 注解扫描详解
<context:component-scan base-package="com.baizhiedu"/>
当前包 及其 子包 
1. 排除方式
<context:component-scan base-package="com.baizhiedu">
   <context:exclude-filter type="" expression=""/>
   type:assignable:排除特定的类型 不进行扫描
        annotation:排除特定的注解 不进行扫描
        aspectj:切入点表达式
                包切入点: com.baizhiedu.bean..*
                类切入点: *..User
        regex:正则表达式 
        custom:自定义排除策略框架底层开发
</context:component-scan>

排除策略可以叠加使用 
<context:component-scan base-package="com.baizhiedu">
  <context:exclude-filter type="assignable" expression="com.baizhiedu.bean.User"/>

  <context:exclude-filter type="aspectj" expression="com.baizhiedu.injection..*"/>
</context:component-scan>
2. 包含方式
<context:component-scan base-package="com.baizhiedu" use-default-filters="false">
   <context:include-filter type="" expression=""/>
</context:component-scan>

1. use-default-filters="false"
   作用:让Spring默认的注解扫描方式 失效。
2. <context:include-filter type="" expression=""/>
   作用:指定扫描那些注解 
   type:assignable:排除特定的类型 不进行扫描
        annotation:排除特定的注解 不进行扫描
        aspectj:切入点表达式
                包切入点: com.baizhiedu.bean..*
                类切入点: *..User
        regex:正则表达式 
        custom:自定义排除策略框架底层开发

包含的方式支持叠加
 <context:component-scan base-package="com.baizhiedu" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
 </context:component-scan>
4. 对于注解开发的思考
  • 配置互通

    Spring注解配置 配置文件的配置 互通
    
    @Repository
    public class UserDAOImpl{
    
    
    }
    
    public class UserServiceImpl{
       private UserDAO userDAO;
       set get
    }
    
    <bean id="userService" class="com.baizhiedu.UserServiceImpl">
       <property name="userDAO" ref="userDAOImpl"/>
    </bean>
    
  • 什么情况下使用注解 什么情况下使用配置文件

    @Component 替换 <bean 
    
    基础注解(@Component @Autowired @Value) 程序员开发类型的配置
    
    1. 在程序员开发的类型上 可以加入对应注解 进行对象的创建 
       User  UserService  UserDAO  UserAction 
    
    2. 应用其他非程序员开发的类型时,还是需要使用<bean 进行配置的
       SqlSessionFactoryBean  MapperScannerConfigure 
    
5. SSM整合开发(半注解开发)
  • 搭建开发环境

    • 引入相关jar 【SSM POM】
    • 引入相关配置文件
      • applicationContext.xml
      • struts.xml
      • log4.properties
      • XXXMapper.xml
    • 初始化配置
      • Web.xml Spring (ContextLoaderListener)
      • Web.xml Struts Filter
  • 编码

    <context:component-scan base-package=""/>
    
    • DAO (Spring+Mybatis)

      1. 配置文件的配置
         1. DataSource
         2. SqlSessionFactory ----> SqlSessionFactoryBean
            1. dataSource
            2. typeAliasesPackage
            3. mapperLocations 
         3. MapperScannerConfigur ---> DAO接口实现类
      2. 编码
         1. entity 
         2. table
         3. DAO接口
         4. 实现Mapper文件
      
    • Service

      1. 原始对象 ---》 注入DAO
         @Service ---> @Autowired
      
      2. 额外功能 ---》 DataSourceTransactionManager ---> dataSource
      3. 切入点 + 事务属性
         @Transactional(propagation,readOnly...)
      4. 组装切面
         <tx:annotation-driven 
      
    • Controller (Spring+Struts2)

      1. @Controller
         @Scope("prototype")
         public class RegAction implements Action{
            @Autowired
            private UserService userServiceImpl;
         
         }
      2. struts.xml
          <action class="spring配置文件中action对应的id值"/>
      

第十六章、Spring的高级注解(Spring3.x 及以上)

1. 配置Bean
Spring在3.x提供的新的注解,用于替换XML配置文件。
  
  @Configuration
public class AppConfig{
  
}
  1. 配置Bean在应用的过程中 替换了XML具体什么内容呢?

    image-20200703100033265

  2. AnnotationConfigApplicationContext

    1. 创建工厂代码
       ApplicationContext ctx = new AnnotationConfigApplicationContext();
    2. 指定配置文件 
       1. 指定配置bean的Class
           ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
       2. 指定配置bean所在的路径 
           ApplicationContext ctx = new AnnotationConfigApplicationContext("com.baizhiedu");
    
  • 配置Bean开发的细节分析

    • 基于注解开发使用日志

      不能集成Log4j
      集成logback 
      
      • 引入相关jar

         <dependency>
              <groupId>org.slf4j</groupId>
              <artifactId>slf4j-api</artifactId>
              <version>1.7.25</version>
            </dependency>
        
            <dependency>
              <groupId>org.slf4j</groupId>
              <artifactId>jcl-over-slf4j</artifactId>
              <version>1.7.25</version>
            </dependency>
        
            <dependency>
              <groupId>ch.qos.logback</groupId>
              <artifactId>logback-classic</artifactId>
              <version>1.2.3</version>
            </dependency>
        
            <dependency>
              <groupId>ch.qos.logback</groupId>
              <artifactId>logback-core</artifactId>
              <version>1.2.3</version>
            </dependency>
        
            <dependency>
              <groupId>org.logback-extensions</groupId>
              <artifactId>logback-ext-spring</artifactId>
              <version>0.1.4</version>
            </dependency>
        
      • 引入logback配置文件 (logback.xml)

        <?xml version="1.0" encoding="UTF-8"?>
        <configuration>
            <!-- 控制台输出 -->
            <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
                <encoder>
                    <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
                    <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
                </encoder>
            </appender>
        
            <root level="DEBUG">
                <appender-ref ref="STDOUT" />
            </root>
        
        </configuration>
        
    • @Configuration注解的本质

      本质:也是@Component注解的衍生注解
      
      可以应用<context:component-scan进行扫描
      
2. @Bean注解
@Bean注解在配置bean中进行使用,等同于XML配置文件中的<bean标签
1. @Bean注解的基本使用
  • 对象的创建

    image-20200703150632630

    1. 简单对象
       直接能够通过new方式创建的对象 
       User  UserService   UserDAO 
    2. 复杂对象
       不能通过new的方式直接创建的对象
       Connection SqlSessionFactory
    
    • @Bean注解创建复杂对象的注意事项

      遗留系统整合 
      @Bean
      public Connection conn1() {
        Connection conn = null;
        try {
          ConnectionFactoryBean factoryBean = new ConnectionFactoryBean();
          conn = factoryBean.getObject();
        } catch (Exception e) {
          e.printStackTrace();
        }
        return conn;
      }
      
  • 自定义id值

    @Bean("id")
    
  • 控制对象创建次数

    @Bean
    @Scope("singleton|prototype") 默认值 singleton
    
2. @Bean注解的注入
  • 用户自定义类型

    @Bean
    public UserDAO userDAO() {
      return new UserDAOImpl();
    }
    
    @Bean
    public UserService userService(UserDAO userDAO) {
      UserServiceImpl userService = new UserServiceImpl();
      userService.setUserDAO(userDAO);
      return userService;
    }
    
    //简化写法
    @Bean
    public UserService userService() {
      UserServiceImpl userService = new UserServiceImpl();
      userService.setUserDAO(userDAO());
      return userService;
    }
    
  • JDK类型的注入

    @Bean
    public Customer customer() {
      Customer customer = new Customer();
      customer.setId(1);
      customer.setName("xiaohei");
    
      return customer;
    }
    
    • JDK类型注入的细节分析

      如果直接在代码中进行set方法的调用,会存在耦合的问题 
      
      @Configuration
      @PropertySource("classpath:/init.properties")
      public class AppConfig1 {
      
          @Value("${id}")
          private Integer id;
          @Value("${name}")
          private String name;
       
          @Bean
          public Customer customer() {
              Customer customer = new Customer();
              customer.setId(id);
              customer.setName(name);
      
              return customer;
          }
      }
      
3. @ComponentScan注解
@ComponentScan注解在配置bean中进行使用,等同于XML配置文件中的<context:component-scan>标签

目的:进行相关注解的扫描 (@Component @Value ...@Autowired)
1. 基本使用
@Configuration
@ComponentScan(basePackages = "com.baizhiedu.scan")
public class AppConfig2 {

}

<context:component-scan base-package=""/>
2. 排除、包含的使用
  • 排除

    <context:component-scan base-package="com.baizhiedu">
      <context:exclude-filter type="assignable" expression="com.baizhiedu.bean.User"/>
    </context:component-scan>
    
    @ComponentScan(basePackages = "com.baizhiedu.scan",
                   excludeFilters = {@ComponentScan.Filter(type= FilterType.ANNOTATION,value={Service.class}),
                                     @ComponentScan.Filter(type= FilterType.ASPECTJ,pattern = "*..User1")})
    
    type = FilterType.ANNOTATION          value
                     .ASSIGNABLE_TYPE     value
                     .ASPECTJ             pattern   
                     .REGEX               pattern
                     .CUSTOM              value
    
  • 包含

    <context:component-scan base-package="com.baizhiedu" use-default-filters="false">
       <context:include-filter type="" expression=""/>
    </context:component-scan>
    
    @ComponentScan(basePackages = "com.baizhiedu.scan",
                   useDefaultFilters = false,
                   includeFilters = {@ComponentScan.Filter(type= FilterType.ANNOTATION,value={Service.class})})
    
    type = FilterType.ANNOTATION          value
                     .ASSIGNABLE_TYPE     value
                     .ASPECTJ             pattern   
                     .REGEX               pattern
                     .CUSTOM              value
    
4. Spring工厂创建对象的多种配置方式
1. 多种配置方式的应用场景

image-20200706174301418

2. 配置优先级
@Component及其衍生注解 < @Bean < 配置文件bean标签
优先级高的配置 覆盖优先级低配置 

@Component
public class User{

}

@Bean
public User user(){
  return new User();
}

<bean id="user" class="xxx.User"/>

配置覆盖:id值 保持一致
  • 解决基于注解进行配置的耦合问题

    @Configuration
    //@ImportResource("applicationContext.xml")
    public class AppConfig4 {
    
        @Bean
        public UserDAO userDAO() {
            return new UserDAOImpl();
        }
    }
    
    @Configuration
    @ImportResource("applicationContext.xml")
    public class AppConfig5{
      
    }
    
    applicationContext.xml
    <bean id="userDAO" class="com.baizhiedu.injection.UserDAOImplNew"/>
    
5. 整合多个配置信息
  • 为什么会有多个配置信息
拆分多个配置bean的开发,是一种模块化开发的形式,也体现了面向对象各司其职的设计思想
  • 多配置信息整合的方式
    • 多个配置Bean的整合
    • 配置Bean与@Component相关注解的整合
    • 配置Bean与SpringXML配置文件的整合
  • 整合多种配置需要关注那些要点
    • 如何使多配置的信息 汇总成一个整体
    • 如何实现跨配置的注入
1. 多个配置Bean的整合
  • 多配置的信息汇总

    • base-package进行多个配置Bean的整合

      image-20200707170421669

    • @Import

      1. 可以创建对象
      2. 多配置bean的整合
      

      image-20200707170745814

    • 在工厂创建时,指定多个配置Bean的Class对象 【了解】

      ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig1.class,AppConfig2.class);
      
  • 跨配置进行注入

    在应用配置Bean的过程中,不管使用哪种方式进行配置信息的汇总,其操作方式都是通过成员变量加入@Autowired注解完成。
    @Configuration
    @Import(AppConfig2.class)
    public class AppConfig1 {
    
        @Autowired
        private UserDAO userDAO;
    
        @Bean
        public UserService userService() {
            UserServiceImpl userService = new UserServiceImpl();
            userService.setUserDAO(userDAO);
            return userService;
        }
    }
    
    @Configuration
    public class AppConfig2 {
    
        @Bean
        public UserDAO userDAO() {
            return new UserDAOImpl();
        }
    }
    
2. 配置Bean与@Component相关注解的整合
@Component(@Repository)
public class UserDAOImpl implements UserDAO{
  
}

@Configuration
@ComponentScan("")
public class AppConfig3 {
   
    @Autowired
    private UserDAO userDAO;

    @Bean
    public UserService userService() {
        UserServiceImpl userService = new UserServiceImpl();
        userService.setUserDAO(userDAO);
        return userService;
    }
}

ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig3.class);
3. 配置Bean与配置文件整合
1. 遗留系统的整合 2. 配置覆盖
  
public class UserDAOImpl implements UserDAO{
  
}
<bean id="userDAO" class="com.baizhiedu.injection.UserDAOImpl"/>

@Configuration
@ImportResource("applicationContext.xml")
public class AppConfig4 {
  
    @Autowired
    private UserDAO userDAO;

    @Bean
    public UserService userService() {
        UserServiceImpl userService = new UserServiceImpl();
        userService.setUserDAO(userDAO);
        return userService;
    }
}

ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig4.class);
6. 配置Bean底层实现原理
Spring在配置Bean中加入了@Configuration注解后,底层就会通过Cglib的代理方式,来进行对象相关的配置、处理

image-20200709114200371

7. 四维一体的开发思想
1. 什么是四维一体
Spring开发一个功能的4种形式,虽然开发方式不同,但是最终效果是一样的。
1. 基于schema
2. 基于特定功能注解
3. 基于原始<bean
4. 基于@Bean注解
2. 四维一体的开发案例
1. <context:property-placehoder
2. @PropertySource  【推荐】
3. <bean id="" class="PropertySourcePlaceholderConfigure"/>
4. @Bean            【推荐】
8. 纯注解版AOP编程
1. 搭建环境
1. 应用配置Bean 
2. 注解扫描
2. 开发步骤
1. 原始对象
   @Service(@Component)
   public class UserServiceImpl implements UserService{
     
   }
2. 创建切面类 (额外功能 切入点 组装切面)
    @Aspect
    @Component
    public class MyAspect {

        @Around("execution(* login(..))")
        public Object arround(ProceedingJoinPoint joinPoint) throws Throwable {

            System.out.println("----aspect log ------");

            Object ret = joinPoint.proceed();


            return ret;
        }
    }
3. Spring的配置文件中
   <aop:aspectj-autoproxy />
   @EnableAspectjAutoProxy ---> 配置Bean 
3. 注解AOP细节分析
1. 代理创建方式的切换 JDK Cglib 
   <aop:aspectj-autoproxy proxy-target-class=true|false />
   @EnableAspectjAutoProxy(proxyTargetClass)
2. SpringBoot AOP的开发方式
     @EnableAspectjAutoProxy 已经设置好了 
     
    1. 原始对象
     @Service(@Component)
     public class UserServiceImpl implements UserService{

     }
    2. 创建切面类 (额外功能 切入点 组装切面)
      @Aspect
      @Component
      public class MyAspect {

        @Around("execution(* login(..))")
        public Object arround(ProceedingJoinPoint joinPoint) throws Throwable {

          System.out.println("----aspect log ------");

          Object ret = joinPoint.proceed();


          return ret;
        }
      }
    Spring AOP 代理默认实现 JDK  SpringBOOT AOP 代理默认实现 Cglib 
9. 纯注解版Spring+MyBatis整合
  • 基础配置 (配置Bean)

    1. 连接池
      <!--连接池-->
      <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/suns?useSSL=false"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
      </bean>
       
       @Bean
       public DataSource dataSource(){
          DruidDataSource dataSource = new DruidDataSource();
          dataSource.setDriverClassName("");
          dataSource.setUrl();
          ...
          return dataSource;
       }
    
    2. SqlSessionFactoryBean
        <!--创建SqlSessionFactory SqlSessionFactoryBean-->
        <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
          <property name="dataSource" ref="dataSource"></property>
          <property name="typeAliasesPackage" value="com.baizhiedu.entity"></property>
          <property name="mapperLocations">
            <list>
              <value>classpath:com.baizhiedu.mapper/*Mapper.xml</value>
            </list>
          </property>
        </bean>
    
        @Bean
        public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
             SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
             sqlSessionFactoryBean.setDataSource(dataSource);
             sqlSessionFactoryBean.setTypeAliasesPackage("");
             ...
             return sqlSessionFactoryBean;
        }
    
    3. MapperScannerConfigure 
       <!--创建DAO对象 MapperScannerConfigure-->
      <bean id="scanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"></property>
        <property name="basePackage" value="com.baizhiedu.dao"></property>
      </bean>
      
      @MapperScan(basePackages={"com.baizhiedu.dao"}) ---> 配置bean完成
      
    
  • 编码

    1. 实体
    2. 表
    3. DAO接口
    4. Mapper文件 
    
    1. MapperLocations编码时通配的写法
    //设置Mapper文件的路径
    sqlSessionFactoryBean.setMapperLocations(Resource..);
    Resource resouce = new ClassPathResouce("UserDAOMapper.xml")
      
    sqlSessionFactoryBean.setMapperLocations(new ClassPathResource("UserDAOMapper.xml"));
    
    <property name="mapperLocations">
       <list>
         <value>classpath:com.baizhiedu.mapper/*Mapper.xml</value>
       </list>
    </property>
    一组Mapper文件 
    
    ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    Resource[] resources = resolver.getResources("com.baizhi.mapper/*Mapper.xml");
    sqlSessionFactoryBean.setMapperLocations(resources)
    
    2. 配置Bean数据耦合的问题
    mybatis.driverClassName = com.mysql.jdbc.Driver
    mybatis.url = jdbc:mysql://localhost:3306/suns?useSSL=false
    mybatis.username = root
    mybatis.password = 123456
    mybatis.typeAliasesPackages = com.baizhiedu.mybatis
    mybatis.mapperLocations = com.baizhiedu.mapper/*Mapper.xml
    
    @Component
    @PropertySource("classpath:mybatis.properties")
    public class MybatisProperties {
        @Value("${mybatis.driverClassName}")
        private String driverClassName;
        @Value("${mybatis.url}")
        private String url;
        @Value("${mybatis.username}")
        private String username;
        @Value("${mybatis.password}")
        private String password;
        @Value("${mybatis.typeAliasesPackages}")
        private String typeAliasesPackages;
        @Value("${mybatis.mapperLocations}")
        private String mapperLocations;
    }
    
    public class MyBatisAutoConfiguration {
    
        @Autowired
        private MybatisProperties mybatisProperties;
    
        @Bean
        public DataSource dataSource() {
            DruidDataSource dataSource = new DruidDataSource();
            dataSource.setDriverClassName(mybatisProperties.getDriverClassName());
            dataSource.setUrl(mybatisProperties.getUrl());
            dataSource.setUsername(mybatisProperties.getUsername());
            dataSource.setPassword(mybatisProperties.getPassword());
            return dataSource;
        }
    
        @Bean
        public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
            SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
            sqlSessionFactoryBean.setDataSource(dataSource);
            sqlSessionFactoryBean.setTypeAliasesPackage(mybatisProperties.getTypeAliasesPackages());
            //sqlSessionFactoryBean.setMapperLocations(new ClassPathResource("UserDAOMapper.xml"));
    
            try {
                ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
                Resource[] resources = resolver.getResources(mybatisProperties.getMapperLocations());
                sqlSessionFactoryBean.setMapperLocations(resources);
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            return sqlSessionFactoryBean;
        }
    }
    
    
10. 纯注解版事务编程
1. 原始对象 XXXService
   <bean id="userService" class="com.baizhiedu.service.UserServiceImpl">
     <property name="userDAO" ref="userDAO"/>
   </bean>

   @Service
   public class UserServiceImpl implements UserService{
         @Autowired
         private UserDAO userDAO;
   }

2. 额外功能
   <!--DataSourceTransactionManager-->
    <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <property name="dataSource" ref="dataSource"/>
    </bean>
    
    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource){
          DataSourceTransactionManager dstm = new DataSourceTransactionManager();
          dstm.setDataSource(dataSource);
          return dstm 
    }

3. 事务属性
    @Transactional
    @Service
    public class UserServiceImpl implements UserService {
        @Autowired
        private UserDAO userDAO;

4. 基于Schema的事务配置 
   <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
   @EnableTransactionManager ---> 配置Bean
1. ApplicationContext ctx = new AnnotationConfigApplicationContext("com.baizhiedu.mybatis");
   SpringBoot 实现思想
2. 注解版MVC整合,SpringMVC中进行详细讲解
   SpringMyBatis --->DAO  事务基于注解 --> Service   Controller 
   org.springframework.web.context.ContextLoaderListener ---> XML工厂 无法提供 new AnnotationConfigApplicationContext
11. Spring框架中YML的使用
1. 什么是YML
YML(YAML)是一种新形式的配置文件,比XML更简单,比Properties更强大。

YAML is a nice human-readable format for configuration, and it has some useful hierarchical properties. It's more or less a superset of JSON, so it has a lot of similar features.
2. Properties进行配置问题
1. Properties表达过于繁琐,无法表达数据的内在联系. 
2. Properties无法表达对象 集合类型
3. YML语法简介
1. 定义yml文件 
   xxx.yml xxx.yaml
2. 语法
   1. 基本语法
      name: suns
      password: 123456
   2. 对象概念 
      account: 
         id: 1
         password: 123456
   3. 定义集合 
      service: 
         - 11111
         - 22222
4. Spring与YML集成思路的分析
1. 准备yml配置文件 
   init.yml
   name: suns
   password: 123456
2. 读取yml 转换成 Properties
   YamlPropertiesFactoryBean.setResources( yml配置文件的路径 ) new ClassPathResource();
   YamlPropertiesFactoryBean.getObject() ---> Properties 
3. 应用PropertySourcesPlaceholderConfigurer
   PropertySourcesPlaceholderConfigurer.setProperties();
4. 类中 @Value注解 注入 
5. Spring与YML集成编码
  • 环境搭建

    <dependency>
      <groupId>org.yaml</groupId>
      <artifactId>snakeyaml</artifactId>
      <version>1.23</version>
    </dependency>
    最低版本 1.18 
    
  • 编码

    1. 准备yml配置文件
    2. 配置Bean中操作 完成YAML读取 与 PropertySourcePlaceholderConfigure的创建 
        @Bean
        public PropertySourcesPlaceholderConfigurer configurer() {
            YamlPropertiesFactoryBean yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean();
            yamlPropertiesFactoryBean.setResources(new ClassPathResource("init.yml"));
            Properties properties = yamlPropertiesFactoryBean.getObject();
    
            PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
            configurer.setProperties(properties);
            return configurer;
        }
    3. 类 加入 @Value注解 
    
6. Spring与YML集成的问题
1. 集合处理的问题
   SpringEL表达式解决
   @Value("#{'${list}'.split(',')}")
2. 对象类型的YAML进行配置时 过于繁琐 
   @Value("${account.name}")
   
SpringBoot  @ConfigurationProperties

第十七章、AOP编程之静态代理设计模式

1. 为什么需要代理设计模式
1.1 问题
  • 在JavaEE分层开发开发中,那个层次对于我们来讲最重要

    DAO ---> Service --> Controller 
    
    JavaEE分层开发中,最为重要的是Service层
    
  • Service层中包含了哪些代码?

    Service层中 = 核心功能(几十行 上百代码) + 额外功能(附加功能)
    1. 核心功能
       业务运算
       DAO调用
    2. 额外功能 
       1. 不属于业务
       2. 可有可无
       3. 代码量很小 
       
       事务、日志、性能...
       
    
  • 额外功能书写在Service层中好不好?

    Service层的调用者的角度(Controller):需要在Service层书写额外功能。
                             软件设计者:Service层不需要额外功能
                             
    
  • 现实生活中的解决方式
    image-20200422110206172

2. 代理设计模式
1.1 概念
通过代理类,为原始类(目标)增加额外的功能
好处:利于原始类(目标)的维护
1.2名词解释
1. 目标类 原始类 
   指的是 业务类 (核心功能 --> 业务运算 DAO调用)
2. 目标方法,原始方法
   目标类(原始类)中的方法 就是目标方法(原始方法)
3. 额外功能 (附加功能)
   日志,事务,性能
1.3 代理开发的核心要素
代理类 = 目标类(原始类) + 额外功能 + 原始类(目标类)实现相同的接口

房东 ---> public interface UserService{
               m1
               m2
          }
          UserServiceImpl implements UserService{
               m1 ---> 业务运算 DAO调用
               m2 
          }
          UserServiceProxy implements UserService
               m1
               m2
1.4 编码

静态代理:为每一个原始类,手工编写一个代理类 (.java .class)

image-20200422114654195

1.5 静态代理存在的问题
1. 静态类文件数量过多,不利于项目管理
   UserServiceImpl  UserServiceProxy
   OrderServiceImpl OrderServiceProxy
2. 额外功能维护性差
   代理类中 额外功能修改复杂(麻烦)

第十八章、Spring的动态代理开发

1. Spring动态代理的概念
概念:通过代理类为原始类(目标类)增加额外功能
好处:利于原始类(目标类)的维护
2. 搭建开发环境
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
  <version>5.1.14.RELEASE</version>
</dependency>

<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjrt</artifactId>
  <version>1.8.8</version>
</dependency>

<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.8.3</version>
</dependency>

3. Spring动态代理的开发步骤
  1. 创建原始对象(目标对象)

    public class UserServiceImpl implements UserService {
        @Override
        public void register(User user) {
            System.out.println("UserServiceImpl.register 业务运算 + DAO ");
        }
    
        @Override
        public boolean login(String name, String password) {
            System.out.println("UserServiceImpl.login");
            return true;
        }
    }
    
    
    <bean id="userService" class="com.baizhiedu.proxy.UserServiceImpl"/>
    
  2. 额外功能
    MethodBeforeAdvice接口

    额外的功能书写在接口的实现中,运行在原始方法执行之前运行额外功能。
    
    public class Before implements MethodBeforeAdvice {
        /*
          作用:需要把运行在原始方法执行之前运行的额外功能,书写在before方法中
         */
        @Override
        public void before(Method method, Object[] args, Object target) throws Throwable {
            System.out.println("-----method before advice log------");
        }
    }
    
    <bean id="before" class="com.baizhiedu.dynamic.Before"/>
    
  3. 定义切入点

    切入点:额外功能加入的位置
    
    目的:由程序员根据自己的需要,决定额外功能加入给那个原始方法
    register
    login
    
    简单的测试:所有方法都做为切入点,都加入额外的功能。
    
    <aop:config>
       <aop:pointcut id="pc" expression="execution(* *(..))"/>
    </aop:config>
    
  4. 组装 (2 3整合)

    表达的含义:所有的方法 都加入 before的额外功能
    <aop:advisor advice-ref="before" pointcut-ref="pc"/>
    
  5. 调用

    目的:获得Spring工厂创建的动态代理对象,并进行调用
    ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
    注意:
       1. Spring的工厂通过原始对象的id值获得的是代理对象
       2. 获得代理对象后,可以通过声明接口类型,进行对象的存储
       
    UserService userService=(UserService)ctx.getBean("userService");
    
    userService.login("")
    userService.register()
    
    
4. 动态代理细节分析
  1. Spring创建的动态代理类在哪里?

    Spring框架在运行时,通过动态字节码技术,在JVM创建的,运行在JVM内部,等程序结束后,会和JVM一起消失
    
    什么叫动态字节码技术:通过第三个动态字节码框架,在JVM中创建对应类的字节码,进而创建对象,当虚拟机结束,动态字节码跟着消失。
    
    结论:动态代理不需要定义类文件,都是JVM运行过程中动态创建的,所以不会造成静态代理,类文件数量过多,影响项目管理的问题。
    

    image-20200423165547079

    1. 动态代理编程简化代理的开发

      在额外功能不改变的前提下,创建其他目标类(原始类)的代理对象时,只需要指定原始(目标)对象即可。
      
    2. 动态代理额外功能的维护性大大增强

第十九章、Spring动态代理详解

1. 额外功能的详解
  • MethodBeforeAdvice分析

    1. MethodBeforeAdvice接口作用:额外功能运行在原始方法执行之前,进行额外功能操作。
    
    public class Before1 implements MethodBeforeAdvice {
        /*
          作用:需要把运行在原始方法执行之前运行的额外功能,书写在before方法中
    
          Method: 额外功能所增加给的那个原始方法
                  login方法
    
                  register方法
    
                  showOrder方法
    
          Object[]: 额外功能所增加给的那个原始方法的参数。String name,String password
                                                   User
    
           Object: 额外功能所增加给的那个原始对象  UserServiceImpl
                                              OrderServiceImpl
         */
        @Override
        public void before(Method method, Object[] args, Object target) throws Throwable {
            System.out.println("-----new method before advice log------");
        }
    }
    
    2. before方法的3个参数在实战中,该如何使用。
       before方法的参数,在实战中,会根据需要进行使用,不一定都会用到,也有可能都不用。
    
       Servlet{
           service(HttpRequest request,HttpResponse response){
                request.getParameter("name") -->
                
                response.getWriter() ---> 
           
           }
       
       }
    
    
  • MethodInterceptor(方法拦截器)

    methodinterceptor接口:额外功能可以根据需要运行在原始方法执行 前、后、前后。
    
    public class Arround implements MethodInterceptor {
        /*
             invoke方法的作用:额外功能书写在invoke
                            额外功能  原始方法之前
                                     原始方法之后
                                     原始方法执行之前 之后
             确定:原始方法怎么运行
    
             参数:MethodInvocation (Method):额外功能所增加给的那个原始方法
                        login
                        register
                  invocation.proceed() ---> login运行
                                            register运行
    
              返回值:Object: 原始方法的返回值
    
             Date convert(String name)
         */
    
    
    
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
              System.out.println("-----额外功能 log----");
              Object ret = invocation.proceed();
    
              return ret;
        }
    }
    
    
    

    额外功能运行在原始方法执行之后

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
      Object ret = invocation.proceed();
      System.out.println("-----额外功能运行在原始方法执行之后----");
    
      return ret;
    }
    

    额外功能运行在原始方法执行之前,之后

    什么样的额外功能 运行在原始方法执行之前,之后都要添加?
    事务
    
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
      System.out.println("-----额外功能运行在原始方法执行之前----");
      Object ret = invocation.proceed();
      System.out.println("-----额外功能运行在原始方法执行之后----");
    
      return ret;
    }
    

    额外功能运行在原始方法抛出异常的时候

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
    
      Object ret = null;
      try {
        ret = invocation.proceed();
      } catch (Throwable throwable) {
    
        System.out.println("-----原始方法抛出异常 执行的额外功能 ---- ");
        throwable.printStackTrace();
      }
    
    
      return ret;
    }
    

    MethodInterceptor影响原始方法的返回值

    原始方法的返回值,直接作为invoke方法的返回值返回,MethodInterceptor不会影响原始方法的返回值
    
    MethodInterceptor影响原始方法的返回值
    Invoke方法的返回值,不要直接返回原始方法的运行结果即可。
    
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
       System.out.println("------log-----");
       Object ret = invocation.proceed();
       return false;
    }
    
2. 切入点详解
切入点决定额外功能加入位置(方法)

<aop:pointcut id="pc" expression="execution(* *(..))"/>
exection(* *(..)) ---> 匹配了所有方法    a  b  c 

1. execution()  切入点函数
2. * *(..)      切入点表达式 
2.1 切入点表达式
  1. 方法切入点表达式
    image-20200425105040237

    *  *(..)  --> 所有方法
    
    * ---> 修饰符 返回值
    * ---> 方法名
    ()---> 参数表
    ..---> 对于参数没有要求 (参数有没有,参数有几个都行,参数是什么类型的都行)
    
    • 定义login方法作为切入点

      * login(..)
      
      # 定义register作为切入点
      * register(..)
      
    • 定义login方法且login方法有两个字符串类型的参数 作为切入点

      * login(String,String)
      
      #注意:非java.lang包中的类型,必须要写全限定名
      * register(com.baizhiedu.proxy.User)
      
      # ..可以和具体的参数类型连用
      * login(String,..)  --> login(String),login(String,String),login(String,com.baizhiedu.proxy.User)
      
    • 精准方法切入点限定

      修饰符 返回值         包.类.方法(参数)
      
          *               com.baizhiedu.proxy.UserServiceImpl.login(..)
          *               com.baizhiedu.proxy.UserServiceImpl.login(String,String)
      
  2. 类切入点

    指定特定类作为切入点(额外功能加入的位置),自然这个类中的所有方法,都会加上对应的额外功能
    
    • 语法1

      #类中的所有方法加入了额外功能 
      * com.baizhiedu.proxy.UserServiceImpl.*(..)  
      
    • 语法2

      #忽略包
      1. 类只存在一级包  com.UserServiceImpl
      * *.UserServiceImpl.*(..)
      
      2. 类存在多级包    com.baizhiedu.proxy.UserServiceImpl
      * *..UserServiceImpl.*(..)
      
  3. 包切入点表达式 实战

    指定包作为额外功能加入的位置,自然包中的所有类及其方法都会加入额外的功能
    
    • 语法1

      #切入点包中的所有类,必须在proxy中,不能在proxy包的子包中
      * com.baizhiedu.proxy.*.*(..)
      
    • 语法2

      #切入点当前包及其子包都生效 
      * com.baizhiedu.proxy..*.*(..) 
      
2.2 切入点函数
切入点函数:用于执行切入点表达式
  1. execution

    最为重要的切入点函数,功能最全。
    执行 方法切入点表达式 类切入点表达式 包切入点表达式 
    
    弊端:execution执行切入点表达式 ,书写麻烦
         execution(* com.baizhiedu.proxy..*.*(..))
         
    注意:其他的切入点函数 简化是execution书写复杂度,功能上完全一致
    
  2. args

    作用:主要用于函数(方法) 参数的匹配
    
    切入点:方法参数必须得是2个字符串类型的参数
    
    execution(* *(String,String))
    
    args(String,String)
    
  3. within

    作用:主要用于进行类、包切入点表达式的匹配
    
    切入点:UserServiceImpl这个类
    
    execution(* *..UserServiceImpl.*(..))
    
    within(*..UserServiceImpl)
    
    execution(* com.baizhiedu.proxy..*.*(..))
    
    within(com.baizhiedu.proxy..*)
    
    

4.@annotation

作用:为具有特殊注解的方法加入额外功能

<aop:pointcut id="" expression="@annotation(com.baizhiedu.Log)"/>
  1. 切入点函数的逻辑运算

    指的是 整合多个切入点函数一起配合工作,进而完成更为复杂的需求
    
    • and与操作

      案例:login 同时 参数 2个字符串 
      
      1. execution(* login(String,String))
      
      2. execution(* login(..)) and args(String,String)
      
      注意:与操作不同用于同种类型的切入点函数 
      
      案例:register方法 和 login方法作为切入点 
      
      execution(* login(..)) or  execution(* register(..))
      
      
    • or或操作

      案例:register方法 和 login方法作为切入点 
      
      execution(* login(..)) or  execution(* register(..))
      

第二十章、AOP编程

1. AOP概念
AOP (Aspect Oriented Programing)   面向切面编程 = Spring动态代理开发
以切面为基本单位的程序开发,通过切面间的彼此协同,相互调用,完成程序的构建
切面 = 切入点 + 额外功能

OOP (Object Oritened Programing)   面向对象编程 Java
以对象为基本单位的程序开发,通过对象间的彼此协同,相互调用,完成程序的构建

POP (Producer Oriented Programing) 面向过程(方法、函数)编程 C 
以过程为基本单位的程序开发,通过过程间的彼此协同,相互调用,完成程序的构建
AOP的概念:
     本质就是Spring得动态代理开发,通过代理类为原始类增加额外功能。
     好处:利于原始类的维护

注意:AOP编程不可能取代OOP,OOP编程有意补充。
2. AOP编程的开发步骤
1. 原始对象
2. 额外功能 (MethodInterceptor)
3. 切入点
4. 组装切面 (额外功能+切入点)
3. 切面的名词解释
切面 = 切入点 + 额外功能 

几何学
   面 = 点 + 相同的性质

image-20200427134740273

第二十一章、AOP的底层实现原理

1. 核心问题
1. AOP如何创建动态代理类(动态字节码技术)
2. Spring工厂如何加工创建代理对象
   通过原始对象的id值,获得的是代理对象
2. 动态代理类的创建
2.1 JDK的动态代理
  • Proxy.newProxyInstance方法参数详解
    image-20200428175248912

image-20200428175316276

  • 编码

    public class TestJDKProxy {
    
        /*
            1. 借用类加载器  TestJDKProxy
                           UserServiceImpl
            2. JDK8.x前
    
                final UserService userService = new UserServiceImpl();
         */
        public static void main(String[] args) {
            //1 创建原始对象
            UserService userService = new UserServiceImpl();
    
            //2 JDK创建动态代理
            /*
    
             */
    
            InvocationHandler handler = new InvocationHandler(){
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("------proxy  log --------");
                    //原始方法运行
                    Object ret = method.invoke(userService, args);
                    return ret;
                }
            };
    
            UserService userServiceProxy = (UserService)Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(),userService.getClass().getInterfaces(),handler);
    
            userServiceProxy.login("suns", "123456");
            userServiceProxy.register(new User());
        }
    }
    
    
2.2 CGlib的动态代理
CGlib创建动态代理的原理:父子继承关系创建代理对象,原始类作为父类,代理类作为子类,这样既可以保证2者方法一致,同时在代理类中提供新的实现(额外功能+原始方法)

image-20200429111709226

  • CGlib编码

    package com.baizhiedu.cglib;
    
    import com.baizhiedu.proxy.User;
    import org.springframework.cglib.proxy.Enhancer;
    import org.springframework.cglib.proxy.MethodInterceptor;
    import org.springframework.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    public class TestCglib {
        public static void main(String[] args) {
            //1 创建原始对象
            UserService userService = new UserService();
    
            /*
              2 通过cglib方式创建动态代理对象
                Proxy.newProxyInstance(classloader,interface,invocationhandler)
    
                Enhancer.setClassLoader()
                Enhancer.setSuperClass()
                Enhancer.setCallback();  ---> MethodInterceptor(cglib)
                Enhancer.create() ---> 代理
             */
    
            Enhancer enhancer = new Enhancer();
    
            enhancer.setClassLoader(TestCglib.class.getClassLoader());
            enhancer.setSuperclass(userService.getClass());
    
    
            MethodInterceptor interceptor = new MethodInterceptor() {
                //等同于 InvocationHandler --- invoke
                @Override
                public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                    System.out.println("---cglib log----");
                    Object ret = method.invoke(userService, args);
    
                    return ret;
                }
            };
    
            enhancer.setCallback(interceptor);
    
            UserService userServiceProxy = (UserService) enhancer.create();
    
            userServiceProxy.login("suns", "123345");
            userServiceProxy.register(new User());
        }
    }
    
    
  • 总结

    1. JDK动态代理   Proxy.newProxyInstance()  通过接口创建代理的实现类 
    2. Cglib动态代理 Enhancer                  通过继承父类创建的代理类 
    
3. Spring工厂如何加工原始对象
  • 思路分析
    image-20200430113353205

  • 编码

    public class ProxyBeanPostProcessor implements BeanPostProcessor {
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            return bean;
        }
    
        @Override
        /*
             Proxy.newProxyInstance();
         */
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    
            InvocationHandler handler = new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("----- new Log-----");
                    Object ret = method.invoke(bean, args);
    
                    return ret;
                }
            };
          return Proxy.newProxyInstance(ProxyBeanPostProcessor.class.getClassLoader(),bean.getClass().getInterfaces(),handler);
        }
    }
    
    <bean id="userService" class="com.baizhiedu.factory.UserServiceImpl"/>
    
    
    <!--1. 实现BeanPostProcessor 进行加工
            2. 配置文件中对BeanPostProcessor进行配置
        -->
    
    <bean id="proxyBeanPostProcessor" class="com.baizhiedu.factory.ProxyBeanPostProcessor"/>
    
    

第二十二章、基于注解的AOP编程

1. 基于注解的AOP编程的开发步骤
  1. 原始对象

  2. 额外功能

  3. 切入点

  4. 组装切面

    # 通过切面类 定义了 额外功能 @Around
               定义了 切入点   @Around("execution(* login(..))")
               @Aspect 切面类 
               
    package com.baizhiedu.aspect;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    
    
    /*
           1. 额外功能
                     public class MyArround implements MethodInterceptor{
    
                          public Object invoke(MethodInvocation invocation){
    
                                  Object ret = invocation.proceed();
    
                                  return ret;
    
                          }
    
                     }
    
           2. 切入点
                 <aop:config
                     <aop:pointcut id=""  expression="execution(* login(..))"/>
     */
    @Aspect
    public class MyAspect {
    
        @Around("execution(* login(..))")
        public Object arround(ProceedingJoinPoint joinPoint) throws Throwable {
    
            System.out.println("----aspect log ------");
    
            Object ret = joinPoint.proceed();
    
    
            return ret;
        }
    }
       
    
     <bean id="userService" class="com.baizhiedu.aspect.UserServiceImpl"/>
    
        <!--
           切面
             1. 额外功能
             2. 切入点
             3. 组装切面
    
    
        -->
    <bean id="arround" class="com.baizhiedu.aspect.MyAspect"/>
    
    <!--告知Spring基于注解进行AOP编程-->
    <aop:aspectj-autoproxy />
    
2. 细节
  1. 切入点复用

    切入点复用:在切面类中定义一个函数 上面@Pointcut注解 通过这种方式,定义切入点表达式,后续更加有利于切入点复用。
    
    @Aspect
    public class MyAspect {
        @Pointcut("execution(* login(..))")
        public void myPointcut(){}
    
        @Around(value="myPointcut()")
        public Object arround(ProceedingJoinPoint joinPoint) throws Throwable {
    
            System.out.println("----aspect log ------");
    
            Object ret = joinPoint.proceed();
    
    
            return ret;
        }
    
    
        @Around(value="myPointcut()")
        public Object arround1(ProceedingJoinPoint joinPoint) throws Throwable {
    
            System.out.println("----aspect tx ------");
    
            Object ret = joinPoint.proceed();
    
    
            return ret;
        }
    
    }
    
  2. 动态代理的创建方式

AOP底层实现  2种代理创建方式
1.  JDK  通过实现接口 做新的实现类方式 创建代理对象
2.  Cglib通过继承父类 做新的子类      创建代理对象

默认情况 AOP编程 底层应用JDK动态代理创建方式 
如果切换Cglib
     1. 基于注解AOP开发
        <aop:aspectj-autoproxy proxy-target-class="true" />
     2. 传统的AOP开发
        <aop:config proxy-target-class="true">
        </aop>

第二十三章、AOP开发中的一个坑

坑:在同一个业务类中,进行业务方法间的相互调用,只有最外层的方法,才是加入了额外功能(内部的方法,通过普通的方式调用,都调用的是原始方法)。如果想让内层的方法也调用代理对象的方法,就要AppicationContextAware获得工厂,进而获得代理对象。
public class UserServiceImpl implements UserService, ApplicationContextAware {
    private ApplicationContext ctx;


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
              this.ctx = applicationContext;
    }

    @Log
    @Override
    public void register(User user) {
        System.out.println("UserServiceImpl.register 业务运算 + DAO ");
        //throw new RuntimeException("测试异常");

        //调用的是原始对象的login方法 ---> 核心功能
        /*
            设计目的:代理对象的login方法 --->  额外功能+核心功能
            ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext2.xml");
            UserService userService = (UserService) ctx.getBean("userService");
            userService.login();

            Spring工厂重量级资源 一个应用中 应该只创建一个工厂
         */

        UserService userService = (UserService) ctx.getBean("userService");
        userService.login("suns", "123456");
    }

    @Override
    public boolean login(String name, String password) {
        System.out.println("UserServiceImpl.login");
        return true;
    }
}

第二十四章、AOP阶段知识总结

image-20200503162625116

百知教育 — Spring系列课程 — MVC框架整合


第二十五章、MVC框架整合思想

1. 搭建Web运行环境
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>3.1.0</version>
  <scope>provided</scope>
</dependency>

<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>jstl</artifactId>
  <version>1.2</version>
</dependency>

<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
<dependency>
  <groupId>javax.servlet.jsp</groupId>
  <artifactId>javax.servlet.jsp-api</artifactId>
  <version>2.3.1</version>
  <scope>provided</scope>
</dependency>

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>5.1.14.RELEASE</version>
</dependency>

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-core</artifactId>
  <version>5.1.14.RELEASE</version>
</dependency>

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-beans</artifactId>
  <version>5.1.14.RELEASE</version>
</dependency>


<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-tx</artifactId>
  <version>5.1.14.RELEASE</version>
</dependency>

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-jdbc</artifactId>
  <version>5.1.14.RELEASE</version>
</dependency>

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis-spring</artifactId>
  <version>2.0.2</version>
</dependency>

<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>1.1.18</version>
</dependency>

<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.48</version>
</dependency>

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>3.4.6</version>
</dependency>

<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.11</version>
  <scope>test</scope>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.1.4.RELEASE</version>
</dependency>

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
  <version>5.1.14.RELEASE</version>
</dependency>

<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjrt</artifactId>
  <version>1.8.8</version>
</dependency>

<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.8.3</version>
</dependency>

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>1.7.25</version>
</dependency>

<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>
2. 为什么要整合MVC框架
1. MVC框架提供了控制器(Controller)调用Service
   DAO ---》 Service 
2. 请求响应的处理
3. 接受请求参数 request.getParameter("")
4. 控制程序的运行流程
5. 视图解析 (JSP JSON Freemarker Thyemeleaf )
3. Spring可以整合那些MVC框架
1. struts1 
2. webwork
3. jsf
4. struts2
5. springMVC 
4.Spring整合MVC框架的核心思路
1. 准备工厂
1. Web开发过程中如何创建工厂
      ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
                                   WebXmlApplicationContext()
2. 如何保证工厂唯一同时被共用
   被共用:Web request|session|ServletContext(application)
   工厂存储在ServletContext这个作用域中 ServletContext.setAttribute("xxxx",ctx);
   
   唯一:ServletContext对象 创建的同时 ---》 ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
       
        ServletContextListener ---> ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
        ServletContextListener 在ServletContext对象创建的同时,被调用(只会被调用一次) ,把工厂创建的代码,写在ServletContextListener中,也会保证只调用
        一次,最终工厂就保证了唯一性
 3. 总结
      ServletContextListener(唯一)
             ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
             ServletContext.setAttribute("xxx",ctx) (共用)
             
 4. Spring封装了一个ContextLoaderListener 
     1. 创建工厂
     2. 把工厂存在ServletContext中
ContextLoaderListener使用方式 

web.xml

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>

2. 代码整合
依赖注入:把Sevice对象注入个控制器对象。

image-20200520143653347

第二十六章、Spring与Struts2框架整合 (选学)

1. Spring与Struts2整合思路分析
1. Struts2中的Action需要通过Spring的依赖注入获得Service对象。
2. Spring与Struts2整合的编码实现
  • 搭建开发环境

    • 引入相关jar (Spring Struts2)

      <dependency>
        <groupId>org.apache.struts</groupId>
        <artifactId>struts2-spring-plugin</artifactId>
        <version>2.3.8</version>
      </dependency>
      
    • 引入对应的配置文件

      • applicationContext.xml
      • struts.xml
      • log4j.properties
    • 初始化配置

      <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
      
      <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
      </context-param>
      
      <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
      </filter>
      <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
      </filter-mapping>
      
      • Spring (ContextLoaderListener —> Web.xml)
      • Struts2(Filter —> Web.xml)
  • 编码

    • 开发Service对象

      最终在Spring配置文件中创建Service对象
      <bean id="userService" class="com.baizhi.struts2.UserServiceImpl"/>
      
    • 开发Action对象

      • 开发类

        public class RegAction implements Action{
           private UserService userService;
           set get
             
           public String execute(){
               userService.register();
               return Action.SUCCESS;
           }
        }
        
      • Spring (applicationContext.xml)

        <bean id="regAction" class="com.baizhi.struts2.RegAction" scope="prototype">
            <property name="userService" ref=""/>
        </bean
        
      • Struts2(struts.xml)

        <package name="ssm" extends="struts-default">
          url reg.action  ---> 会接受到用户的请求后,创建RegAction这个类的对象 进行相应的处理
          
          <action name="reg" class="regAction">
            <result name="success">/index.jsp</result>
          </action>
        </package>
        
3. Spring+Struts2+Mybatis整合(SSM)
1. 思路分析
SSM = Spring+Struts2  Spring+Mybatis

image-20200520172752717

2. 整合编码
  • 搭建开发环境

    • 引入相关jar (Spring Struts2 Mybatis)

          <dependency>
            <groupId>org.apache.struts</groupId>
            <artifactId>struts2-spring-plugin</artifactId>
            <version>2.3.8</version>
          </dependency>
      
          <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
          </dependency>
      
          <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
          </dependency>
      
          <!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
          <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.1</version>
            <scope>provided</scope>
          </dependency>
      
          <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.1.14.RELEASE</version>
          </dependency>
      
          <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.1.14.RELEASE</version>
          </dependency>
      
          <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.1.14.RELEASE</version>
          </dependency>
      
      
          <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.1.14.RELEASE</version>
          </dependency>
      
          <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.14.RELEASE</version>
          </dependency>
      
          <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.2</version>
          </dependency>
      
          <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.18</version>
          </dependency>
      
          <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.48</version>
          </dependency>
      
          <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
          </dependency>
      
          <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
          </dependency>
      
          <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
          <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.4.RELEASE</version>
          </dependency>
      
          <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.1.14.RELEASE</version>
          </dependency>
      
          <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.8</version>
          </dependency>
      
          <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.3</version>
          </dependency>
      
          <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.25</version>
          </dependency>
      
          <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
          </dependency>
      
      
      
        
      
    • 引入对应的配置文件

      • applicationContext.xml
      • struts.xml
      • log4j.properties
      • xxxxMapper.xml
    • 初始化配置

      • Spring (ContextLoaderListener —> Web.xml)

      • Struts2(Filter —> Web.xml)

      <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
      
      <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
      </context-param>
      
      <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
      </filter>
      <filter-mapping>
      <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
      </filter-mapping>
      
  • 编码

    • DAO (Spring+Mybatis)

      1. 配置文件的配置
         1. DataSource
         2. SqlSessionFactory ----> SqlSessionFactoryBean
            1. dataSource
            2. typeAliasesPackage
            3. mapperLocations 
         3. MapperScannerConfigur ---> DAO接口实现类
      2. 编码
         1. entity 
         2. table
         3. DAO接口
         4. 实现Mapper文件
      
    • Service (Spring添加事务)

      1. 原始对象 ---》 注入DAO
      2. 额外功能 ---》 DataSourceTransactionManager ---> dataSource
      3. 切入点 + 事务属性
         @Transactional(propagation,readOnly...)
      4. 组装切面
         <tx:annotation-driven 
      
    • Controller (Spring+Struts2)

      1. 开发控制器 implements Action 注入Service
      2. Spring的配置文件 
          1. 注入 Service
          2. scope = prototype
      3. struts.xml
          <action class="spring配置文件中action对应的id值"/>
      
4. Spring开发过程中多配置文件的处理
Spring会根据需要,把配置信息分门别类的放置在多个配置文件中,便于后续的管理及维护。

DAO  ------  applicationContext-dao.xml 
Service ---  applicationContext-service.xml
Action  ---  applicationContext-action.xml

注意:虽然提供了多个配置文件,但是后续应用的过程中,还要进行整合
  • 通配符方式

    1. 非web环境
       ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext-*.xml");
    2. web环境
       <context-param>
           <param-name>contextConfigLocation</param-name>
           <param-value>classpath:applicationContext-*.xml</param-value>
       <context-param>
    
  • <import标签

    applicationContext.xml 目的 整合其他配置内容
        <import resource="applicationContext-dao.xml " />
        <import resource="applicationContext-service.xml " />
        <import resource="applicationContext-action.xml " />
        
    1. 非web环境
       ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
    2. web环境
       <context-param>
           <param-name>contextConfigLocation</param-name>
           <param-value>classpath:applicationContext.xml</param-value>
       <context-param>
        
    

第二十七章、持久层整合

1.Spring框架为什么要与持久层技术进行整合
1. JavaEE开发需要持久层进行数据库的访问操作。
2. JDBC Hibernate MyBatis进行持久开发过程存在大量的代码冗余
3. Spring基于模板设计模式对于上述的持久层技术进行了封装
2. Spring可以与那些持久层技术进行整合?
1. JDBC
     |-  JDBCTemplate 
2. Hibernate (JPA)
     |-  HibernateTemplate
3. MyBatis
     |-  SqlSessionFactoryBean MapperScannerConfigure 

第二十八章、Spring与MyBatis整合

1. MyBatis开发步骤的回顾
1. 实体
2. 实体别名  
3. 表
4. 创建DAO接口
5. 实现Mapper文件
6. 注册Mapper文件
7. MybatisAPI调用
2. Mybatis在开发过程中存在问题
配置繁琐  代码冗余 

1. 实体
2. 实体别名         配置繁琐 
3. 表
4. 创建DAO接口
5. 实现Mapper文件
6. 注册Mapper文件   配置繁琐 
7. MybatisAPI调用  代码冗余 
3. Spring与Mybatis整合思路分析

image-20200504141407141

4. Spring与Mybatis整合的开发步骤
  • 配置文件(ApplicationContext.xml) 进行相关配置

    #配置 是需要配置一次 
    <bean id="dataSource" class=""/> 
    
    <!--创建SqlSessionFactory-->
    <bean id="ssfb" class="SqlSessionFactoryBean">
        <property name="dataSource" ref=""/>
        <property name="typeAliasesPackage">
             指定 实体类所在的包  com.baizhiedu.entity  User
                                                     Product
        </property>
        <property name="mapperLocations">
              指定 配置文件(映射文件)的路径 还有通用配置 
              com.baizhiedu.mapper/*Mapper.xml 
        </property>
    </bean>
    
    <!--DAO接口的实现类
        session ---> session.getMapper() --- xxxDAO实现类对象 
        XXXDAO  ---> xXXDAO
    -->
    <bean id="scanner" class="MapperScannerConfigure">
        <property name="sqlSessionFactoryBeanName" value="ssfb"/>
        <property name="basePacakge">
            指定 DAO接口放置的包  com.baizhiedu.dao 
        </property>
    </bean>
    
  • 编码

    # 实战经常根据需求 写的代码
    1. 实体
    2. 表
    3. 创建DAO接口
    4. 实现Mapper文件
    
5. Spring与Mybatis整合编码
  • 搭建开发环境(jar)

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.1.14.RELEASE</version>
    </dependency>
    
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>2.0.2</version>
    </dependency>
    
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.18</version>
    </dependency>
    
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.48</version>
    </dependency>
    
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.6</version>
    </dependency>
    
  • Spring配置文件的配置

    <!--连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
      <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
      <property name="url" value="jdbc:mysql://localhost:3306/suns?useSSL=false"></property>
      <property name="username" value="root"></property>
      <property name="password" value="123456"></property>
    </bean>
    
    <!--创建SqlSessionFactory SqlSessionFactoryBean-->
    <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
      <property name="dataSource" ref="dataSource"></property>
      <property name="typeAliasesPackage" value="com.baizhiedu.entity"></property>
      <property name="mapperLocations">
        <list>
          <value>classpath:com.baizhiedu.mapper/*Mapper.xml</value>
        </list>
      </property>
    </bean>
    
    <!--创建DAO对象 MapperScannerConfigure-->
    
    <bean id="scanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
      <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"></property>
      <property name="basePackage" value="com.baizhiedu.dao"></property>
    </bean>
    
  • 编码

    1. 实体
    2. 表
    3. DAO接口
    4. Mapper文件配置
    
6. Spring与Mybatis整合细节
  • 问题:Spring与Mybatis整合后,为什么DAO不提交事务,但是数据能够插入数据库中?

    Connection --> tx
    Mybatis(Connection)
    
    本质上控制连接对象(Connection) ---> 连接池(DataSource)
    1. Mybatis提供的连接池对象 ---> 创建Connection
         Connection.setAutoCommit(false) 手工的控制了事务 , 操作完成后,手工提交
    2. Druid(C3P0 DBCP)作为连接池        ---> 创建Connection
         Connection.setAutoCommit(true) true默认值 保持自动控制事务,一条sql 自动提交 
    答案:因为Spring与Mybatis整合时,引入了外部连接池对象,保持自动的事务提交这个机制(Connection.setAutoCommit(true)),不需要手工进行事务的操作,也能进行事务的提交 
    
    注意:未来实战中,还会手工控制事务(多条sql一起成功,一起失败),后续Spring通过事务控制解决这个问题。
    

第二十九章、Spring的事务处理

1. 什么是事务?
保证业务操作完整性的一种数据库机制

事务的4特点: A C I D
1. A 原子性
2. C 一致性
3. I 隔离性
4. D 持久性
2. 如何控制事务
JDBC:
    Connection.setAutoCommit(false);
    Connection.commit();
    Connection.rollback();
Mybatis:
    Mybatis自动开启事务
    
    sqlSession(Connection).commit();
    sqlSession(Connection).rollback();

结论:控制事务的底层 都是Connection对象完成的。
3.Spring控制事务的开发
Spring是通过AOP的方式进行事务开发
1. 原始对象
public class XXXUserServiceImpl{
   private xxxDAO xxxDAO
   set get

   1. 原始对象 ---》 原始方法 ---》核心功能 (业务处理+DAO调用)
   2. DAO作为Service的成员变量,依赖注入的方式进行赋值
}
2. 额外功能
1. org.springframework.jdbc.datasource.DataSourceTransactionManager
2. 注入DataSource 
1. MethodInterceptor
   public Object invoke(MethodInvocation invocation){
      try{
        Connection.setAutoCommit(false);
        Object ret = invocation.proceed();
        Connection.commit();
      }catch(Exception e){
        Connection.rollback();
      }
        return ret;
   }
2. @Aspect
   @Around 
3. 切入点
@Transactional 
事务的额外功能加入给那些业务方法。

1. 类上:类中所有的方法都会加入事务
2. 方法上:这个方法会加入事务
4 组装切面
1. 切入点
2. 额外功能

<tx:annotation-driven transaction-manager=""/>
4. Spring控制事务的编码
  • 搭建开发环境 (jar)

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>5.1.14.RELEASE</version>
    </dependency>
    
  • 编码

    <bean id="userService" class="com.baizhiedu.service.UserServiceImpl">
      <property name="userDAO" ref="userDAO"/>
    </bean>
    
    <!--DataSourceTransactionManager-->
    <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <property name="dataSource" ref="dataSource"/>
    </bean>
    
    @Transactional
    public class UserServiceImpl implements UserService {
        private UserDAO userDAO;
    
    <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
    
  • 细节

    <tx:annotation-driven transaction-manager="dataSourceTransactionManager" proxy-target-class="true"/>
    进行动态代理底层实现的切换   proxy-target-class
        默认 false JDK
            true  Cglib 
    

第三十章、 Spring中的事务属性(Transaction Attribute)

1. 什么是事务属性
属性:描述物体特征的一系列值
     性别 身高 体重 ...
事务属性:描述事务特征的一系列值 
1. 隔离属性
2. 传播属性
3. 只读属性
4. 超时属性
5. 异常属性 
2. 如何添加事务属性
@Transactional(isloation=,propagation=,readOnly=,timeout=,rollbackFor=,noRollbackFor=,)
3. 事务属性详解
1. 隔离属性 (ISOLATION)
  • 隔离属性的概念

    概念:他描述了事务解决并发问题的特征
    1. 什么是并发
           多个事务(用户)在同一时间,访问操作了相同的数据
           
           同一时间:0.000几秒 微小前 微小后
    2. 并发会产生那些问题
           1. 脏读
           2. 不可重复读
           3. 幻影读
    3. 并发问题如何解决
           通过隔离属性解决,隔离属性中设置不同的值,解决并发处理过程中的问题。
    
  • 事务并发产生的问题

    • 脏读

      一个事务,读取了另一个事务中没有提交的数据。会在本事务中产生数据不一致的问题
      解决方案  @Transactional(isolation=Isolation.READ_COMMITTED)
      
    • 不可重复读

      一个事务中,多次读取相同的数据,但是读取结果不一样。会在本事务中产生数据不一致的问题
      注意:1 不是脏读 2 一个事务中
      解决方案 @Transactional(isolation=Isolation.REPEATABLE_READ)
      本质: 一把行锁
      
    • 幻影读

      一个事务中,多次对整表进行查询统计,但是结果不一样,会在本事务中产生数据不一致的问题
      解决方案 @Transactional(isolation=Isolation.SERIALIZABLE)
      本质:表锁 
      
    • 总结

      并发安全: SERIALIZABLE>REPEATABLE_READ>READ_COMMITTED
      运行效率: READ_COMMITTED>REPEATABLE_READ>SERIALIZABLE
      
  • 数据库对于隔离属性的支持

    隔离属性的值 MySQL Oracle
    ISOLATION_READ_COMMITTED
    IOSLATION_REPEATABLE_READ
    ISOLATION_SERIALIZABLE
    Oracle不支持REPEATABLE_READ值 如何解决不可重复读
    采用的是多版本比对的方式 解决不可重复读的问题
    
  • 默认隔离属性

    ISOLATION_DEFAULT:会调用不同数据库所设置的默认隔离属性
    
    MySQL : REPEATABLE_READ 
    Oracle: READ_COMMITTED  
    
    • 查看数据库默认隔离属性

      • MySQL

        select @@tx_isolation;
        
      • Oracle

        SELECT s.sid, s.serial#,
           CASE BITAND(t.flag, POWER(2, 28))
              WHEN 0 THEN 'READ COMMITTED'
              ELSE 'SERIALIZABLE'
           END AS isolation_level
        FROM v$transaction t 
        JOIN v$session s ON t.addr = s.taddr
        AND s.sid = sys_context('USERENV', 'SID');
        
  • 隔离属性在实战中的建议

    推荐使用Spring指定的ISOLATION_DEFAULT
     1. MySQL   repeatable_read
     2. Oracle  read_commited 
    
    未来实战中,并发访问情况 很低 
    
    如果真遇到并发问题,乐观锁 
       Hibernate(JPA)  Version 
       MyBatis         通过拦截器自定义开发
     
    
2. 传播属性(PROPAGATION)
  • 传播属性的概念

    概念:他描述了事务解决嵌套问题的特征
    
    什么叫做事务的嵌套:他指的是一个大的事务中,包含了若干个小的事务
    
    问题:大事务中融入了很多小的事务,他们彼此影响,最终就会导致外部大的事务,丧失了事务的原子性
    
  • 传播属性的值及其用法

    传播属性的值 外部不存在事务 外部存在事务 用法 备注
    REQUIRED 开启新的事务 融合到外部事务中 @Transactional(propagation = Propagation.REQUIRED) 增删改方法
    SUPPORTS 不开启事务 融合到外部事务中 @Transactional(propagation = Propagation.SUPPORTS) 查询方法
    REQUIRES_NEW 开启新的事务 挂起外部事务,创建新的事务 @Transactional(propagation = Propagation.REQUIRES_NEW) 日志记录方法中
    NOT_SUPPORTED 不开启事务 挂起外部事务 @Transactional(propagation = Propagation.NOT_SUPPORTED) 及其不常用
    NEVER 不开启事务 抛出异常 @Transactional(propagation = Propagation.NEVER) 及其不常用
    MANDATORY 抛出异常 融合到外部事务中 @Transactional(propagation = Propagation.MANDATORY) 及其不常用
  • 默认的传播属性

    REQUIRED是传播属性的默认值
    
  • 推荐传播属性的使用方式

    增删改 方法:直接使用默认值REQUIRED 
    查询   操作:显示指定传播属性的值为SUPPORTS  
    
3. 只读属性(readOnly)
针对于只进行查询操作的业务方法,可以加入只读属性,提供运行效率

默认值:false 
4. 超时属性(timeout)
指定了事务等待的最长时间

1. 为什么事务进行等待?
   当前事务访问数据时,有可能访问的数据被别的事务进行加锁的处理,那么此时本事务就必须进行等待。
2. 等待时间 秒
3. 如何应用 @Transactional(timeout=2)
4. 超时属性的默认值 -1 
   最终由对应的数据库来指定
5. 异常属性
Spring事务处理过程中
默认 对于RuntimeException及其子类 采用的是回滚的策略
默认 对于Exception及其子类 采用的是提交的策略

rollbackFor = {java.lang.Exception,xxx,xxx} 
noRollbackFor = {java.lang.RuntimeException,xxx,xx}

@Transactional(rollbackFor = {java.lang.Exception.class},noRollbackFor = {java.lang.RuntimeException.class})

建议:实战中使用RuntimeExceptin及其子类 使用事务异常属性的默认值
4. 事务属性常见配置总结
1. 隔离属性   默认值 
2. 传播属性   Required(默认值) 增删改   Supports 查询操作
3. 只读属性   readOnly false  增删改   true 查询操作
4. 超时属性   默认值 -1
5. 异常属性   默认值 

增删改操作   @Transactional
查询操作     @Transactional(propagation=Propagation.SUPPORTS,readOnly=true)
5. 基于标签的事务配置方式(事务开发的第二种形式)
基于注解 @Transaction的事务配置回顾
<bean id="userService" class="com.baizhiedu.service.UserServiceImpl">
  <property name="userDAO" ref="userDAO"/>
</bean>

<!--DataSourceTransactionManager-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"/>
</bean>

@Transactional(isolation=,propagation=,...)
public class UserServiceImpl implements UserService {
    private UserDAO userDAO;

<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>

基于标签的事务配置
<bean id="userService" class="com.baizhiedu.service.UserServiceImpl">
  <property name="userDAO" ref="userDAO"/>
</bean>

<!--DataSourceTransactionManager-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"/>
</bean>

事务属性 
<tx:advice id="txAdvice" transacation-manager="dataSourceTransactionManager">
    <tx:attributes>
          <tx:method name="register" isoloation="",propagation=""></tx:method>
          <tx:method name="login" .....></tx:method>
          等效于 
          @Transactional(isolation=,propagation=,)
          public void register(){
        
          }
      
    </tx:attributes>
</tx:advice>

<aop:config>
     <aop:pointcut id="pc" expression="execution(* com.baizhiedu.service.UserServiceImpl.register(..))"></aop:pointcut>
     <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"></aop:advisor>
</aop:config>
  • 基于标签的事务配置在实战中的应用方式

    <bean id="userService" class="com.baizhiedu.service.UserServiceImpl">
      <property name="userDAO" ref="userDAO"/>
    </bean>
    
    <!--DataSourceTransactionManager-->
    <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <property name="dataSource" ref="dataSource"/>
    </bean>
    
    编程时候 service中负责进行增删改操作的方法 都以modify开头
                           查询操作 命名无所谓 
    <tx:advice id="txAdvice" transacation-manager="dataSourceTransactionManager">
        <tx:attributes>
              <tx:method name="register"></tx:method>
              <tx:method name="modify*"></tx:method>
              <tx:method name="*" propagation="SUPPORTS"  read-only="true"></tx:method>
        </tx:attributes>
    </tx:advice>
    
    应用的过程中,service放置到service包中
    <aop:config>
         <aop:pointcut id="pc" expression="execution(* com.baizhiedu.service..*.*(..))"></aop:pointcut>
         <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"></aop:advisor>
    </aop:config>
    

第三十一章、注解基础概念

1. 什么是注解编程
指的是在类或者方法上加入特定的注解(@XXX),完成特定功能的开发。
  
	@Component
public class XXX{}
2. 为什么要讲解注解编程
1. 注解开发方便
     代码简洁 开发速度大大提高
2. Spring开发潮流
     Spring2.x引入注解  Spring3.x完善注解 SpringBoot普及 推广注解编程 
3. 注解的作用
  • 替换XML这种配置形式,简化配置

    image-20200527164703807

  • 替换接口,实现调用双方的契约性

    通过注解的方式,在功能调用者和功能提供者之间达成约定,进而进行功能的调用。因为注解应用更为方便灵活,所以在现在的开发中,更推荐通过注解的形式,完成
    

    image-20200527171704953

4. Spring注解的发展历程
1. Spring2.x开始支持注解编程 @Component @Service @Scope..
     目的:提供的这些注解只是为了在某些情况下简化XML的配置,作为XML开发的有益补充。
2. Spring3.x @Configuration @Bean..
     目的:彻底替换XML,基于纯注解编程
3. Spring4.x SpringBoot 
     提倡使用注解常见开发
5. Spring注解开发的一个问题
Spring基于注解进行配置后,还能否解耦合呢?

在Spring框架应用注解时,如果对注解配置的内容不满意,可以通过Spring配置文件进行覆盖的。

第三十二章、Spring的基础注解(Spring2.x)

这个阶段的注解,仅仅是简化XML的配置,并不能完全替代XML
1. 对象创建相关注解
  • 搭建开发环境

    <context:component-scan base-package="com.baizhiedu"/>
    
    作用:让Spring框架在设置包及其子包中扫描对应的注解,使其生效。
    
  • 对象创建相关注解

    • @Component

      作用:替换原有spring配置文件中的<bean标签 
      注意:
          id属性 component注解 提供了默认的设置方式  首单词首字母小写
          class属性 通过反射获得class内容 
      

      image-20200528112232356

    • @Component 细节

      • 如何显示指定工厂创建对象的id值

        @Component("u")
        
      • Spring配置文件覆盖注解配置内容

        applicationContext.xml
        
        <bean id="u" class="com.baizhiedu.bean.User"/>
        
        id值 class的值 要和 注解中的设置保持一值 
        
    • @Component的衍生注解

      @Repository  --->  XXXDAO
        @Repository
        public class UserDAO{
        
        }
      @Service
        @Service
        public class UserService{
        
        }
      @Controller 
        @Controller 
        public class RegAction{
        
        }
      注意:本质上这些衍生注解就是@Component 
           作用 <bean  
           细节 @Service("s")
      
      目的:更加准确的表达一个类型的作用
      
      注意:Spring整合Mybatis开发过程中 不使用@Repository @Component
           
      
  • @Scope注解

    作用:控制简单对象创建次数
    注意:不添加@Scope Spring提供默认值 singleton
    <bean id="" class="" scope="singleton|prototype"/>
    
  • @Lazy注解

    作用:延迟创建单实例对象
    注意:一旦使用了@Lazy注解后,Spring会在使用这个对象时候,进行这个对象的创建
    <bean id="" class="" lazy="false"/>
    
  • 生命周期方法相关注解

    1. 初始化相关方法 @PostConstruct
       InitializingBean
       <bean init-method=""/>
    2. 销毁方法 @PreDestroy
       DisposableBean
       <bean destory-method=""/>
    注意:1. 上述的2个注解并不是Spring提供的,JSR(JavaEE规范)520
         2. 再一次的验证,通过注解实现了接口的契约性
    
2. 注入相关注解
  • 用户自定义类型 @Autowired

    image-20200601114751016

    @Autowired细节
    1. Autowired注解基于类型进行注入 [推荐]
       基于类型的注入:注入对象的类型,必须与目标成员变量类型相同或者是其子类(实现类)
    
    2. Autowired Qualifier 基于名字进行注入 [了解]
       基于名字的注入:注入对象的id值,必须与Qualifier注解中设置的名字相同
    
    3. Autowired注解放置位置 
        a) 放置在对应成员变量的set方法上 
        b) 直接把这个注解放置在成员变量之上,Spring通过反射直接对成员变量进行注入(赋值)[推荐]
    
    4. JavaEE规范中类似功能的注解
        JSR250 @Resouce(name="userDAOImpl") 基于名字进行注入
               @Autowired()
               @Qualifier("userDAOImpl")
               注意:如果在应用Resource注解时,名字没有配对成功,那么他会继续按照类型进行注入。
        JSR330 @Inject 作用 @Autowired完全一致 基于类型进行注入 ---》 EJB3.0
              <dependency>
                <groupId>javax.inject</groupId>
                <artifactId>javax.inject</artifactId>
                <version>1</version>
              </dependency>
    
  • JDK类型

    @Value注解完成
    1. 设置xxx.properties 
       id = 10
       name = suns
    2. Spring的工厂读取这个配置文件 
       <context:property-placeholder location=""/>
    3. 代码 
       属性 @Value("${key}")
    
    • @PropertySource

      1. 作用:用于替换Spring配置文件中的<context:property-placeholder location=""/>标签
      2. 开发步骤 
          1. 设置xxx.properties 
             id = 10
             name = suns
          2. 应用@PropertySource
          3. 代码
             属性 @Value()
      
    • @Value注解使用细节

      • @Value注解不能应用在静态成员变量上

        如果应用,赋值(注入)失败
        
      • @Value注解+Properties这种方式,不能注入集合类型

        Spring提供新的配置形式 YAML YML (SpringBoot)
        
3. 注解扫描详解
<context:component-scan base-package="com.baizhiedu"/>
当前包 及其 子包 
1. 排除方式
<context:component-scan base-package="com.baizhiedu">
   <context:exclude-filter type="" expression=""/>
   type:assignable:排除特定的类型 不进行扫描
        annotation:排除特定的注解 不进行扫描
        aspectj:切入点表达式
                包切入点: com.baizhiedu.bean..*
                类切入点: *..User
        regex:正则表达式 
        custom:自定义排除策略框架底层开发
</context:component-scan>

排除策略可以叠加使用 
<context:component-scan base-package="com.baizhiedu">
  <context:exclude-filter type="assignable" expression="com.baizhiedu.bean.User"/>

  <context:exclude-filter type="aspectj" expression="com.baizhiedu.injection..*"/>
</context:component-scan>
2. 包含方式
<context:component-scan base-package="com.baizhiedu" use-default-filters="false">
   <context:include-filter type="" expression=""/>
</context:component-scan>

1. use-default-filters="false"
   作用:让Spring默认的注解扫描方式 失效。
2. <context:include-filter type="" expression=""/>
   作用:指定扫描那些注解 
   type:assignable:排除特定的类型 不进行扫描
        annotation:排除特定的注解 不进行扫描
        aspectj:切入点表达式
                包切入点: com.baizhiedu.bean..*
                类切入点: *..User
        regex:正则表达式 
        custom:自定义排除策略框架底层开发

包含的方式支持叠加
 <context:component-scan base-package="com.baizhiedu" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
 </context:component-scan>
4. 对于注解开发的思考
  • 配置互通

    Spring注解配置 配置文件的配置 互通
    
    @Repository
    public class UserDAOImpl{
    
    
    }
    
    public class UserServiceImpl{
       private UserDAO userDAO;
       set get
    }
    
    <bean id="userService" class="com.baizhiedu.UserServiceImpl">
       <property name="userDAO" ref="userDAOImpl"/>
    </bean>
    
  • 什么情况下使用注解 什么情况下使用配置文件

    @Component 替换 <bean 
    
    基础注解(@Component @Autowired @Value) 程序员开发类型的配置
    
    1. 在程序员开发的类型上 可以加入对应注解 进行对象的创建 
       User  UserService  UserDAO  UserAction 
    
    2. 应用其他非程序员开发的类型时,还是需要使用<bean 进行配置的
       SqlSessionFactoryBean  MapperScannerConfigure 
    
5. SSM整合开发(半注解开发)
  • 搭建开发环境

    • 引入相关jar 【SSM POM】
    • 引入相关配置文件
      • applicationContext.xml
      • struts.xml
      • log4.properties
      • XXXMapper.xml
    • 初始化配置
      • Web.xml Spring (ContextLoaderListener)
      • Web.xml Struts Filter
  • 编码

    <context:component-scan base-package=""/>
    
    • DAO (Spring+Mybatis)

      1. 配置文件的配置
         1. DataSource
         2. SqlSessionFactory ----> SqlSessionFactoryBean
            1. dataSource
            2. typeAliasesPackage
            3. mapperLocations 
         3. MapperScannerConfigur ---> DAO接口实现类
      2. 编码
         1. entity 
         2. table
         3. DAO接口
         4. 实现Mapper文件
      
    • Service

      1. 原始对象 ---》 注入DAO
         @Service ---> @Autowired
      
      2. 额外功能 ---》 DataSourceTransactionManager ---> dataSource
      3. 切入点 + 事务属性
         @Transactional(propagation,readOnly...)
      4. 组装切面
         <tx:annotation-driven 
      
    • Controller (Spring+Struts2)

      1. @Controller
         @Scope("prototype")
         public class RegAction implements Action{
            @Autowired
            private UserService userServiceImpl;
         
         }
      2. struts.xml
          <action class="spring配置文件中action对应的id值"/>
      

第三十三章、Spring的高级注解(Spring3.x 及以上)

1. 配置Bean
Spring在3.x提供的新的注解,用于替换XML配置文件。
  
  @Configuration
public class AppConfig{
  
}
  1. 配置Bean在应用的过程中 替换了XML具体什么内容呢?

    image-20200703100033265

  2. AnnotationConfigApplicationContext

    1. 创建工厂代码
       ApplicationContext ctx = new AnnotationConfigApplicationContext();
    2. 指定配置文件 
       1. 指定配置bean的Class
           ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
       2. 指定配置bean所在的路径 
           ApplicationContext ctx = new AnnotationConfigApplicationContext("com.baizhiedu");
    
  • 配置Bean开发的细节分析

    • 基于注解开发使用日志

      不能集成Log4j
      集成logback 
      
      • 引入相关jar

         <dependency>
              <groupId>org.slf4j</groupId>
              <artifactId>slf4j-api</artifactId>
              <version>1.7.25</version>
            </dependency>
        
            <dependency>
              <groupId>org.slf4j</groupId>
              <artifactId>jcl-over-slf4j</artifactId>
              <version>1.7.25</version>
            </dependency>
        
            <dependency>
              <groupId>ch.qos.logback</groupId>
              <artifactId>logback-classic</artifactId>
              <version>1.2.3</version>
            </dependency>
        
            <dependency>
              <groupId>ch.qos.logback</groupId>
              <artifactId>logback-core</artifactId>
              <version>1.2.3</version>
            </dependency>
        
            <dependency>
              <groupId>org.logback-extensions</groupId>
              <artifactId>logback-ext-spring</artifactId>
              <version>0.1.4</version>
            </dependency>
        
      • 引入logback配置文件 (logback.xml)

        <?xml version="1.0" encoding="UTF-8"?>
        <configuration>
            <!-- 控制台输出 -->
            <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
                <encoder>
                    <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
                    <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
                </encoder>
            </appender>
        
            <root level="DEBUG">
                <appender-ref ref="STDOUT" />
            </root>
        
        </configuration>
        
    • @Configuration注解的本质

      本质:也是@Component注解的衍生注解
      
      可以应用<context:component-scan进行扫描
      
2. @Bean注解
@Bean注解在配置bean中进行使用,等同于XML配置文件中的<bean标签
1. @Bean注解的基本使用
  • 对象的创建

    image-20200703150632630

    1. 简单对象
       直接能够通过new方式创建的对象 
       User  UserService   UserDAO 
    2. 复杂对象
       不能通过new的方式直接创建的对象
       Connection SqlSessionFactory
    
    • @Bean注解创建复杂对象的注意事项

      遗留系统整合 
      @Bean
      public Connection conn1() {
        Connection conn = null;
        try {
          ConnectionFactoryBean factoryBean = new ConnectionFactoryBean();
          conn = factoryBean.getObject();
        } catch (Exception e) {
          e.printStackTrace();
        }
        return conn;
      }
      
  • 自定义id值

    @Bean("id")
    
  • 控制对象创建次数

    @Bean
    @Scope("singleton|prototype") 默认值 singleton
    
2. @Bean注解的注入
  • 用户自定义类型

    @Bean
    public UserDAO userDAO() {
      return new UserDAOImpl();
    }
    
    @Bean
    public UserService userService(UserDAO userDAO) {
      UserServiceImpl userService = new UserServiceImpl();
      userService.setUserDAO(userDAO);
      return userService;
    }
    
    //简化写法
    @Bean
    public UserService userService() {
      UserServiceImpl userService = new UserServiceImpl();
      userService.setUserDAO(userDAO());
      return userService;
    }
    
  • JDK类型的注入

    @Bean
    public Customer customer() {
      Customer customer = new Customer();
      customer.setId(1);
      customer.setName("xiaohei");
    
      return customer;
    }
    
    • JDK类型注入的细节分析

      如果直接在代码中进行set方法的调用,会存在耦合的问题 
      
      @Configuration
      @PropertySource("classpath:/init.properties")
      public class AppConfig1 {
      
          @Value("${id}")
          private Integer id;
          @Value("${name}")
          private String name;
       
          @Bean
          public Customer customer() {
              Customer customer = new Customer();
              customer.setId(id);
              customer.setName(name);
      
              return customer;
          }
      }
      
3. @ComponentScan注解
@ComponentScan注解在配置bean中进行使用,等同于XML配置文件中的<context:component-scan>标签

目的:进行相关注解的扫描 (@Component @Value ...@Autowired)
1. 基本使用
@Configuration
@ComponentScan(basePackages = "com.baizhiedu.scan")
public class AppConfig2 {

}

<context:component-scan base-package=""/>
2. 排除、包含的使用
  • 排除

    <context:component-scan base-package="com.baizhiedu">
      <context:exclude-filter type="assignable" expression="com.baizhiedu.bean.User"/>
    </context:component-scan>
    
    @ComponentScan(basePackages = "com.baizhiedu.scan",
                   excludeFilters = {@ComponentScan.Filter(type= FilterType.ANNOTATION,value={Service.class}),
                                     @ComponentScan.Filter(type= FilterType.ASPECTJ,pattern = "*..User1")})
    
    type = FilterType.ANNOTATION          value
                     .ASSIGNABLE_TYPE     value
                     .ASPECTJ             pattern   
                     .REGEX               pattern
                     .CUSTOM              value
    
  • 包含

    <context:component-scan base-package="com.baizhiedu" use-default-filters="false">
       <context:include-filter type="" expression=""/>
    </context:component-scan>
    
    @ComponentScan(basePackages = "com.baizhiedu.scan",
                   useDefaultFilters = false,
                   includeFilters = {@ComponentScan.Filter(type= FilterType.ANNOTATION,value={Service.class})})
    
    type = FilterType.ANNOTATION          value
                     .ASSIGNABLE_TYPE     value
                     .ASPECTJ             pattern   
                     .REGEX               pattern
                     .CUSTOM              value
    
4. Spring工厂创建对象的多种配置方式
1. 多种配置方式的应用场景

image-20200706174301418

2. 配置优先级
@Component及其衍生注解 < @Bean < 配置文件bean标签
优先级高的配置 覆盖优先级低配置 

@Component
public class User{

}

@Bean
public User user(){
  return new User();
}

<bean id="user" class="xxx.User"/>

配置覆盖:id值 保持一致
  • 解决基于注解进行配置的耦合问题

    @Configuration
    //@ImportResource("applicationContext.xml")
    public class AppConfig4 {
    
        @Bean
        public UserDAO userDAO() {
            return new UserDAOImpl();
        }
    }
    
    @Configuration
    @ImportResource("applicationContext.xml")
    public class AppConfig5{
      
    }
    
    applicationContext.xml
    <bean id="userDAO" class="com.baizhiedu.injection.UserDAOImplNew"/>
    
5. 整合多个配置信息
  • 为什么会有多个配置信息
拆分多个配置bean的开发,是一种模块化开发的形式,也体现了面向对象各司其职的设计思想
  • 多配置信息整合的方式
    • 多个配置Bean的整合
    • 配置Bean与@Component相关注解的整合
    • 配置Bean与SpringXML配置文件的整合
  • 整合多种配置需要关注那些要点
    • 如何使多配置的信息 汇总成一个整体
    • 如何实现跨配置的注入
1. 多个配置Bean的整合
  • 多配置的信息汇总

    • base-package进行多个配置Bean的整合

      image-20200707170421669

    • @Import

      1. 可以创建对象
      2. 多配置bean的整合
      

      image-20200707170745814

    • 在工厂创建时,指定多个配置Bean的Class对象 【了解】

      ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig1.class,AppConfig2.class);
      
  • 跨配置进行注入

    在应用配置Bean的过程中,不管使用哪种方式进行配置信息的汇总,其操作方式都是通过成员变量加入@Autowired注解完成。
    @Configuration
    @Import(AppConfig2.class)
    public class AppConfig1 {
    
        @Autowired
        private UserDAO userDAO;
    
        @Bean
        public UserService userService() {
            UserServiceImpl userService = new UserServiceImpl();
            userService.setUserDAO(userDAO);
            return userService;
        }
    }
    
    @Configuration
    public class AppConfig2 {
    
        @Bean
        public UserDAO userDAO() {
            return new UserDAOImpl();
        }
    }
    
2. 配置Bean与@Component相关注解的整合
@Component(@Repository)
public class UserDAOImpl implements UserDAO{
  
}

@Configuration
@ComponentScan("")
public class AppConfig3 {
   
    @Autowired
    private UserDAO userDAO;

    @Bean
    public UserService userService() {
        UserServiceImpl userService = new UserServiceImpl();
        userService.setUserDAO(userDAO);
        return userService;
    }
}

ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig3.class);
3. 配置Bean与配置文件整合
1. 遗留系统的整合 2. 配置覆盖
  
public class UserDAOImpl implements UserDAO{
  
}
<bean id="userDAO" class="com.baizhiedu.injection.UserDAOImpl"/>

@Configuration
@ImportResource("applicationContext.xml")
public class AppConfig4 {
  
    @Autowired
    private UserDAO userDAO;

    @Bean
    public UserService userService() {
        UserServiceImpl userService = new UserServiceImpl();
        userService.setUserDAO(userDAO);
        return userService;
    }
}

ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig4.class);
6. 配置Bean底层实现原理
Spring在配置Bean中加入了@Configuration注解后,底层就会通过Cglib的代理方式,来进行对象相关的配置、处理

image-20200709114200371

7. 四维一体的开发思想
1. 什么是四维一体
Spring开发一个功能的4种形式,虽然开发方式不同,但是最终效果是一样的。
1. 基于schema
2. 基于特定功能注解
3. 基于原始<bean
4. 基于@Bean注解
2. 四维一体的开发案例
1. <context:property-placehoder
2. @PropertySource  【推荐】
3. <bean id="" class="PropertySourcePlaceholderConfigure"/>
4. @Bean            【推荐】
8. 纯注解版AOP编程
1. 搭建环境
1. 应用配置Bean 
2. 注解扫描
2. 开发步骤
1. 原始对象
   @Service(@Component)
   public class UserServiceImpl implements UserService{
     
   }
2. 创建切面类 (额外功能 切入点 组装切面)
    @Aspect
    @Component
    public class MyAspect {

        @Around("execution(* login(..))")
        public Object arround(ProceedingJoinPoint joinPoint) throws Throwable {

            System.out.println("----aspect log ------");

            Object ret = joinPoint.proceed();


            return ret;
        }
    }
3. Spring的配置文件中
   <aop:aspectj-autoproxy />
   @EnableAspectjAutoProxy ---> 配置Bean 
3. 注解AOP细节分析
1. 代理创建方式的切换 JDK Cglib 
   <aop:aspectj-autoproxy proxy-target-class=true|false />
   @EnableAspectjAutoProxy(proxyTargetClass)
2. SpringBoot AOP的开发方式
     @EnableAspectjAutoProxy 已经设置好了 
     
    1. 原始对象
     @Service(@Component)
     public class UserServiceImpl implements UserService{

     }
    2. 创建切面类 (额外功能 切入点 组装切面)
      @Aspect
      @Component
      public class MyAspect {

        @Around("execution(* login(..))")
        public Object arround(ProceedingJoinPoint joinPoint) throws Throwable {

          System.out.println("----aspect log ------");

          Object ret = joinPoint.proceed();


          return ret;
        }
      }
    Spring AOP 代理默认实现 JDK  SpringBOOT AOP 代理默认实现 Cglib 
9. 纯注解版Spring+MyBatis整合
  • 基础配置 (配置Bean)

    1. 连接池
      <!--连接池-->
      <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/suns?useSSL=false"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
      </bean>
       
       @Bean
       public DataSource dataSource(){
          DruidDataSource dataSource = new DruidDataSource();
          dataSource.setDriverClassName("");
          dataSource.setUrl();
          ...
          return dataSource;
       }
    
    2. SqlSessionFactoryBean
        <!--创建SqlSessionFactory SqlSessionFactoryBean-->
        <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
          <property name="dataSource" ref="dataSource"></property>
          <property name="typeAliasesPackage" value="com.baizhiedu.entity"></property>
          <property name="mapperLocations">
            <list>
              <value>classpath:com.baizhiedu.mapper/*Mapper.xml</value>
            </list>
          </property>
        </bean>
    
        @Bean
        public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
             SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
             sqlSessionFactoryBean.setDataSource(dataSource);
             sqlSessionFactoryBean.setTypeAliasesPackage("");
             ...
             return sqlSessionFactoryBean;
        }
    
    3. MapperScannerConfigure 
       <!--创建DAO对象 MapperScannerConfigure-->
      <bean id="scanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"></property>
        <property name="basePackage" value="com.baizhiedu.dao"></property>
      </bean>
      
      @MapperScan(basePackages={"com.baizhiedu.dao"}) ---> 配置bean完成
      
    
  • 编码

    1. 实体
    2. 表
    3. DAO接口
    4. Mapper文件 
    
    1. MapperLocations编码时通配的写法
    //设置Mapper文件的路径
    sqlSessionFactoryBean.setMapperLocations(Resource..);
    Resource resouce = new ClassPathResouce("UserDAOMapper.xml")
      
    sqlSessionFactoryBean.setMapperLocations(new ClassPathResource("UserDAOMapper.xml"));
    
    <property name="mapperLocations">
       <list>
         <value>classpath:com.baizhiedu.mapper/*Mapper.xml</value>
       </list>
    </property>
    一组Mapper文件 
    
    ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    Resource[] resources = resolver.getResources("com.baizhi.mapper/*Mapper.xml");
    sqlSessionFactoryBean.setMapperLocations(resources)
    
    2. 配置Bean数据耦合的问题
    mybatis.driverClassName = com.mysql.jdbc.Driver
    mybatis.url = jdbc:mysql://localhost:3306/suns?useSSL=false
    mybatis.username = root
    mybatis.password = 123456
    mybatis.typeAliasesPackages = com.baizhiedu.mybatis
    mybatis.mapperLocations = com.baizhiedu.mapper/*Mapper.xml
    
    @Component
    @PropertySource("classpath:mybatis.properties")
    public class MybatisProperties {
        @Value("${mybatis.driverClassName}")
        private String driverClassName;
        @Value("${mybatis.url}")
        private String url;
        @Value("${mybatis.username}")
        private String username;
        @Value("${mybatis.password}")
        private String password;
        @Value("${mybatis.typeAliasesPackages}")
        private String typeAliasesPackages;
        @Value("${mybatis.mapperLocations}")
        private String mapperLocations;
    }
    
    public class MyBatisAutoConfiguration {
    
        @Autowired
        private MybatisProperties mybatisProperties;
    
        @Bean
        public DataSource dataSource() {
            DruidDataSource dataSource = new DruidDataSource();
            dataSource.setDriverClassName(mybatisProperties.getDriverClassName());
            dataSource.setUrl(mybatisProperties.getUrl());
            dataSource.setUsername(mybatisProperties.getUsername());
            dataSource.setPassword(mybatisProperties.getPassword());
            return dataSource;
        }
    
        @Bean
        public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
            SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
            sqlSessionFactoryBean.setDataSource(dataSource);
            sqlSessionFactoryBean.setTypeAliasesPackage(mybatisProperties.getTypeAliasesPackages());
            //sqlSessionFactoryBean.setMapperLocations(new ClassPathResource("UserDAOMapper.xml"));
    
            try {
                ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
                Resource[] resources = resolver.getResources(mybatisProperties.getMapperLocations());
                sqlSessionFactoryBean.setMapperLocations(resources);
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            return sqlSessionFactoryBean;
        }
    }
    
    
10. 纯注解版事务编程
1. 原始对象 XXXService
   <bean id="userService" class="com.baizhiedu.service.UserServiceImpl">
     <property name="userDAO" ref="userDAO"/>
   </bean>

   @Service
   public class UserServiceImpl implements UserService{
         @Autowired
         private UserDAO userDAO;
   }

2. 额外功能
   <!--DataSourceTransactionManager-->
    <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <property name="dataSource" ref="dataSource"/>
    </bean>
    
    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource){
          DataSourceTransactionManager dstm = new DataSourceTransactionManager();
          dstm.setDataSource(dataSource);
          return dstm 
    }

3. 事务属性
    @Transactional
    @Service
    public class UserServiceImpl implements UserService {
        @Autowired
        private UserDAO userDAO;

4. 基于Schema的事务配置 
   <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
   @EnableTransactionManager ---> 配置Bean
1. ApplicationContext ctx = new AnnotationConfigApplicationContext("com.baizhiedu.mybatis");
   SpringBoot 实现思想
2. 注解版MVC整合,SpringMVC中进行详细讲解
   SpringMyBatis --->DAO  事务基于注解 --> Service   Controller 
   org.springframework.web.context.ContextLoaderListener ---> XML工厂 无法提供 new AnnotationConfigApplicationContext
11. Spring框架中YML的使用
1. 什么是YML
YML(YAML)是一种新形式的配置文件,比XML更简单,比Properties更强大。

YAML is a nice human-readable format for configuration, and it has some useful hierarchical properties. It's more or less a superset of JSON, so it has a lot of similar features.
2. Properties进行配置问题
1. Properties表达过于繁琐,无法表达数据的内在联系. 
2. Properties无法表达对象 集合类型
3. YML语法简介
1. 定义yml文件 
   xxx.yml xxx.yaml
2. 语法
   1. 基本语法
      name: suns
      password: 123456
   2. 对象概念 
      account: 
         id: 1
         password: 123456
   3. 定义集合 
      service: 
         - 11111
         - 22222
4. Spring与YML集成思路的分析
1. 准备yml配置文件 
   init.yml
   name: suns
   password: 123456
2. 读取yml 转换成 Properties
   YamlPropertiesFactoryBean.setResources( yml配置文件的路径 ) new ClassPathResource();
   YamlPropertiesFactoryBean.getObject() ---> Properties 
3. 应用PropertySourcesPlaceholderConfigurer
   PropertySourcesPlaceholderConfigurer.setProperties();
4. 类中 @Value注解 注入 
5. Spring与YML集成编码
  • 环境搭建

    <dependency>
      <groupId>org.yaml</groupId>
      <artifactId>snakeyaml</artifactId>
      <version>1.23</version>
    </dependency>
    最低版本 1.18 
    
  • 编码

    1. 准备yml配置文件
    2. 配置Bean中操作 完成YAML读取 与 PropertySourcePlaceholderConfigure的创建 
        @Bean
        public PropertySourcesPlaceholderConfigurer configurer() {
            YamlPropertiesFactoryBean yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean();
            yamlPropertiesFactoryBean.setResources(new ClassPathResource("init.yml"));
            Properties properties = yamlPropertiesFactoryBean.getObject();
    
            PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
            configurer.setProperties(properties);
            return configurer;
        }
    3. 类 加入 @Value注解 
    
6. Spring与YML集成的问题
1. 集合处理的问题
   SpringEL表达式解决
   @Value("#{'${list}'.split(',')}")
2. 对象类型的YAML进行配置时 过于繁琐 
   @Value("${account.name}")
   
SpringBoot  @ConfigurationProperties
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring学习笔记总结 的相关文章

随机推荐

  • python自动化笔记(九)文件操作

    文件的打开 file open test txt w encoding utf 8 参数 文件名 访问模式 write 默认为read file write hello python 删除原有内容 并写入 ret file read 读取文
  • Flutter使用百度定位经纬度数据正常,详细地址为null

    Flutter使用百度定位经纬度数据正常 详细地址为null 更新至2021 09 07 一 问题 1 使用百度定位 插件返回的数据中经纬度有正常值 其他地址信息都为null 二 分析原因 1 在wifi或移动网络没有 不好的情况下 会出现
  • 自动注册appleid

    1 通过猴油注册脚本 用js填写表单 问题 由于apple官网采用了自己封装的mvvm框架 如果只是赋值的话 还不能把视图上的数据更新到model上 必须触发一下表单元素的input事件或者change事件完成model的更新 CSP网站安
  • 孤儿进程和僵尸进程

    作者 华清远见讲师 前段时间 由于研究经典面试题 把孤儿进程和僵尸进程也总结了一下 我们有这样一个问题 孤儿进程和僵尸进程 怎么产生的 有什么危害 怎么去预防 下面是针对此问题的总结与概括 一 产生的原因 1 一般进程 正常情况下 子进程由
  • vue 表格表头内容居中

    放入
  • elasticsearch的object类型和动态映射

    我们需要讨论的最后一个自然JSON数据类型是对象 object 在其它语言中叫做hash hashmap dictionary 或者 associative array 内部对象 inner objects 经常用于在另一个对象中嵌入一个实
  • node mysql高级用法_nodejs中mysql用法

    1 建立数据库连接 createConnection Object 方法 该方法接受一个对象作为参数 该对象有四个常用的属性host user password database 与php中链接数据库的参数相同 属性列表如下 host 连接
  • Xray使用教程

    简介 Xray是长亭科技开发的一款漏扫工具 支持多种扫描方式和漏洞类型 可自定义POC Proof of Concept 概念验证 即漏洞验证程序 俺是在 乌雲安全 看到了这个工具的使用 作为一个脚本小子初学者 这里做一下笔记 使用 web
  • NVDLA系列之C-model:cvif<99>

    NV NVDLA cvif cpp WriteRequest sdp2cvif void NV NVDLA cvif WriteRequest sdp2cvif uint64 t base addr uint64 t first base
  • 通过递归,实现数组转树

    一 为什么需要数组转树 当我们做后台管理系统时难免会遇到关于公司组织架构这样的模块 一个部门下会有好几个小部门 这时我们就可以运用树形图来更好地进行查看 下面简单举例 将数组 const arr id 1 pid 0 name 生鲜 id
  • linux安装分区详解lvm,Linux下LVM的配置详解

    LVM是Logical Volume Manager 逻辑卷管理器 的简写 它为主机提供了更高层次的磁盘存储管理能力 LVM可以帮助系统管理员为应用与用户方便地分配存储空间 在LVM管理下的逻辑卷可以按需改变大小或添加移除 另外 LVM可以
  • 【正点原子探索者STM32F407开发板例程连载+教学】第30章 SPI通信实验

    第三十章 SPI 实验 mw shl code c true 1 硬件平台 正点原子探索者STM32F407开发板 2 软件平台 MDK5 1 3 固件库版本 V1 4 0 mw shl code 本章我们将向大家介绍STM32F4的SPI
  • upload-labs通关(Pass-06~Pass-10)

    目录 Pass 06 Pass 07 Pass 08 Pass 09 Pass 10 Pass 06 上传sh php失败 burp中将抓到的包send to repeater 修改filename为sh xxx发现可以上传成功 说明是黑名
  • 线上常见问题排查之CPU过高

    目前应用程序基本是Java 所以需要登录docker容器内部执行jstack命令打印堆栈信息再分析 确认目标进程 执行top命令查看CPU占用情况 找出CPU占用高的进程ID PS 输入大写P即可按照CPU占比排序进程 即 Shift p
  • BMP图像读取数据

    我们在使用Windows的画图软件 画一张图 然后保存成24位位图BMP图像后 要对这幅图像进行一系列的格式转换之前 需要先将BMP里的数据提取出来 然后再保存成BGR888的图像 有两处细节要注意 第一 我们直接保存成BMP图片 不知道为
  • [项目管理-25]:高效沟通的利器,结构思考力与树形结构化表达

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 目录 前言 第1章 结构化思考力概述 1 1 非结构化思考力的问题与结构化思路力的好处 1 2 什么是结构化思路力 1 3 三大沟通层次 层
  • vue prop属性使用方法小结

    Prop 一 基本用法 Prop的基本用法很简单 作用是在子组件中接收父组件的值 父组件传值
  • Allegro如何调整丝印字号

    1 设置颜色管理器 先将所有的都关掉 如果要调节top层丝印 打开top层的焊盘和阻焊层 绿油层 打开板框 丝印和位号 2 选择 设置字号 在Edit中执行Change命令 Find中选择Text 在Options中勾选刚才修改的2号字体
  • JSON首字母大写问题

    一般如果json中的字段和类里的属性相同 则不需要修改 如果不同则可以在类的属性上添加 JsonProperty 或者 JSONField 但是如果JSON数据或者类中属性首字母大写的话 只能使用 JsonProperty 否则注入的值是n
  • Spring学习笔记总结

    第一章 引言 1 EJB存在的问题 2 什么是Spring Spring是一个轻量级的JavaEE解决方案 整合众多优秀的设计模式 轻量级 1 对于运行环境是没有额外要求的 开源 tomcat resion jetty 收费 weblogi