Spring5学习详细笔记

本文详细介绍了Spring框架的学习,从引言开始,讲解了Spring的核心API、工厂设计模式,以及Spring与其他日志框架的整合。深入探讨了注入、Set注入、构造注入等,并分析了Spring的事务处理和持久层整合,特别是Spring与MyBatis的整合。此外,还涵盖了Spring的注解编程,包括基本注解和高级注解的应用,以及事务和AOP编程。通过对Spring各个重要特性的全面解析,帮助读者掌握Spring框架的精髓。

学习链接

Spring5学习详细笔记

Spring学习总结 — 工厂


第一章 :引言

1. 什么是spring

spring是一个轻量级的JavaEE解决方案,整合了众多优秀的设计模式

  • 轻量级

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

请添加图片描述

  • 整合代理模式

    1.工厂
    2.代理
    3.模板
    4.策略![在这里插入图片描述](https://img-blog.csdnimg.cn/d7647e3b58d44942bd647c79402861ff.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5b2x5a2QbXM=,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
    
    

2. 设计模式

1. 广义概念
		面对对象设计中,解决特定问题的经典代码
2. 狭义概念
		GOF4人帮定义的23中设计模式:工厂,适配器,门面,代理...

3. 工厂设计模式

3.1 什么是工厂设计模式
1. 概念:通过⼯⼚类,创建对象
   		User user = new User();
   		UserDAO userDAO = newUserDAOImpl();
2. 1. 好处:解耦合
   2. 耦合:指定是代码间的强关联关系,⼀⽅的改变会影响到另⼀⽅
   3. 问题:不利于代码维护
   4. 简单:把接⼝的实现类,硬编码在程序中
 		UserService userService =new UserServiceImpl();
3.2 简单工厂的设计
package com.it.factory;

import com.it.service.UserService;

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

/**
 * @Author muss
 * @Date 2021/4/11 15:19
 */
public class BeanFactory {

    private static final Properties properties =new Properties();

    static {

        try {
            InputStream is = BeanFactory.class.getResourceAsStream("/application.properties");
            properties.load(is);
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static UserService getUserService()  {

        UserService userService = null;

        try {
            Class<?> aClass = Class.forName(properties.getProperty("userService"));
            userService = (UserService) aClass.newInstance();
        } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
            e.printStackTrace();
        }

        return userService;
    }

}

3.3 通用工厂的设计
 	/**
     *
     * @param key  传入需要的对象对应的key
     * @return 返回需要的对象
     */
    public static Object getBean(String key){

        Object object = null;

        try{
            object = Class.forName(key).newInstance();
        }catch (Exception e){
            e.printStackTrace();
        }


        return object;
    }
3.4 通用工厂的使用方法
1. 定义类型(类)
2. 通过配置文件的配置告知工厂(即application.properties)
		key = value
3. 通过工厂获取了类的对象
		Object object = BeanFactory.getBean(key);

4. 总结

Spring本质:工厂 ApplicationContext(applicationContext.xml)

第二章 :第一个的Spring程序

1. 软件版本

1. jDK1.8+
2. Maven 3.5+
3. IDEA 2020.3
4. SpringFramework 5.1.4

2. 环境搭建

  • Spring的jar包

    <dependency>
    	<groupId>org.springframework</groupId>
    	<artifactId>spring-context</artifactId>
        <version>5.1.4.RELEASE</version>
    </dependency>
    
  • Spring的配置文件

    1. 配置文件的放置位置:任意位置 没有硬性要求
    2. 配置文件的命名:任意名字 推荐使用:applicationContext.xml
    

3. Spring的核心API

  • ApplicationContext

    1. 作用:Spring提供ApplicationContext这个工厂是用于进行对象的创建
    2. 好处:解耦合
    
    • ApplicationContext 接口类型

      使用接口 :屏蔽实现的差异
      	非web环境下使用:ClassPathXmlApplicationContext (可以在main方法中,junit中使用)
      	web环境下使用:XmlWebApplicationContext
      

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2NxJCypr-1618670320032)(Spring5.assets/image-20210411160632964.png)]

    • 重量级资源

      1. ApplicationContext工厂的对象会占用大量的内存
      2. 不会频繁的创建对象:	一般一个应用只会创建一个对象
      3. ApplicaitonContext工厂:是 线程安全 的,所以可以多线程访问
      

4. 程序开发

1. 创建类型
2. 在配置文件applicationContext.xml, 中配置
	<bean id="user" class="com.muss.bean.User"/>
3. 通过工厂类ApplicationContext,获取对象
   ApplicationContext
			|- ClassPathXmlApplicationContext
   ApplicationContext ac = new ClassPathXmlApplicationContext(“/applicationContext.xml”) 
   

5.细节分析

  • 名词解释

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

    @Test
      public void test01() {
    
        ApplicationContext ac = new ClassPathXmlApplicationContext("/applicationContext.xml");
    
        // 第一种获取bean的方法
        User user = (User) ac.getBean("user");
    
        // 第二种获取bean的方法
        User user1 = ac.getBean("user", User.class);
    
        // 第三种获取bean的方法  !这种方法只能在配置文件中只配置了一个改类型的bean时生效,否则报错
        User bean = ac.getBean(User.class);
    
        // 获取Spring工厂配置文件下所有bean标签的id值
        String[] beanDefinitionNames = ac.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
          System.out.println("beanDefinitionName=   " + beanDefinitionName);
        }
    
        // 根据类型获取bean的id值
        String[] beanNamesForType = ac.getBeanNamesForType(User.class);
        for (String s : beanNamesForType) {
          System.out.println("s=  " + s);
        }
        // 用于判断是否存在有指定id值的bean,name属性的值不行
        boolean user2 = ac.containsBeanDefinition("user");
        System.out.println("boolean=  " + user2);
    
        // 用于判断是否存在指定id的单例bean,name属性的值同样可以
        boolean user3 = ac.containsBean("user");
        System.out.println("boolean=  " + user3);
      }
    
  • 配置文件

    1. bean标签中只配置class属性
    	<bean class="com.muss.bean.User"/>
    		1:如果没有id值的话,使用工厂对象的getBeanNamesForType方法时,返回的id值为系统默认的值,如com.muss.bean.User#01
    		2:这种配置方式的应用场景:如果这个bean只需要使用一次,那么就可以省略id值
    								 如果这个bean会使用多次,或被其他bean引用的话则需要设置id值
     
     2. bean标签中的name属性
     	作用:用在Spring的配置文件中,为bean对象定义别名(小名)
     	与id属性的异同:
     		相同:都可以使用工厂对象的getbean方法  ac.getBean("user") == ac.getBean("u1")
     		区别:别名可以定义多个且都有效,但id属性可以定义多个,但是只有第一个有效,其他的值会报错
    

6. Spring工厂的底层实现原理(简易版)

在这里插入图片描述

  1. 首先通过ClassPathXmlApplicationContext工厂读取配置文件 application.xml
  2. 获取配置文件中bean标签的相关信息 id值,class值
  3. 然后通过反射创建对象,底层会调用对象自己的构造方法 !Spring工厂不管是公共的还是私有的都可以调用

7. 思考

问题:是否在开发中所有的对象都会交给Spring工厂来创建?
回答:理论上是,但实际上实体对象(entity)是不会交给Spring来创建的,而是有持久层框架(mybatis)来创建

第三章 :Spring5与日志框架的整合

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

    • 导入maven坐标

      <!-- 日志框架所需的jar包 -->
      <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-log4j12</artifactId>
          <version>1.7.7</version>
      </dependency>
      <dependency>
          <groupId>log4j</groupId>
          <artifactId>log4j</artifactId>
          <version>1.2.17</version>
      </dependency>
      
    • 引入配置文件

      # 配置根
      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 为什么需要注入

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

在这里插入图片描述

1.2 如何进行注入
  • 类的成员变量提供了setget方法

  • 配置Spring的配置文件

    <bean id="account" class="com.it.bean.Account">
            <property name="id">
                <value>
                  11
                </value>
        	</property>
            <property name="name">
                <value>
                  xiaojr
                </value>
        	</property>
    </bean>
    
1.3 注入的好处

解耦合

2. Spring注入的原理分析(简易版)

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

image-20210411223315290

第五章 :Set注入详解

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

1. JDK类型

1.1 JDK八大基本类型+String类型
<value>qiqi</value>
1.2 数组
<list>
    <value>haha01@qq.com</value>
    <value>haha02@qq.com</value>
    <value>haha03@qq.com</value>
</list>
1.3 Set集合
<property name="tels">
    <set>
        <value>haha</value>
        <list>
            <value>aa</value>
            <value>bb</value>
        </list>
    </set>
</property>
1.4 List集合
<list>
	<value>aaa</value>
	<set>
		<value>01</value>
		<value>02</value>
	</set>
</list>
1.5 Map集合
注意:map --> entry --> key有特定标签<key></key>,值需要根据对应类型来选择标签
<map>
    <entry>
        <key>
            <value>aaa</value>
        </key>
        <value>hhh</value>
    </entry>
</map>
1.6 Properties类型
Porperties类型 是特殊的 Map类型 key=String,value=String

<props>
	<prop key="a">aa</prop>
    <prop key="b">bb</prop>
</props>

2. 用户自定义类型

2.1 第一种方法
  • 为成员变量提供getset方法

  • 配置文件中进行注入

    <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属性 只能简化 8种基本类型+String 注⼊标签

⽤户⾃定义类型
<property name="userDAO" ref="userDAO"/>
3.2 基于p命名空间简化
JDK类型注⼊
<bean id="person"class="xxx.Person" p:name="suns"/>
注意:value属性 只能简化 8种基本类型+String 注⼊标签

⽤户⾃定义类型
<bean id="userService" class="xxx.UserServiceImpl" p:userDAO-ref="userDAO"/>

第六章: 构造注入

注⼊:通过Spring的配置⽂件,为成员变量赋值
Set注⼊:Spring调⽤Set⽅法 通过配置⽂件 为成员变量赋值
构造注⼊:Spring调⽤构造⽅法 通过配置⽂件 为成员变量赋值

1. 开发步骤

  • 提供有参构造⽅法

  • 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.2 构造参数个数相同时
通过在标签引⼊ type属性 进⾏类型的区分<constructor-arg type="">

3. 注⼊的总结

未来的实战中,应⽤set注⼊还是构造注⼊?
答案:set注⼊更多
 1. 构造注⼊麻烦 (重载)
 2. Spring框架底层 ⼤量应⽤了set注⼊

在这里插入图片描述

第七章:反转控制 与 依赖注⼊

1. 反转控制(IOC Inverse of Control)

控制:对于成员变量赋值的控制权
反转控制:把对于成员变量赋值的控制权,从代码中反转(转移)到Spring⼯⼚和配置⽂件中完成
 	好处:解耦合
	底层实现:⼯⼚设计模式

在这里插入图片描述

2. 依赖注⼊ (Dependency InjectionDI)

注⼊:通过Spring的⼯⼚及配置⽂件,为对象(bean,组件)的成员变量赋值

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

在这里插入图片描述

第八章:Spring⼯⼚创建复杂对象

在这里插入图片描述

1. 什么是复杂对象

复杂对象:指的就是不能直接通过new构造⽅法创建的对象
例如:
	Connection
 	SqlSessionFactory

2. Spring⼯⼚创建复杂对象的3种⽅式

2.1 FactoryBean接⼝
  • 开发步骤

    • 实现FactoryBean接口

    在这里插入图片描述

    • 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(&) ?
    
    
    1. 通过conn获得ConnectionFactoryBean类的对象 ,进⽽通过instanceof 判断出是FactoryBean接⼝的实现类
    2. Spring按照规定 getObject() —> Connection
    3. 返回Connection

在这里插入图片描述

  • 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⼯⼚创建对象的总结

在这里插入图片描述

第九章:控制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
    例如:线程不安全的,不可以多次使用
    

Spring学习总结 — 工厂高级特性

第一章:对象的生命周期

1. 什么是对象的生命周期

指的是一个对象的创建、存活、销毁的一个完整过程

2. 为什么要学习对象的生命周期

由Spring负责对象的创建、存活、销毁,了解生命周期,有利于更好使用Spring为我们创建的对象

3. 生命周期的三个阶段

  • 创建阶段

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

      Spring工厂创建的同时,把对象创建
      注意:设置scope=singleton 这种情况下想要在获取对象的同时,创建对象,便可以在配置文件中设置lazy-init属性
      <bean lazy-init="true"/>
      
    • scope=“prototype”

      Spring⼯⼚会在获取对象的同时,创建对象
      ac.getBean(“xxx”)
      
  • 初始化阶段

    Spring⼯⼚在创建完对象后,调⽤对象的初始化⽅法,完成对应的初始化操作
    
    1. 初始化⽅法提供:程序员根据需求,提供初始化⽅法,最终完成初始化操作
    2. 初始化⽅法调⽤:Spring⼯⼚进⾏调⽤
    

    可以通过两种方法来实现初始化方法

    • InitializingBean接口

      //通过实现InitializingBean接口,在afterPropertiesSet方法设置初始化的操作
      public void afterPropertiesSet(){}
      
    • 对象中提供⼀个普通的⽅法

      //编写一个方法,然后在配置文件中设置
      public void myInit(){}
      
      <bean id="product" class="xxx.Product" init-method="myInit"/>
      
    • 细节分析

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

        先执行InitializingBean中的afterPropertiesSet方法,然后执行普通初始化方法
        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="" destroymethod="myDestroy"/>
      
    • 细节分析

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

      2. 什么叫做销毁操作

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

第二章:配置文件参数化

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

配置⽂件参数化:利于Spring配置⽂件的维护(修改)

1. 配置文件参数的开发步骤

  • 提供一个小的配置文件

    #需要以.properties结尾,对于所在位置,文件名称没有要求
    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-placeholderlocation = "classpath:/db.properties"/>
    
  • 在Spring配置⽂件中通过${key}获取⼩配置⽂件中对应的值

在这里插入图片描述

第三章:自定义类型转换器

1. 类型转换器

作⽤:Spring通过类型转换器把配置⽂件中字符串类型的数据,转换成了对象中成员变量对应类型的数据,进⽽完成了注⼊

在这里插入图片描述

2. ⾃定义类型转换器

原因:当Spring内部没有提供特定类型转换器时,⽽程序员在应⽤的过程中还需要使⽤,那么就需要程序员⾃⼰定义类型转换器
  • 需要类 implement Converter接口

    public class MyDateConverter
    implements Converter<String,Date> {
    /*convert⽅法作⽤:
    	String ---> Date
    	SimpleDateFormat sdf = new SimpleDateFormat();
    	sdf.parset(String) ---> Dateparam:source 代表的是配置⽂件中 ⽇期字符串 <value>2020-10-11</value> 
    	return : 当把转换好的Date作 为convert⽅法的返回值后,Spring⾃动的为birthday属性进⾏注⼊(赋值) 
    */
    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中的⽇期的格式,通过依赖注⼊的⽅式,由配置⽂件完成赋值

    <!--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
    

第四章:后置处理Bean

BeanPostProcessor作⽤:对Spring⼯⼚所创建的对象,进⾏再加⼯
  • 后置处理Bean的运⾏原理分析

在这里插入图片描述

程序员实现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⽅法即可
  • BeanPostProcessor的开发步骤

    1. 类 实现 BeanPostProcessor接⼝

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

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

      BeanPostProcessor会对Spring⼯⼚中所有创建的对象进⾏加⼯。
      

Spring学习总结 — AOP编程

第一章:静态代理设计模式

1. 为什么需要代理设计模式

  1. 问题

    • Service层中包含了哪些代码?

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

      Service层的调⽤者的⻆度
      	(Controller):需要在Service层书写额外功能。
      软件设计者的角度
      	Service层不需要额外功能
      
    • 现实⽣活中的解决⽅式

在这里插入图片描述

2. 代理设计模式

1. 概念
通过代理类,为原始类(⽬标)增加额外的功能
好处:利于原始类(⽬标)的维护
2. 名词解释
1. ⽬标类 原始类
	 	指的是 业务类 (核⼼功能 --> 业务运算DAO调⽤)
2. ⽬标⽅法,原始⽅法
 		⽬标类(原始类)中的⽅法 就是⽬标⽅法(原始⽅法)
3. 额外功能 (附加功能)
 		⽇志,事务,性能
3. 代理开发的核⼼要素
代理类 = ⽬标类(原始类) + 额外功能 +原始类(⽬标类)实现相同的接⼝

房东 ---> public interfaceUserService{
 	m1
 	m2
 }
UserServiceImpl implements UserService{
 	m1 ---> 业务运算 DAO
	调⽤m2
 }
UserServiceProxy implements UserService
 	m1
 	m2
4. 编码

静态代理:为每⼀个原始类,⼿⼯编写⼀个代理类(.java .class)

在这里插入图片描述

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

第二章:Spring的动态代理开发

1. Spring动态代理的概念

概念:通过代理类为原始类(⽬标类)增加额外功能
好处:利于原始类(⽬标类)的维护

2. 搭建开发环境

<dependency>
     <groupId>org.springframework</groupId>
     <artifactId>springaop</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 PersonService {
        public void register(){
        System.out.println("PersonService.register");
        }
    }
    
    //在配置文件中
    <bean id="personService"  class="com.muss.serivice.PersonService"/>
    
  2. 额外功能 MethodBeforeAdvice接⼝

    //额外的功能书写在接⼝的实现中,运⾏在原始⽅法执⾏之前运⾏额外功能
    public void before(Method method, Object[] args, Object target) throws Throwable {
    	System.out.println("-------方法执行之前我执行--------");
    }
    
    //在配置文件中
    <bean id="before" class="com.muss.dynamic.Before"/>
    
  3. 定义切⼊点

    切⼊点:额外功能加⼊的位置
    ⽬的:由程序员根据⾃⼰的需要,决定额外功能加⼊给那个原始⽅法 register
    <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. 获得代理对象后,可以通过声明接⼝类型,进⾏对象的存储
    @Test
      public void test01() {
    
        ApplicationContext ac = new ClassPathXmlApplicationContext("/applicationContext.xml");
        PersonService personService = ac.getBean("personService", PersonService.class);
        personService.register();
      }
    

4. 动态代理细节分析

  1. Spring创建的动态代理类在哪⾥?

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

在这里插入图片描述

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

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

第三章:Spring动态代理详解

1. 额外功能的详解

  • MethodBeforeAdvice分析

    1. MethodBeforeAdvice接⼝作⽤:额外功能运⾏在原始⽅法执⾏之前,进⾏额外功能操作
    
    public class Before implements MethodBeforeAdvice {
        /**
         * 作用:把运行在原始方法之前运行的额外功能,书写在before方法中
         * @param method 原始方法
         * @param args 原始方法的参数列表
         * @param target 对象
         * @throws Throwable 抛出异常
         */
        @Override
        public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("-------方法执行之前我执行--------");
        }
    }
    
    2. before⽅法的3个参数在实战中,该如何使⽤。
       before⽅法的参数,在实战中,会根据需要进⾏使⽤,不⼀定都会⽤到,也有可能都不⽤。
    
  • MethodInterceptor(⽅法拦截器)

    methodinterceptor接⼝:额外功能可以根据需要运⾏在原始⽅法执⾏ 前、后、前后
    
    public class Around implements MethodInterceptor {
    
      /**
       * 
       * @param invocation 原始方法
       * @return 返回原始方法的返回值
       * @throws Throwable 抛出异常     
       */
      @Override
      public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("--------方法执行之气那------");
        Object ret = invocation.proceed();
        System.out.println("--------方法执行之后-------");
        return ret;
      }
    }
    
    //什么样的额外功能 运⾏在原始⽅法执⾏之前,之后都要添加? 例如事务
    
    //额外功能运⾏在原始⽅法抛出异常的时候
    try {
        ret = invocation.proceed();
    } catch (Throwable throwable) {
        System.out.println("-----原始⽅法抛出异常 执⾏的额外功能 ----");
        throwable.printStackTrace();
    }
    
    
    //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. ⽅法切⼊点表达式

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

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

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

      修饰符 返回值 包.类.⽅ 法(参数)
      
          com.baizhiedu.proxy.UserServi
          ceImpl.login(..)
      
          com.baizhiedu.proxy.UserServi
          ceImpl.login(String,String)
      
  2. 类切⼊点

指定特定类作为切⼊点(额外功能加⼊的位置),⾃然这个类中的所有⽅法,都会加上对应的额外功能
  • ​ 语法1
## 类中的所有⽅法加⼊了额外功能
com.baizhiedu.proxy.UserServiceImpl.*(..)
  • ​ 语法2

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

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

      ## 切⼊点包中的所有类,必须在proxy proxy中,不能在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)"/>
    
  5. 切⼊点函数的逻辑运算

    指的是 整合多个切⼊点函数⼀起配合⼯作,进⽽完成更为复杂的需求
    
    • 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. 切⾯的名词解释

## 切⾯ = 切⼊点 + 额外功能

⼏何学
 	⾯ = 点 + 相同的性质

在这里插入图片描述

第五章:AOP的底层实现原理

1. 核⼼问题

1. AOP如何创建动态代理类(动态字节码技术)
2. Spring⼯⼚如何加⼯创建代理对象
 		通过原始对象的id值,获得的是代理对象

2. 动态代理类的创建

2.1 JDK的动态代理
  • Proxy.newProxyInstance⽅法参数详解

在这里插入图片描述

  • 编码

    public class TestProxy02 {
    
        public static void main(String[] args){
    
            PersonService personService = new PersonService();
    
            //使用JDK的动态代理
    
            //获取类加载器
            ClassLoader classLoader = PersonService.class.getClassLoader();
    
            //获取原始类实现的接口
            Class<?>[] interfaces = personService.getClass().getInterfaces();
    
            //获取invocationHandler的实现
            InvocationHandler invocationHandler = (proxy, method, args1) -> {
                System.out.println("-----log-----");
                Object ret = method.invoke(personService, args1);
                System.out.println("-----a-----");
                return ret;
            };
    
    
            PersonDao personServiceProxy = (PersonDao) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
    
            personServiceProxy.register();
    
        }
    }
    
2.2 CGlib的动态代理
CGlib创建动态代理的原理:⽗⼦继承关系创建代理对象,原始类作为⽗类,代理类作为⼦类,
这样既可以保证2者⽅法⼀致,同时在代理类中提供新的实现(额外功能+原始⽅法)

在这里插入图片描述

  • CGlib编码
    	PersonService personService = new PersonService();	
    
    	// 使用CGlib的动态代理
        Enhancer enhancer = new Enhancer();
    
        enhancer.setClassLoader(TestProxy02.class.getClassLoader());
        enhancer.setSuperclass(personService.getClass());
    
        enhancer.setCallback(
                (MethodInterceptor) (o, method, objects, methodProxy) -> {
                  System.out.println("--------log--------");
                  Object ret = method.invoke(personService, objects);
                  System.out.println("-------b------------");
                  return ret;
                });
          PersonService personServiceProxy = (PersonService) enhancer.create();
    
          personServiceProxy.register();
    
  • 总结

    1. JDK动态代理 
    		Proxy.newProxyInstance() 通过接⼝创建代理的实现类
    2. Cglib动态代理 Enhancer 
     		通过继承⽗类创建的代理类
    

3. Spring⼯⼚如何加⼯原始对象

  • 思路分析

在这里插入图片描述

  • 编码

    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 {
    
            InvocationHandler invocationHandler = new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
                    Object ret = method.invoke(bean, args);
                    System.out.println("------log-----");
    
                    return ret;
                }
            };
    
        	return Proxy.newProxyInstance(MyBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), invocationHandler);
        }
    }
    
    <bean id="personServiceProxy" class="com.muss.service.PersonService"/>
    
    <bean id="myBeanPostProcessor" class="com.muss.factory.MyBeanPostProcessor"/>
    
    

第六章:基于注解的AOP编程

1. 基于注解的AOP编程的开发步骤

  1. 原始对象

  2. 额外功能

  3. 切入点

  4. 组装切面

    // 通过切⾯类 定义了 额外功能
    @Around 定义了 切⼊点
    @Around("execution(*login(..))")
    
    @Aspect 切⾯类
    public class MyAspect {
    
      @Pointcut("execution(* register(..))")
      public void myPointcut() {}
    
      @Around("myPointcut()")
      public Object around(ProceedingJoinPoint pjp) throws Throwable {
    
        Object ret = pjp.proceed();
        System.out.println("111");
        return ret;
      }
    }    
          
    
    <bean id="arround" class="com.baizhiedu.aspect.MyAspect"/>
    
    <!--告知Spring基于注解进⾏AOP编程-->
    <aop:aspectj-autoproxy />
    

2. 细节

  1. 切⼊点复⽤

    切⼊点复⽤:在切⾯类中定义⼀个函数上⾯@Pointcut注解 通过这种⽅式,定义切⼊点表达式,后续更加有利于切⼊点复⽤。
    
    @Pointcut("execution(* register(..))")
    public void myPointcut() {}
    
    @Around("myPointcut()")
    # 在切面类中编写一个返回类型为void,方法体为空,参数为空的方法上添加注解@Pointcut
    # 之后就可以在@Around注解中添加有@Pointcut的方法的方法名
    
  2. 动态代理的创建⽅式

    AOP底层实现 2种代理创建⽅式
    1. JDK 通过实现接⼝ 做新的实现类⽅式 创建代理对象
    2. Cglib通过继承⽗类 做新的⼦类
     
    创建代理对象
    # 默认情况 AOP编程 底层应⽤JDK动态
    代理创建⽅式
    如果切换Cglib
     1. 基于注解AOP开发
     		<aop:aspectjautoproxy proxy-target-class="true" />
     2. 传统的AOP开发
     		<aop:config proxytarget-class="true"></aop>
    

第七章:AOP开发中的⼀个坑

坑:在同⼀个业务类中,进⾏业务⽅法间的相互调⽤,只有最外层的⽅法,才是加⼊了额外功能(内部的⽅法,通过普通的⽅式调⽤,都调⽤的是原始⽅法)。
   如果想让内层的⽅法也调⽤代理对象的⽅法,就要AppicationContextAware获得⼯⼚,进⽽获得代理对象
public class UserServiceImpl implements UserService,ApplicationContextAware {
    private ApplicationContextctx;	
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throwsBeansException {
        this.ctx =applicationContext;
    }
    
    @Override
    public void register(User user) {
        System.out.println("UserServiceImpl.register 业务运算 + DAO ");
        UserService userService = (UserService)ctx.getBean("userService");
        userService.login("suns","123456");
    }

第八章:AOP阶段知识总结

在这里插入图片描述

Spring学习总结 — 持久层整合

第一章:持久层整合

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. Spring与MyBatis整合思路分析

在这里插入图片描述

4. Spring和MyBatis整合的开发步骤

  • 配置文件(applicaitonContext.xml)中

    配置dataSource的bean
    配置SqlSessionFactoryBean的bean
    配置MapperSacnnerConfigure的bean
    
  • 编码

1. 实体
2. 表
3. 创建DAO接口
4. 实现Mapper文件

5.Spring和MyBatis整合编码

  • 添加依赖

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>5.1.4.RELEASE</version>
    </dependency>
    
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.1.4.RELEASE</version>
    </dependency>
    
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.4</version>
    </dependency>
    
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.10</version>
    </dependency>
    
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.32</version>
    </dependency>
    
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.4</version>
    </dependency>
    
  • 配置文件

    <!-- 数据源 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql:///test"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
    
    <!-- 创建SqlSessionFactoryBean -->
    <bean id="sqlSessionFactoryBean"  class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="typeAliasesPackage" value="com.muss.bean"/>
        <property name="mapperLocations">
            <list>
                <value>classpath:*Mapper.xml</value>
            </list>
        </property>
    </bean>
    
    <!-- 创建MapperScannerConfigure -->
    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
        <property name="basePackage" value="com.muss.dao"/>
    </bean>
    
  • 编码

    // 实体
    public class Person {
        private String username;
        private String password;
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "username='" + username + '\'' +
                    ", password='" + password + '\'' +
                    '}';
        }
    }
    
    //dao文件
    public interface PersonDao {
    
        public  void save(Person person);
    }
    
    //mapper文件
    <mapper namespace="com.muss.dao.PersonDao">
        <insert id="save" parameterType="Person">
            insert  into person (username,password) values (#{username},#{password});
        </insert>
    </mapper>
    
    

6.Spring与MyBatis整合细节

  • 问题:Spring和MyBatis整合之后,为什么DAO不提交事务,但是数据库能够插入数据呢

    Connection --> tx
    
    # 本质上控制连接对象(Connection)--> 连接池(DataSource)
    1. MyBatis提供的数据库连接池  --> 创建Connection
    		Connection.setAutoCommit(false) 手工控制事务,需要在操作完成后手动提交
    2. Druid(C3P0)作为连接池 --> 创建Connection
    		Connection.setAutoCommit(true)  true默认值,保持自动控制事务,自动提交
    # 注意:未来实战中,使用Spring通过控制事务解决多条sql一起成功一起失败的问题
    

第三章:Spring的事务处理

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

  • 事务的四个特点:A C I D
    • A :原子性
    • C : 一致性
    • I :隔离性
    • D :持久性

1. 如何控制事务

JDBC:
	Connection.setAutoCommit(false)
	Connection.commit();
	Connection.rollback();
Mybatis:
	Mybatis自动开启事务
	sqlsession(Connection).commit()
	sqlsession(Connection).rollback()
# 结论:控制事务的底层,都是通过Connection对象完成的

2. Spring控制事务的开发

Spring是通过AOP的方式进行事务开发

  1. 原始对象

    public class UserServiceImpl(){
    	private UserDAO userDAO;
    	set, get
    1. 原始对象-->原始方法-->核心功能(业务处理+核心功能调用)
    2. Dao作为Service的成员变量,通过依赖注入的方式进行赋值
    }
    
  2. 额外功能

    1. MethodInterceptor
    public Object invoke(MethodInvocation invocation){
    	Object ret = null;
    	try{
    		Connection.setAutoCommit(false);
    		ret = invocation.proceed()
    		Connection.commit();
    	}catch(Exception e){
    		Connection.rollback();
    	}
    	return ret
    }
    2. @Aspect
       @Around
       
    # spring封装了 DateSourceTransactionManager,进行事务管理但是我们需要为其注入连接(注入连接池DateSource)
    
  3. 切入点

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

    切入点
    额外功能
    
    <tx:annotation-driven transction-manger="dataSourceTransactionManger"/>
    
    <bean id="userService" class="com.gewei.service.UserServiceImpl">
        <property name="dao" ref="userDao"/>
    </bean>
    <bean id="dateSourceTransactionManger" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dateSource"/>
    
    </bean>
    <tx:annotation-driven transaction-manager="dateSourceTransactionManger"/>
    
  5. 细节

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

第四章:Spring事务属性

事务属性:描述事务特征的一系列值

# 隔离属性
# 传播属性
# 只读属性
# 超时属性 
# 异常属性
  • 如何添加事务属性

    @Transactional(isolation = ,propagation = , readOnly = ,timeout = , rollbackFor = ,noRollbackFor = )
    
  • 隔离属性:他表示了事务解决并发问题的特征

  • 什么是并发

    多个事务,或者用户,在同一时间访问操作了统一数据(同一时间:并不是说必须分毫不差,可能差0.00几秒)
    
  • 并发会产生哪些问题

    • 脏读
    • 不可重复读
    • 幻影读
  • 并发问题如何解决

    通过隔离属性解决,隔离属性中设置不同的值,解决并发 处理过程中的问题
    

1. 隔离属性

脏读
一个事务,读取了另一个事务没有提交的数据,会产生数据不一致的问题

解决办法:读已提交 @Transactional(isolation = Isolation.READ_COMMITTED)
不可重复读
 一个事务,多次读取相同的数据,但是读取结果不一致,会在本事务中产生数据不一致的问题

注意:1 不是脏读 2 在一个事务中

解决方法:@Transactional(isolation = Isolation.REPEATABLE_READ)

本质:一把行锁
幻影读
一个事务中,多次对整表进行查询统计,但是结果不一样,会在 本事务中产生数据不一致的问题。

解决方法:@Transactional(isolation = Isolation.SERIALIZABLE)

本质:表锁
总结
并发安全:SERIALIZABLE>REPEATABLE_READ>READ_COMMITTED
运行效率:READ_COMMITTE>REPEATABLE_READ>SERIALIZABLE
数据库对隔离属性的支持
隔离属性的值OracleMysql
isolation = Isolation.READ_COMMITTED可以可以
Isolation.REPEATABLE_READ不可以可以
Isolation.SERIALIZABLE可以可以

Oracle不支持REPEATABLE_READ值,如何解决不可重复读采用多版本的的方式,解决不可重复读的问题

默认隔离属性
  • ISOLATION_DEAFULT :会调用不同数据库设置的隔离属性

  • Mysql:不可重复读 REPEATABLE_READ

    • 可以通过sql语句查询: SELECT @@tx_isolation
  • Oracle:读已提交 READ_COMMITTED

  • 隔离属性在实战中的建议:直接使用默认就好,在未来的实战中,并发访问的几率很低如果真的遇到并发问题,推荐使用乐观锁。

    • Hibernate(JPA) Version
    • Mybatis:通过拦截器自定义开发

2. 传播属性(propagation)

  • 传播属性:描述了事务解决嵌套问题的特征
  • 什么是事务的嵌套:它指的是一个大的事务中,包含若干个小的事务
  • 问题:大事务中融入了很多小的事务,他们彼此影响,最终会导致外部大的事务,丧失了事务的原子性
传播属性的值外部不存在事务外部存在事务备注
REQUIRED开启新的事务融入外部事务Propagation.REQUIRED增删改操作
SUPPORTS不开启事务融入外部事务Propagation.SUPPORTS查询方法

Required是查询属性的默认值

查询:显示的指定传播属性为Supports

3. 只读属性

针对于进行查询操作的方法,可以加入只读属性,提高运行效率

默认值为false

4.超时属性

当前事务访问数据时,有可能访问的数据被其他数据获取锁后加锁了,当前事务就必须要等待释放锁。

超时属性默认值为-1,最终由数据库指定(在开发中很少使用)

5. 异常属性

Spring事务处理过程中
默认 对RuntimeException及其子类,采用的是回滚的机制
默认 对Exception及其子类,采用的是提交事务的机制
rollbackFor = java.lang.Exception Exception开启回滚
noRollbackFor = java.lang.RuntimeEexception 关闭回滚
建议:实战中使用默认就可以了

6. 基于标签的事务配置方式(事务开发的第二种形式)

基于标签的事务配置

1. 原始类
...

2. 额外功能与事务属性
...

<tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">
    <tx:attributes>
        <tx:method name="register"/>
        <tx:method name="login" propagation="SUPPORTS" read-only="true"/>
    </tx:attributes>
</tx:advice>
<!-- 等效于
	@Transactional
	public void register(User user) {...}
	
	@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
	public void login(String username, String password) {...} 
-->

<aop:config>
	3. 切入点
    <aop:pointcut id="pc" expression="execution(* com.angenin1.service.UserServiceImpl.*(..))"/>
    4. 组装切面
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
</aop:config>
  • 基于标签的事务配置在实战中的引用方式

    事务属性配置
    <tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">
        <tx:attributes>
            <!--<tx:method name="register"/>-->
            <!--<tx:method name="login" propagation="SUPPORTS" read-only="true"/>-->
            
            <!--使用通配符解决tx:method标签过多的问题-->
            <!--编程时,service中负责增删改操作的方法都以modify开头即可-->
            <tx:method name="modify*"/>
            <!-- *:指除上面的其他方法(查询),需要把范围小的放前面,范围大的放后面 -->
            <tx:method name="*" propagation="SUPPORTS" read-only="true"/>
        </tx:attributes>
    </tx:advice>
    
    切入点
    <aop:config>
        <!--<aop:pointcut id="pc" expression="execution(* com.angenin1.service.UserServiceImpl.*(..))"/>-->
        <!--使用包切入点-->
    	<aop:pointcut id="pc" expression="execution(* com.angenin1.service..*.*(..))"/>
    	
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
    </aop:config>
    

Spring学习总结 — 注解编程

Spring开发过程中多配置文件的处理

Spring根据需要,将配置文件分门别类放在多个配置文件中,便于后期管理和维护

DAO 		--> applicaiton-dao.xml
Service 	--> application-service.xml
Controller  --> application-controller.xml
  • 通配符方式

    1. 非web环境下
    		ApplicaitonCOntext ac = new ClassPathXmlApplicationContext("/application-*.xml")
    		
    2. web环境下
    		<context-param>
    			<param-name>contextConfigLocation</param-name>
    			<param-value>classpath:application-*.xml</param-value>
    		</context-param>
    
  • import标签

    application.xml
    		<import resource="application-dao.xml"/>
    		<import resource="application-service.xml"/>
    		<import resource="application-controller.xml"/>
    		
    1. 非web环境下
    		ApplicaitonCOntext ac = new ClassPathXmlApplicationContext("/application.xml")
    		
    2. web环境下
    		<context-param>
    			<param-name>contextConfigLocation</param-name>
    			<param-value>classpath:application.xml</param-value>
    		</context-param>
    

    第一章:注解基础概念

1. 什么是注解编程

指的是在类或方法上加入特定的注解(@XXX),完成特定功能的开发
    
@Component
public class XX(){}

2. 为什么要使用注解

1. 注解开发方便
	代码简洁 开发速度大大提高
2. Spring开发潮流

3. 注解的作用

  • 替换XML这种配置形式,简化配置

在这里插入图片描述

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

    通过注解的方式,在功能调用者和功能提供者之间达成约定,进而进行功能的调用。因为注解应用更加灵活
    

在这里插入图片描述

4. Spring注解开发的一个问题

Spring基于注解进行配置后,还能解耦合吗?

在Spring框架应用注解时,如果对注解配置的内容觉得不满意,可以使用Spring配置文件的方式进行覆盖

第二章:Spring的基本注解

1. 对象创建相关注解

  • 搭建开发环境

    <context:component-scan base-package="包名"/>
    
    作用:让Spring框架在设置包及其子包中扫描对应的注解,使其生效
    
@Component
作用:替换Spring配置文件中的bean标签
注意:
	id属性:component注解 提供了默认的设置方式  首单词字母小写
	class属性:通过反射获取class内容

在这里插入图片描述

  • @Component细节

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

      @Component("想要输入的id值")
      
    • Spring配置文件覆盖注解内容

      applicationx.xml
      <bean id="XXX" class="xxx"/>
      
      注意:id值 class值 要和 注解中的设置保持一致
      
    • @Component的衍生注解

      @Repository
      @Serrvice
      @Controller
      
      
      注意:本质上衍生注解就是 @Component
      
      目的:更加准确的表达一个类型的作用
      
      注意:Spring与MyBatis整合过程中,@Repository @Component不会使用
      
@Scope
作用:控制简单对象创建的次数

@Scope
public class XX(){}
@Lazy
作用:延迟创建单实例对象
注意:一旦使用这个注解后,Spring会在使用这个对象时才创建

@Lazy
public class XX(){}

2. 生命周期方法相关注解

1. 初始化方法 @PostConstruct
		InitializingBean
		<bean init-method=""/>
2. 销毁方法 @PreDestory 
		DisposableBean
		<bean destory-method=""/>
		
注意: 
	1 上述的2个注解是由JSR(JavaEE规范)520 提供的
	2 再一次验证,通过注解实现接口的契约性

3. 注入相关注解

  • 用户自定义类型 @Autowired

在这里插入图片描述

@Autowired细节
1. Autowired注解基于类型注入【推荐】
		基于类型注入:注入对象的类型,必须与目标成员变量类型相同或者是其子类(接口实现)
2. Autowired Qualifier 基于名字进行注入【了解】
		基于名字注入:注入对象的id值,必须与Qualifier注解中设置的名字相同
3. Autowired注解的放置位置
		a) 放置在对应的成员变量set方法上
		b) 直接放置在成员变量上,Spring通过反射直接对成员变量进行注入【推荐】
4. JavaEE规范类似的注解
	JSR250 中基于名字注入
		@Resource(name="xxx") 等同于 @Autowired + @Qualifier(name="xxx")
		注意:如果在应用Resource注解时,名字没有配对成功,那么会按照类型进行注入
    JSR330 中@Inject 作用与 @Autowired 完全一致   一般在EJB中使用
    	想使用的话需要引入相关依赖
    		<dependency>
    			<groupId>javax.inject</groupId>
    			<artifactId>javax.inject</artifactId>
    			<version>1</version>
    		</dependency>
  • JDK类型

    • @Value

      @Value注解完成步骤
      1. 设置xxx.properties
      		key = value
      		id = 1
      2. Spring的工厂读取这个配置文件
      		<context:property-placeholder location=""/>
      3. 代码
      		属性 @Value()
      
      • @Value细节

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

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

    • @PropertySource

      作用:用于代替Spring配置文件中<context:property-placeholder location=""/>的
      1. 设置xxx.properties
      		key = value
      		id = 1
      2. 应用@PropertySource
      		 @PropertySource("classpath:/xx.properties")
      		 public class xx(){}
      3. 代码
      		属性 @Value()
      

4. 注解扫描详解

<context:component-scan base-package="xxx"/>
  1. 排除方式

    <context:component-scan base-package="com.muss">
        <context:exclude-filter type="" expression=""/>
        type:	assignable:	排除特定的类型 不进行扫描
        		anntation:	排除特定的注解 不进行扫描
        		aspectj:	切入点表达式排除
        		regex:		正则表达式排除
        		custom:		自定义排除册罗 一般在框架底层开发
    </context:component-scan>
    
  2. 包含方式

     <context:component-scan base-package="com.muss" use-default-filters="false">
            <context:include-filter type="" expression=""/>
     </context:component-scan>
    1. use-default-filters 作用:让Spring的默认注解扫描方式失效
    2.  <context:include-filter type="" expression=""/>
    	作用:指定扫描那些注解
        type:	assignable:		指定特定的类型 进行扫描
                    anntation:	指定特定的注解 进行扫描
                    aspectj:	切入点表达式扫描
                    regex:		正则表达式扫描
                    custom:		自定义扫描策略 一般在框架底层开发
    	包含的方式支持叠加
    

5. 对于注解的思考

  • 配置互通

    注解配置 Spring配置文件 互通
    
  • 什么情况使用注解 什么情况下使用配置文件

    基础注解 程序员开发类型的配置
    1. 在程序员开发的类型上 可以加入对应注解
    2. 应用非程序员开发的类型是,还是需要使用<bean>进行配置
    

第三章:Spring的高级注解

1. 配置Bean

Spring3.X提供的新注解,用于代替XML配置文件
  • @Configuration注解的本质

    本质:也是@Component注解的衍生注解
    

2. @Bean

1. @Bean注解的基本使用
  • 对象的创建

在这里插入图片描述

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

    @Bean
    public Connection conn(){
    	ConnectionFactoryBean cfb = new ConnectionFactoryBean();
    	Connection conn = cfb.getObject();
    	return conn;
    }
    
  • 自定义id值

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

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

    @Bean
    public UserService userService(){
    	UserServiceImpl usi = new UserServiceImpl();
    	usi.setUserDao(userDao());//userDao()是另一个有@Bean的方法
    	return usi;
    }
    
  • JDK的注入

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

      //如果直接在代码中使用set方法调用会产生耦合
      @Configuration
      @PorpertySource("classpath:/init.properties")
      public class MyConifg{
      	@Value("${id}")
      	private int id;
      
          @Bean
          public Customer customer(){
              Customer customer = new Customer();
              customer.setId(id);
              customer.setName("qiqi");
              return customer;
          }
      }
      

3. @ComponentScan

@ComponentScan在注解配置中等同与 <context:component-sacn>标签

目的:进行相关注解的扫描
  1. 基本使用

    @Configuration
    @ComponentScan(basePackages="xxx")
    public class MyConifg{}
    
  2. 排除,包含的使用

    • 排除

在这里插入图片描述

  • 包含

在这里插入图片描述

4. Spring工厂创建对象的多种配置方式

1. 多种配置方式的应用场景

在这里插入图片描述

2. 配置的优先级
@Component及其衍生注解 < @Bean < bean标签

# 优先级高的覆盖优先级低的  但id值需要保持一致
  • 解决注解配置产生耦合的问题

在这里插入图片描述

5. 整合多个配置信息

  • 为什么会有多个配置信息

    拆分多个配置bean的开发,是一种模块化的开发,也体现了面对对象各司其职的设计思想
    
  • 多个配置信息整合的方式

    • 多个配置bean的整合
    • 配置bean与@Component相关注解的整合
    • 配置bean与SpringXML配置文件的整合
  • 整合多个配置需要关注的那些要点

    • 如何使用多配置信息汇总成一个整体
    • 如何实现跨配置的注入
1. 多个配置Bean的整合
  • 多配置的信息汇总

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

在这里插入图片描述

  • @Import

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

在这里插入图片描述

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

    ApplicaitonContext ac = new AnnotationConfigApplicaitonContext(MyConfig.class,MyConfig2.class);
    
  • 跨配置的注入

在这里插入图片描述

2. 配置Bean和@Component及其衍生注解整合

在这里插入图片描述

3. 配置Bean与配置文件整合

在这里插入图片描述

6. 配置Bean底层实现原理

Spring在配置Bean中加入@Configuration注解后,底层就会通过CGlib的代理方式,来进行对象相关的配置.处理

在这里插入图片描述

7. 四维一体的开发思想

1. 什么是四维一体
Spring开发一个功能的四种方式,虽然开发方式不同,但是结果是一样的
1. 基于schema
2. 基于特定功能注解
3. 基于原始bean标签
4. 基于@Bean注解

8. 纯注解AOP编程

  • 开发步骤

在这里插入图片描述

  • 注解AOP细节分析

在这里插入图片描述

9. 纯注解事务编程

在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值