230921-初识spring

1、 Spring是⼀个轻量级的JavaEE解决⽅案,整合众多优秀的设计模式

  • 轻量级 JavaEE的解决⽅案

  1. 对于运⾏环境是没有额外要求的 开源 tomcat resion jetty 收费 weblogic websphere

  2. 代码移植性⾼ 不需要实现额外接⼝

  • 通过设计模式来优化代码 工厂设计模式:解耦合

如果把实现类硬编码在代码中,耦合度太高,一般情况下使用工厂模式来创建实例对象。 对象的创建⽅式:

  1. 直接调⽤构造⽅法 创建对象 UserService userService = new UserServiceImpl();

  2. 通过反射的形式 创建对象 解耦合 Class clazz = Class.forName("com.baizhiedu.basic.UserServiceImpl"); UserService userService = (UserService)clazz.newInstance(); 一般工厂类都是使用反射来创建对象。Pasted image 20230921164052.png如何设计一个通用的工厂类呢?首先类路径不能写死(写死就耦合了),其次返参需要是Object,不能在工厂类中确定创建的确切类型 1、类路径在配置文件中配置,通过静态代码块读取配置文件,以便获取配置内容 Pasted image 20230921165845.png然后再对于的getXXXBean中获取对应的key,创建实例对象 Pasted image 20230921170051.png 但是,以上存在大量的冗余代码,首先是key需要写死在工厂类中,其次,类型也在工厂类中写死了,这就需要一个通用的工厂类来创建实例对象Pasted image 20230921170231.png使用通用的工厂需要如下步骤 Pasted image 20230922135738.png

总结 Spring本质:⼯⼚ ApplicationContext (applicationContext.xml) 重点就是:工厂+配置文件

230922-spring环境搭建和核心API

1、Spring环境搭建

  1. 软件版本

1. JDK1.8+ 
2. Maven3.5+ 
3. IDEA2018+ 
4. SpringFramework 5.1.4
  1. 环境搭建

  • 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 
3. 思考:⽇后应⽤Spring框架时,需要进⾏配置⽂件路径的设置。

Pasted image 20230922143106.png

2、spring的核心API

  • ApplicationContext

作⽤:Spring提供的ApplicationContext这个⼯⼚,⽤于对象的创建 
好处:解耦合
  • ApplicationContext接⼝类型

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

Pasted image 20230922144418.png

  • 重量级资源

ApplicationContext⼯⼚的对象占⽤⼤量内存。 
不会频繁的创建对象 : ⼀个应⽤只会创建⼀个⼯⼚对象。 
ApplicationContext⼯⼚:⼀定是线程安全的(多线程并发访问)

230923-第一个spring程序+Set注入

1、代码示例

  • 开发步骤

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");

代码示例

public class User {  
    private String name;  
    private Integer age;  
  
    public String getName() {  
        return name;  
    }  
  
    public void setName(String name) {  
        this.name = name;  
    }  
  
    public Integer getAge() {  
        return age;  
    }  
  
    public void setAge(Integer age) {  
        this.age = age;  
    }  
}
<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">  
  
    <!--    spring的配置文件使用工厂 ClassPathXmlApplicationContext 来创建对应的对象-->  
    <bean id="user" class="com.sunzhen.User"/>  
</beans>
public class TestSpring1 {  
  
    @Test  
    public void test1(){  
        // 1 获取spring工厂  
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");  
        // 2 通过工厂类获得对象  
        User user = (User)context.getBean("user");  
  
        System.out.println(user);  
    }  
}

工厂类的相关方法

        //当前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,不能判断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);
​
        }
  • 配置文件中需要注意的细节

1. 只配置class属性 <bean class="com.baizhiedu.basic.Person"/> 
a) 上述这种配置 有没有id值 spring会按照特定规则自己生存一个 -> 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 
    3. name属性的值,命名没有要求 /person 
    4. name属性会应⽤在特殊命名的场景下:/person 比如在使用(spring+struts1)开发时
    5. XML发展到了今天:ID属性的限制,不存在 /person 已经可以使用斜杠开始了
// 3. 代码  containsBeanDefinition 和 containsBean 的区别,主要是id和name的判别
//⽤于判断是否存在指定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); 
    }

Pasted image 20230923203300.png

底层通过读取spring的xml配置文件,使用反射创建对象,会调用对象自己的构造方法(无参构造)

  • 思考

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

2、spring和日志框架整合

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

如何整合?

默认 
	Spring1.2.3早期都是于commons-logging.jar 
	Spring5.x默认整合的⽇志框架 logback log4j2 
Spring5.x整合log4j 
1. 引⼊log4j jar包 
2. 引⼊log4.properties配置⽂件
	<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>
# resources⽂件夹根⽬录下⽂件夹根⽬录下  log4j.properties
### 配置根配置根 
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

3、注入

  • 什么是注⼊

通过Spring⼯⼚及配置⽂件,为所创建对象的成员变量赋值
  • 为什么需要注⼊? 通过编码的⽅式,为成员变量进⾏赋值,存在耦合 Pasted image 20230923210031.png

  • 如何进⾏注⼊[开发步骤] 1、类的成员变量提供set get⽅法 2、配置spring的配置⽂件Pasted image 20230923210130.png

  • 注⼊好处

	解耦合

Pasted image 20230923210936.png

4、Set注入详解

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

Pasted image 20230923211854.pngPasted image 20230923214710.png

  • 1、String+8种基本类型

<value>suns</value>
  • 2、数组

<list>  
    <value>1@qq.com</value>  
    <value>2@qq.com</value>  
    <value>3@qq.com</value>  
</list>
  • 3、 set集合

set内部的标签需要根据泛型选择,比如

<ref bean=""/>
<set>  
    <value>13412341234</value>  
    <value>13512341234</value>  
    <value>13612341234</value>  
</set>
  • 4、list集合

内部的标签需要根据泛型选择

<property name="address">  
    <list>  
        <value>chengdu</value>  
        <value>xian</value>  
        <value>hebei</value>  
    </list>  
</property>
  • 5、map集合

注意: map -- entry -- key有特定的标签 值根据对应类型选择对应类型的标签

<property name="qqs">  
    <map>  
        <entry>  
            <key><value>key1</value></key>  
            <value>value1</value>  
        </entry>  
        <entry>  
            <key><value>key2</value></key>  
            <value>value2</value>  
        </entry>  
    </map>  
</property>
  • 6、Properties

Properties类型 特殊的Map key=String value=String

<property name="properties">  
    <props>  
        <prop key="key1">value1</prop>  
        <prop key="key2">value2</prop>  
    </props>  
</property>
  • 7、复杂的JDK类型 (Date)

需要程序员⾃定义类型转换器,处理。

  • 8、用户自定义类型Pasted image 20230923215554.png
    Pasted image 20230923215622.png

  • 第二种方式Pasted image 20230923215708.png

把公用的对象,在配置文件中单独创建,以后每个需要的地方使用ref进行引用即可

  • set注入的简化写法Pasted image 20230923220805.pngPasted image 20230923221100.png

230924-构造注入

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

开发步骤

1、提供有参构造⽅法

public class Customer {  
    private String name;  
    private Integer age;  
  
  
    public Customer(String name, Integer age) {  
        this.name = name;  
        this.age = age;  
    }  
}

2、Spring的配置⽂件

<bean id="customer" class="com.sunzhen.Customer">  
    <constructor-arg>  
        <value>sunzhen</value>  
    </constructor-arg>  
    <constructor-arg>  
        <value>18</value>  
    </constructor-arg>  
</bean>

构造方法重载

参数个数不同时

通过控制 <constructor-arg> 标签的数量进⾏区分

构造参数个数相同时

通过在标签引⼊ type属性 进⾏类型的区分  <constructor-arg type="">

注入的总结

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

Pasted image 20230924164238.png

反转控制 与 依赖注入

反转(转移)控制(IOC inverse of Control)

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

Pasted image 20230924164927.png

依赖注入(Dependency Injection DI)

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

Pasted image 20230924165300.png

Spring⼯⼚创建复杂对象

Pasted image 20230924165827.png

1、什么是复杂对象

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

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

2.1 FactoryBean接⼝

  • 开发步骤

    • 实现FactoryBean接⼝Pasted image 20230924170421.pngPasted image 20230924172624.png

    • Spring配置⽂件的配置

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

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

    • isSingleton⽅法 返回 true 只会创建⼀个复杂对象 返回 false 每⼀次都会创建新的对象 问题:根据这个对象的特点 ,决定是返回true (SqlSessionFactory) 还是 false (Connection)

    • mysql⾼版本连接创建时,需要制定SSL证书,解决问题的⽅式 jdbc:mysql://192.168.3.100:3306/security?useSSL=false

    • 依赖注⼊的体会(DI)

把ConnectionFactoryBean中依赖的4个字符串信息 ,进⾏配置⽂件的注⼊ 
好处:解耦合
<bean id="con" class="com.sunzhen.factory.ConnectionFactoryBean">  
    <property name="classUrl" value="com.mysql.jdbc.Driver"/>  
    <property name="url" value="jdbc:mysql://192.168.3.100:3306/security?useSSL=false"/>  
    <property name="user" value="root"/>  
    <property name="password" value="Sun123456+"/>  
</bean>

FactoryBean的实现原理[简易版]

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

Pasted image 20230924174739.png

  • FactoryBean总结

Spring中⽤于创建复杂对象的⼀种⽅式,也是Spring原⽣提供的,后续讲解Spring整合 其他框架,⼤量应⽤FactoryBean

2.2 实例工厂

  1. 避免Spring框架的侵⼊

  2. 整合遗留系统

遗留代码(已经实现数据库连接获取)

public class ConnectionFactory {  
  
    // 老旧系统遗留代码,已经实现数据库连接获取,现在需要spring整合它  
    public Connection getConnection() {  
        Connection con = null;  
        try {  
            Class.forName("com.mysql.jdbc.Driver");  
            con = DriverManager.getConnection(  
                    "jdbc:mysql://192.168.3.100:3306/security?useSSL=false", "root", "Sun123456+");  
        } catch (ClassNotFoundException e) {  
            e.printStackTrace();  
        } catch (SQLException e) {  
            e.printStackTrace();  
        }  
        return con;  
    }  
}

开发步骤

在spring配置文件中,把实例bean创建,再指定创建连接的实例需要从已有实例工厂中的指定方法创建

<bean id="connFcatory" class="com.sunzhen.factory.ConnectionFactory"/>  
<bean id="conn" factory-bean="connFcatory" factory-method="getConnection"/>

2.3 静态工厂

代码

public class StaticConnectionFactory {  
    public static Connection getConnection(){  
        Connection con = null;  
        try {  
            Class.forName("com.mysql.jdbc.Driver");  
            con = DriverManager.getConnection(  
                    "jdbc:mysql://192.168.3.100:3306/security?useSSL=false", "root", "Sun123456+");  
        } catch (ClassNotFoundException e) {  
            e.printStackTrace();  
        } catch (SQLException e) {  
            e.printStackTrace();  
        }  
        return con;  
    }  
}

配置文件

<bean id="staticConn" class="com.sunzhen.factory.StaticConnectionFactory" factory-method="getConnection"/>

静态工厂不需要创建工厂实例,直接指定对应的方法即可 也是为了避免 spring框架侵入

Spring⼯⼚创建对象的总结

Pasted image 20230924181147.png

控制Spring⼯⼚创建对象的次数

如何控制简单对象的创建次数

<bean id="account" scope="singleton|prototype" class="xxxx.Account"/> 
sigleton:只会创建⼀次简单对象 默认值 
prototype:每⼀次都会创建新的对象

如何控制复杂对象的创建次数

public class FactoryBean{ 
    public boolean isSingleton(){
        return true 只会创建⼀次 
        return false 每⼀次都会创建新的 
    } 
} 
如没有isSingleton⽅法 还是通过scope属性 进⾏对象创建次数的控制

为什么要控制对象的创建次数?

好处:节省不必要的内存浪费

什么样的对象只创建⼀次?

1. SqlSessionFactory 
2. DAO 
3. Service

什么样的对象 每⼀次都要创建新的?

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


230925-⼯⼚⾼级特性

1、对象的生命周期

Pasted image 20230925215216.png

  • 什么是对象的⽣命周期

指的是⼀个对象创建、存活、消亡的⼀个完整过程

  • 为什么要学习对象的⽣命周期

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

  • 生命周期的3个阶段

1、创建阶段

Spring⼯⼚何时创建对象

  1. scope="singleton"

Spring⼯⼚创建的同时,对象的创建

注意:设置scope=singleton 这种情况下 也需要在获取对象的同时,创建对象

<bean lazy-init="true"/>
  1. scope="prototype"

Spring⼯⼚会在获取对象的同时,创建对象 ctx.getBean("")

2、初始化阶段

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

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

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

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

1. InitializingBean
2. 普通初始化⽅法
  1. 注⼊⼀定发⽣在初始化操作的前⾯(先set/构造注入,再init初始化方法)

  2. 什么叫做初始化操作

资源的初始化:数据库 IO ⽹络 .....

3、销毁阶段

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

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

public void myDestroy()throws Exception{
}

// spring的配置文件中
<bean id="" class="" init-method="" destroymethod="myDestroy"/>
  • 细节分析

1. 销毁⽅法的操作只适⽤于 scope="singleton"
2. 什么叫做销毁操作
	主要指的就是 资源的释放操作 io.close() connection.close();

2、配置⽂件参数化

把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
// 把spring配置文件和对应的小配置文件关联起来
<context:property-placeholder location="classpath:/db.properties"/>
  • 在Spring配置⽂件中通过${key}获取⼩配置⽂件中对应的值Pasted image 20230925221400.png

230927-工厂高级特性

1、⾃定义类型转换器

1、类型转换器

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

Pasted image 20230927091847.png

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>1997-10-26</value>           return : 当把转换好的Date作为convert⽅法的返回值后,Spring⾃动的为day属性进⾏注⼊(赋值)  
         */    @Override  
    public Date convert(String source) {  
        Date date = null;  
        try {  
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");  
            date = format.parse(source);  
        } catch (ParseException e) {  
            e.printStackTrace();  
        }  
        return date;  
    }  
}
  • 在Spring的配置⽂件中进⾏配置 1、MyDateConverter对象创建出来

    <!--    创建自定义的类型转换器-->  
      <bean id="myConverter" class="com.sunzhen.converter.MyDateConverter"/>  
    2、类型转换器的注册
    ```xml
<!-- ⽬的:告知Spring框架,我们所创建的MyDateConverter是⼀个类型转换器 -->  
<!-- ⽤于注册类型转换器 -->  
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">  
    <property name="converters">  
        <set>  
            <ref bean="myConverter"/>  
        </set>  
    </property>  
</bean>
  • 细节

MyDateConverter中的⽇期的格式,通过依赖注⼊的⽅式,由配置⽂件完成赋值。

public class MyDateConverter implements Converter<String, Date> {  
  
    // 通过 依赖注入DI 解耦合  
    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>1997-10-26</value>           return : 当把转换好的Date作为convert⽅法的返回值后,Spring⾃动的为day属性进⾏注⼊(赋值)  
         */    @Override  
    public Date convert(String source) {  
        Date date = null;  
        try {  
            SimpleDateFormat format = new SimpleDateFormat(pattern);  
            date = format.parse(source);  
        } catch (ParseException e) {  
            e.printStackTrace();  
        }  
        return date;  
    }  
}
<!--    创建自定义的类型转换器-->  
    <bean id="myConverter" class="com.sunzhen.converter.MyDateConverter">  
        <property name="pattern" value="yyyy-MM-dd"/>  
    </bean>

1、ConversionSeviceFactoryBean 定义 id属性 值必须 conversionService 2、Spring框架内置⽇期类型的转换器 ⽇期格式:2020/05/01 (不⽀持 :2020-05-01)

2、后置处理Bean

BeanPostProcessor作⽤:对Spring⼯⼚所创建的对象,进⾏再加⼯。 
AOP底层实现: 
​
注意:BeanPostProcessor接⼝ 
        xxxx(){ 
        
        }

1、后置处理Bean的运⾏原理分析

Pasted image 20230927153033.png

程序员实现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对象

2、BeanPostProcessor的开发步骤

  • 类实现 BeanPostProcessor接⼝

public class MyBeanPostProcessor implements BeanPostProcessor {  
  
    /**  
     * 自己定义在spring初始化完成前需要对bean进行的操作  
     * @param bean  
     * @param beanName  
     * @return  
     * @throws BeansException  
     */  
    @Override  
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {  
        return bean;  
    }  
  
    /**  
     * 自己定义在spring初始化完成后需要对bean进行的操作  
     * @param bean  
     * @param beanName  
     * @return  
     * @throws BeansException  
     */  
    @Override  
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {  
        if (bean instanceof Category){  
            Category category = (Category) bean;  
            category.setName("大狗子!!!");  
        }  
        return bean;  
    }  
}
  • Spring的配置⽂件中进⾏配置

<bean id="processor" class="com.sunzhen.processor.MyBeanPostProcessor"/>
  • BeanPostProcessor细节 BeanPostProcessor会对Spring⼯⼚中所有创建的对象进⾏加⼯。

230927-AOP编程

1、静态代理设计模式

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

1.1 问题

  • 在JavaEE分层开发开发中,那个层次对于我们来讲最重要

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

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

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

2、代理设计模式

1、概念

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

2、名词解释

1. ⽬标类 原始类 
   指的是 业务类 (核⼼功能 --> 业务运算 DAO调⽤) 
2. ⽬标⽅法,原始⽅法 
   ⽬标类(原始类)中的⽅法 就是⽬标⽅法(原始⽅法) 
3. 额外功能 (附加功能) 
   ⽇志,事务,性能

3、代理开发的核⼼要素

代理类 = 1、⽬标类(原始类) + 2、额外功能 + 3、原始类(⽬标类)实现相同的接⼝ 
房东 --->    public interface UserService{ 
                        m1 
                        m2 
            } 
            UserServiceImpl implements UserService{ 
                        m1 ---> 业务运算 DAO调⽤ 
                        m2 
            } 
            UserServiceProxy implements UserService 
                m1 
                m2

4、编码

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

5、静态代理存在的问题

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

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 interface UserService {  
  
    public void register(User user);  
    public void login(String name, String password);  
}
public class UserServiceImpl implements UserService{  
    @Override  
    public void register(User user) {  
        System.out.println("UserServiceImpl.register 业务运算 + DAO ");  
    }  
  
    @Override  
    public void login(String name, String password) {  
        System.out.println("UserServiceImpl.login ");  
    }  
}

spring工厂创建

<bean id="userService" class="com.sunzhen.proxy.UserServiceImpl"/>

2、额外功能 MethodBeforeAdvice接⼝

额外的功能书写在接⼝的实现中,运⾏在原始⽅法执⾏之前运⾏额外功能。

public class Before implements MethodBeforeAdvice {  
  
    /**  
    作⽤:需要把运⾏在原始⽅法执⾏之前运⾏的额外功能,书写在before⽅法中  
    */  
    @Override  
    public void before(Method method, Object[] objects, Object o) throws Throwable {  
        System.out.println("-----method before advice log-----");  
    }  
}

spring工厂创建

<!-- 额外功能 MethodBeforeAdvice接⼝ 的实例创建 -->  
<bean id="before" class="com.sunzhen.proxy.Before"/>

3、定义切⼊点

切⼊点:额外功能加⼊的位置

⽬的:由程序员根据⾃⼰的需要,决定额外功能加⼊给那个原始⽅法
register
login

简单的测试:所有⽅法都做为切⼊点,都加⼊额外的功能。
<aop:config>  
    <!-- 切⼊点:额外功能加⼊的位置 -->  
    <aop:pointcut id="pc" expression="execution(* *(..))"/>  
</aop:config>
execution(* *(..))表达式表示所有的方法都作为切入点,这些切入点统一使用id=“pc”来表示

4、组装 (2 3整合) 也称为 编织

<aop:config>  
    <!-- 切⼊点:额外功能加⼊的位置 -->  
    <aop:pointcut id="pc" expression="execution(* *(..))"/>  
    <!-- 组装:切入点和增强功能的编织-->  
    <aop:advisor advice-ref="before" pointcut-ref="pc"/>  
</aop:config>

这里是 aop:advisor 标签中的内容

5、调⽤

public class TestProxy {  
  
    @Test  
    public void test1(){  
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext1.xml");  
        // ⽬的:获得Spring⼯⼚创建的动态代理对象,并进⾏调⽤  
        /**  
         * 注意:  
         *  1. Spring的⼯⼚通过原始对象的id值获得的是代理对象  
         *  2. 获得代理对象后,可以通过声明接⼝类型,进⾏对象的存储  
         */  
        UserService userService = (UserService)context.getBean("userService");  
        userService.register(new User());  
        userService.login("sunzhen","123456");  
    }  
}

sout:

-----method before advice log-----
UserServiceImpl.register 业务运算 + DAO 
-----method before advice log-----
UserServiceImpl.login 

3、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⽅法的参数,在实战中,会根据需要进⾏使⽤,不⼀定都会⽤到,也有可能都不⽤。
// 比如web应用中对请求和响应的处理
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("-----额外功能 前----");  
        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. * *(..) 切⼊点表达式

1、切⼊点表达式

1、⽅法切⼊点表达式

Pasted image 20230928154110.png

* *(..) --> 所有⽅法 

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

* login(..) 

# 定义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.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..*.*(..)
这里..*表示proxy下的任何类及其任何子包中的任何类

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(..))

4、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、切⾯的名词解释

切⾯ = 切⼊点 + 额外功能 

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

5、AOP的底层实现原理

1、核⼼问题

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

2、动态代理类的创建

1、JDK的动态代理

  • Proxy.newProxyInstance⽅法参数详解Pasted image 20230928171742.png
    Pasted image 20230928171757.png

JDK动态代理主要使用 Proxy.newProxyInstance 完成代理,其中第一个参数是借用的ClassLoader,第二个参数是相同的接口,第三个接口是功能增强和组装(主要是由InvocationHandler接口完成)

1、代码实现

package com.sunzhen.jdk;  
  
import com.sunzhen.User;  
import com.sunzhen.proxy.UserService;  
import com.sunzhen.proxy.UserServiceImpl;  
  
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Method;  
import java.lang.reflect.Proxy;  
  
/**  
 * @author sunzhen  
 * @version 1.0  
 * @date 2023/10/7 15:53  
 */  
/**  
 * 利用JDK的动态代理,实现代理类增强  
 */  
public class TestJDKProxy {  
  
    public static void main(String[] args) {  
  
        UserService userService = new UserServiceImpl();  
  
        InvocationHandler handler = new InvocationHandler() {  
            /**  
             * 参数说明  
             * @param proxy 可忽略,代表的是代理对象  
             * @param method 额外功能 所增加给的那个原始方法  
             * @param args 原始方法的参数  
             * @return  
             * @throws Throwable  
             */  
            @Override  
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
  
                System.out.println("log - jdk - proxy - log");  
  
                Object res = method.invoke(userService, args);  
  
                return res;  
            }  
        };  
  
        /**  
         * 三个参数  
         * 1、借用类加载器--》用于创建代理类,因为代理类没有自己的.class文件,jvm不会分配类加载器给代理类  
         * 2、原始接口--》 需要提供原始接口  
         * 3、InvocationHandler --》 主要的增强步骤,通过invoke方法实现代理增强  
         */  
        UserService userServiceProxy =  
                (UserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), userService.getClass().getInterfaces(), handler);  
  
        userServiceProxy.login("sunzhen","123456");  
        userServiceProxy.register(new User());  
    }  
}

2、CGlib的动态代理

CGlib创建动态代理的原理:⽗⼦继承关系创建代理对象,原始类作为⽗类,代理类作为⼦类, 这样既可以保证2者⽅法⼀致,同时在代理类中提供新的实现(额外功能+原始⽅法)

Pasted image 20231007170655.png

1、代码实现

package com.sunzhen.cglib;  
  
import com.sunzhen.User;  
import org.springframework.cglib.proxy.Enhancer;  
import org.springframework.cglib.proxy.MethodInterceptor;  
import org.springframework.cglib.proxy.MethodProxy;  
  
import java.lang.reflect.Method;  
  
/**  
 * @author sunzhen  
 * @version 1.0  
 * @date 2023/10/7 16:50  
 */public class TestCGLIBProxy {  
    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(Thread.currentThread().getContextClassLoader());  
        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 - proxy - ");  
                Object res = method.invoke(userService, args);  
                return res;  
            }  
        };  
  
        enhancer.setCallback(interceptor);  
  
        UserService userServiceProcy = (UserService)enhancer.create();  
  
        userServiceProcy.register(new User());  
        userServiceProcy.login("sunzhen","123456");  
    }  
}

3、总结

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

3、Spring工厂如何加工原始对象

通过BeanPostProcessor的加工所有spring工厂对象的特性,结合JDK或者CGLIB动态代理,实现无感的增强

  • 思路分析 Pasted image 20231007174006.png

  • 代码实现

1、创建接口和实现类

package com.sunzhen.processor;  
  
import com.sunzhen.User;  
  
/**  
 * @author sunzhen  
 * @version 1.0  
 * @date 2023/10/7 17:46  
 */public interface UserService {  
  
    public void register(User user);  
    public void login(String name, String password);  
}
package com.sunzhen.processor;  
  
import com.sunzhen.User;  
  
/**  
 * @author sunzhen  
 * @version 1.0  
 * @date 2023/10/7 17:47  
 */public class UserServiceImpl implements UserService{  
  
    @Override  
    public void register(User user){  
        System.out.println("register by class");  
    }  
  
    @Override  
    public void login(String userName, String password){  
        System.out.println("login by class -- userName " + userName + " ; password " + password);  
    }  
}

2、创建BeanPostProcessor实现类

直接在postProcessAfterInitialization中使用JDK动态代理实现无感代理

package com.sunzhen.processor;  
  
import org.springframework.beans.BeansException;  
import org.springframework.beans.factory.config.BeanPostProcessor;  
  
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Method;  
import java.lang.reflect.Proxy;  
  
/**  
 * @author sunzhen  
 * @version 1.0  
 * @date 2023/10/7 17:40  
 */public class ProxyBeanPostProcessor 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 handler = new InvocationHandler(){  
            @Override  
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
                System.out.println("-- log -- new --");  
                Object res = method.invoke(bean, args);  
                return res;  
            }  
        };  
        return Proxy.newProxyInstance(ProxyBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(),handler);  
    }  
}

3、在xml中配置BeanPostProcessor

<bean id="userService" class="com.sunzhen.proxy.UserServiceImpl"/>  
<bean id="processor" class="com.sunzhen.processor.ProxyBeanPostProcessor"/>  

6、基于注解的AOP编程

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

  1. 原始对象

  2. 额外功能

  3. 切⼊点

  4. 组装切⾯

package com.sunzhen.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

/**
 * @author sunzhen
 * @version 1.0
 * @date 2023/10/7 21:56
 */
@Aspect
public class MyAspect {
    /**
     /*
     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(..))"/>
     */
    // 以下自定义的arround方法就相当于是额外功能的实现,ProceedingJoinPoint相当于是MethodInvocation
    // @Around相当于是配置文件中定义的切入点

    @Around("execution(* login(..))")
    public Object arround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("----aspect log ------");
        Object ret = joinPoint.proceed();
        return ret;
    }
}

在xml中注入bean,并告知spring使用注解来完成aop

<!--
    1、此配置文件是用于测试基于注解实现切面编程
  -->
<bean id="userService" class="com.sunzhen.aspect.UserServiceImpl"/>
<!--
     切⾯
     1. 额外功能
     2. 切⼊点
     3. 组装切⾯
 -->
<bean id="arround" class="com.sunzhen.aspect.MyAspect"/>
<!--告知Spring基于注解进⾏AOP编程-->
<aop:aspectj-autoproxy />

2、细节

1、切入点复用

切⼊点复⽤:在切⾯类中定义⼀个函数 上⾯@Pointcut注解 通过这种⽅式,定义切 ⼊点表达式,后续更加有利于切⼊点复⽤。

package com.sunzhen.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

/**
 * @author sunzhen
 * @version 1.0
 * @date 2023/10/7 21:56
 */
@Aspect
public class MyAspect {
    /**
     /*
     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(..))"/>
     */
    // 以下自定义的arround方法就相当于是额外功能的实现,ProceedingJoinPoint相当于是MethodInvocation
    // @Around相当于是配置文件中定义的切入点

    @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 - 设置为true即可,默认是false --》 jdk
 1. 基于注解AOP开发
 <aop:aspectj-autoproxy proxy-target-class="true" />
 2. 传统的AOP开发
 <aop:config proxy-target-class="true">
 </aop>

7、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;
    }
}

8、AOP阶段知识总结

Pasted image 20231008152231.png

231007-持久层整合

1、持久层整合

1、Spring框架为什么要与持久层技术进⾏整合

1. JavaEE开发需要持久层进⾏数据库的访问操作。
2. JDBC Hibernate MyBatis进⾏持久开发过程存在⼤量的代码冗余
3. Spring基于模板设计模式对于上述的持久层技术进⾏了封装

2、Spring可以与那些持久层技术进⾏整合?

1. JDBC
    |- JDBCTemplate
2. Hibernate (JPA)
    |- HibernateTemplate
3. MyBatis
    |- SqlSessionFactoryBean MapperScannerConfigure

2、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整合思路分析

其实整合的目的就是把在mybatis-config.xml中进行配置的各种信息,使用spring的配置进行实现,主要包括1、datasource的数据库连接2、typeAliases类型别名3、Mapper文件注册4、Dao文件的创建 主要的思路代码剖析见下图

Pasted image 20231016190526.png

4、Spring与Mybatis整合的开发步骤

1、配置⽂件(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>

2、编码

# 实战经常根据需求 写的代码 
1. 实体 
2. 表 
3. 创建DAO接⼝ 
4. 实现Mapper⽂件
​
从原本的7步节俭为4步,其他都由spring处理

5、Spring与Mybatis整合编码

1、搭建开发环境(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>

2、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>

3、编码

1. 实体 
2. 表 
3. DAO接⼝ 
4. Mapper⽂件配置

6、Spring与Mybatis整合细节

  • 问题:Spring与Mybatis整合后,为什么DAO不提交事务,但是数据能够插⼊数据库中?

Connection --> tx 
Mybatis(Connection) 

本质上控制连接对象(Connection) ---> 连接池(DataSource) 
1. Mybatis提供的连接池对象 ---> 创建Connection 
	Connection.setAutoCommit(false) ⼿⼯的控制了事务 , 操作完成后,⼿ ⼯提交 

1. Druid(C3P0 DBCP)作为连接池 ---> 创建Connection 
	Connection.setAutoCommit(true) true默认值 保持⾃动控制事务,⼀条 sql ⾃动提交 

答案:因为Spring与Mybatis整合时,引⼊了外部连接池对象,保持⾃动的事务提交这 个机制(Connection.setAutoCommit(true)),不需要⼿⼯进⾏事务的操作,也能进 ⾏事务的提交 

注意:未来实战中,还会⼿⼯控制事务(多条sql⼀起成功,⼀起失败),后续Spring通过事务控制解决这个问题。

3、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
spring为了整合mybatis的事务功能,进行了统一的AOP增强,就是DataSourceTransactionManager,其本质也就是AOP

以往的AOP我们一般使用MethodInterceptor来进行增强
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. 额外功能

spring整合mybatis使用org.springframework.jdbc.datasource.DataSourceTransactionManager作为功能增强,使用@Transactional指定切入点,使用以下配置进行组装切面
<tx:annotation-driven transaction-manager=""/>

4、Spring控制事务的编码

1、搭建开发环境

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

2、编码

<bean id="userService" class="com.sunzhen.service.impl.UserServiceImpl">  
    <property name="userDao" ref="userDao"/>  
</bean>  
<!--DataSourceTransactionManager-->  
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
    <property name="dataSource" ref="dataSource"/>  
</bean>  
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>

spring的配置文件中做以上配置

public class UserServiceImpl implements UserService {  
  
  
    private UserDao userDao;  
  
    @Transactional  
    @Override    public void register(User user){  
        userDao.save(user);  
//        throw new NullPointerException();  
    }  
  
    public UserDao getUserDao() {  
        return userDao;  
    }  
  
    public void setUserDao(UserDao userDao) {  
        this.userDao = userDao;  
    }  
}

java中需要使dao成为成员变量,并提供对应的set方法

3、细节

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

4、Spring中的事务属性(Transaction Attribute)

4.1 什么是事务属性

属性:描述物体特征的⼀系列值 
	性别 身⾼ 体重 ... 
事务属性:描述事务特征的⼀系列值 

1. 隔离属性 
2. 传播属性 
3. 只读属性 
4. 超时属性 
5. 异常属性

4.2 如何添加事务属性

@Transactional(isloation=,propagation=,readOnly=,timeout=,rollbackFor=, noRollbackFor=,)

4.3 事务属性详解

4.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

  • 数据库对于隔离属性的⽀持Pasted image 20231019172820.png

Oracle不⽀持REPEATABLE_READ值 如何解决不可重复读 采⽤的是多版本⽐对的⽅式 解决不可重复读的问题

  • 默认隔离属性

ISOLATION_DEFAULT:会调⽤不同数据库所设置的默认隔离属性

MySQL : REPEATABLE_READ Oracle: READ_COMMITTED

  • 隔离属性在实战中的建议

推荐使⽤Spring指定的ISOLATION_DEFAULT

  1. MySQL repeatable_read

  2. Oracle read_commited 未来实战中,并发访问情况 很低

如果真遇到并发问题,乐观锁 Hibernate(JPA) Version MyBatis 通过拦截器⾃定义开发

4.3.2 传播属性(PROPAGATION)

  • 传播属性的概念

概念:他描述了事务解决嵌套问题的特征

什么叫做事务的嵌套:他指的是⼀个⼤的事务中,包含了若⼲个⼩的事务

问题:⼤事务中融⼊了很多⼩的事务,他们彼此影响,最终就会导致外部⼤的事务,丧失了 事务的原⼦性

  • 传播属性的值及其⽤法Pasted image 20231019174827.png
    Pasted image 20231019174847.png

  • 默认的传播属性

REQUIRED是传播属性的默认值

  • 推荐传播属性的使⽤⽅式

增删改 ⽅法:直接使⽤默认值REQUIRED

查询 操作:显示指定传播属性的值为SUPPORTS

4.3.3 只读属性(readOnly)

针对于只进⾏查询操作的业务⽅法,可以加⼊只读属性,提供运⾏效率 默认值:false

4.3.4 超时属性(timeout)

指定了事务等待的最⻓时间 
1. 为什么事务进⾏等待? 
	当前事务访问数据时,有可能访问的数据被别的事务进⾏加锁的处理,那么此时本事务就必须 进⾏等待。 
2. 等待时间 秒
3. 如何应⽤ @Transactional(timeout=2) 
4. 超时属性的默认值 -1 
	最终由对应的数据库来指定

4.3.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.4 事务属性常⻅配置总结

1. 隔离属性 默认值 
2. 传播属性 Required(默认值) 增删改 Supports 查询操作 
3. 只读属性 readOnly false 增删改 true 查询操作 
4. 超时属性 默认值 -1 
5. 异常属性 默认值 

增删改操作 @Transactional 
查询操作 @Transactional(propagation=Propagation.SUPPORTS,readOnly=true)

4.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.DataSourceTransactionManage r"> 
    <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.DataSourceTransactionManage r"> 
    <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.DataSourceTransactionMa nager"> 
    <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>

231019-MVC整合

一、MVC框架整合思想

1、搭建Web运⾏环境

<dependencies>  
  <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>  
  <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/springcontext-->  
  <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>  
</dependencies>

2、为什么要整合MVC框架

1. MVC框架提供了控制器(Controller)调⽤Service DAO ---》 Service 1. 请求响应的处理 2. 接受请求参数 request.getParameter("") 3. 控制程序的运⾏流程 4. 视图解析 (JSP JSON Freemarker Thyemeleaf )

3、Spring可以整合那些MVC框架

  1. struts1

  2. webwork

  3. jsf

  4. struts2

  5. springMVC

4、Spring整合MVC框架的核⼼思路

4.1 准备⼯⼚

1. Web开发过程中如何创建⼯⼚
    ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
    MVC提供的工厂:WebXmlApplicationContext()
2. 如何保证⼯⼚唯⼀同时被共⽤
    被共⽤:Web request|session|ServletContext(application)
    ⼯⼚存储在ServletContext这个作⽤域中
    ServletContext.setAttribute("xxxx",ctx);
    唯⼀:ServletContext对象 创建的同时 ---》 ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
    ServletContextListener(监听ServletContext对象的创建,所以把工厂放到这里创建可以保证唯一性) ---> ApplicationContext ctx = new
ClassPathXmlApplicationContext("/applicationContext.xml");
    ServletContextListener 在ServletContext对象创建的同时,被调⽤(只会
被调⽤⼀次) ,把⼯⼚创建的代码,写在ServletContextListener中,也会保证只调⽤
⼀次,最终⼯⼚就保证了唯⼀性
​
3. 总结
    ServletContextListener(唯⼀)
    ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
    ServletContext.setAttribute("xxx",ctx) (共⽤)
​
既然我们可以想到如何在MVC中创建唯一的工厂对象,那么Spring也能想到,所以它提供了一个现成的封装类
4. Spring封装了⼀个ContextLoaderListener
    1. 创建⼯⼚
    2. 把⼯⼚存在ServletContext中
ContextLoaderListener使⽤⽅式
​
web.xml
​
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
​
// 这里指明ContextLoaderListener要读取哪个配置文件去创建唯一公用的工厂
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>

4.2 代码整合

依赖注⼊:把Sevice对象注⼊个控制器对象。Pasted image 20231019212329.png

二、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</paramvalue>
<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>


231020-注解编程

一、注解基础概念

1、什么是注解编程

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

2、为什么要讲解注解编程

1. 注解开发⽅便 代码简洁 开发速度⼤⼤提⾼ 2. Spring开发潮流 Spring2.x引⼊注解 Spring3.x完善注解 SpringBoot普及 推⼴注解编程

3、注解的作⽤

  • 替换XML这种配置形式,简化配置 Pasted image 20231020105115.png

  • 替换接⼝,实现调⽤双⽅的契约性

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

Pasted image 20231020105254.png

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内容

    Pasted image 20231024163246.png

    • @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、注⼊相关注解

2.1 ⽤户⾃定义类型 @Autowired

Pasted image 20231024221542.png

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

2.2 JDK类型

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

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

    • @Value注解不能应⽤在静态成员变量上 如果应⽤,赋值(注⼊)失败

    • @Value注解+Properties这种⽅式,不能注⼊集合类型 Spring提供新的配置形式 YAML YML (SpringBoot)

3、注解扫描详解

当前spring中使用的扫描配置: <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-defaultfilters="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-defaultfilters="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

三、Spring的⾼级注解(Spring3.x 及以上)--注解替换标签

1、配置Bean

Spring在3.x提供的新的注解,⽤于替换XML配置⽂件。

@Configuration
public class AppConfig{

}
  1. 配置Bean在应⽤的过程中 替换了XML具体什么内容呢?

使用@Configuration注解替换xml文件Pasted image 20231026195002.png

  1. AnnotationConfigApplicationContext

使用了新的创建工厂的方式

1. 创建⼯⼚代码
	ApplicationContext ctx = new AnnotationConfigApplicationContext();
	
2. 指定配置⽂件
	1. 指定配置bean的Class
	ApplicationContext ctx = new
	AnnotationConfigApplicationContext(AppConfig.class);
	2. 指定配置bean所在的路径
	ApplicationContext ctx = new
	AnnotationConfigApplicationContext("com.baizhiedu");
  • @Configuration注解的本质

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

可以应⽤<context:component-scan进⾏扫描

2、@Bean注解

@Bean注解在配置bean中进⾏使⽤,等同于XML配置⽂件中的<bean标签

1、@Bean注解的基本使⽤

1.1 对象的创建

Pasted image 20231026201039.png

1. 简单对象
	直接能够通过new⽅式创建的对象
	User UserService UserDAO
2. 复杂对象
	不能通过new的⽅式直接创建的对象
	Connection SqlSessionFactory

1.2 ⾃定义id值

@Bean("id")

1.3 控制对象创建次数

@Bean
@Scope("singleton|prototype") 默认值 singleton

2、@Bean注解的注⼊

2.1 ⽤户⾃定义类型

@Bean  
public UserDao userDao(){  
    return new UserDaoImpl();  
}  
  
@Bean  
public UserService userService(){  
    UserServiceImpl userService = new UserServiceImpl();  
    userService.setUserDao(userDao());  
    return userService;  
}

2.2 JDK类型的注⼊

@Bean  
public User user(){  
    User user = new User();  
    user.setAge(18);  
    user.setName("sunzhen");  
    return user;  
}

2.3 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)

3.1 基本使⽤

@Configuration
@ComponentScan(basePackages = "com.baizhiedu.scan")
public class AppConfig2 {
}

等同于:
<context:component-scan base-package=""/>

3.2 排除、包含的使⽤

  • 排除

1、标签的扫描配置-排除
<context:component-scan base-package="com.baizhiedu">
	<context:exclude-filter type="assignable" expression="com.baizhiedu.bean.User"/>
</context:component-scan>

2、替代标签的注解使用
@ComponentScan(basePackages = "com.baizhiedu.scan", 
excludeFilters = {@ComponentScan.Filter(type= FilterType.ANNOTATION,value={Service.class}),
				  @ComponentScan.Filter(type= FilterType.ASPECTJ,pattern = "*..User1")}
				  )

type枚举和参数的对应关系:
	type = FilterType.ANNOTATION         value
					  ASSIGNABLE_TYPE    value
					  ASPECTJ            pattern
					  REGEX              pattern
					  CUSTOM             value
  • 包含

1、标签的扫描配置-包含
<context:component-scan base-package="com.baizhiedu" use-defaultfilters="false">
<context:include-filter type="" expression=""/>
</context:component-scan>


2、替代标签的注解使用
@ComponentScan(basePackages = "com.baizhiedu.scan", 
			   useDefaultFilters = false,
			   includeFilters = 
			   {@ComponentScan.Filter(type= FilterType.ANNOTATION,value={Service.class})}
			   )
			   
type枚举和参数的对应关系:
	type = FilterType.ANNOTATION         value
					  ASSIGNABLE_TYPE    value
					  ASPECTJ            pattern
					  REGEX              pattern
					  CUSTOM             value

4、Spring⼯⼚创建对象的多种配置⽅式

4.1 多种配置⽅式的应⽤场景

Pasted image 20231027123926.png

4.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"/>

可以使用@ImportResource注解把spring配置文件和@Configuration的配置类结合使用

5、整合多个配置信息

  • 为什么会有多个配置信息 拆分多个配置bean的开发,是⼀种模块化开发的形式,也体现了⾯向对象各司其职的设计思想(比如数据库单独一个MybatisConfig,redis一个单独的RedisConfig配置文件)

  • 多配置信息整合的⽅式

    • 多个配置Bean的整合

    • 配置Bean与@Component相关注解的整合

    • 配置Bean与SpringXML配置⽂件的整合

  • 整合多种配置需要关注那些要点

    • 如何使多配置的信息 汇总成⼀个整体

    • 如何实现跨配置的注⼊

5.1 多个配置Bean的整合

  • 多配置的信息汇总

    • base-package进⾏多个配置Bean的整合 Pasted image 20231027125214.png创建工厂的时候使用包去创建即可

    • @Import

    1. 可以创建对象

    2. 多配置bean的整合Pasted image 20231027125136.png多个配置文件使用@Import进行导入即可

    • 在⼯⼚创建时,指定多个配置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();
	}
}

5.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);

只需要使用@ComponentScan扫描@Component注解即可注入

5.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);

使用@ImportResource导入spring配置文件即可整合

6、配置Bean底层实现原理

Spring在配置Bean中加⼊了@Configuration注解后,底层就会通过Cglib的代理⽅式,来进 ⾏对象相关的配置、处理 Pasted image 20231027130311.png

7、四维⼀体的开发思想

7.1 什么是四维⼀体

Spring开发⼀个功能的4种形式,虽然开发⽅式不同,但是最终效果是⼀样的。
1. 基于schema
2. 基于特定功能注解
3. 基于原始<bean
4. 基于@Bean注解

7.2 四维⼀体的开发案例

1. <context:property-placehoder
2. @PropertySource 【推荐】
3. <bean id="" class="PropertySourcePlaceholderConfigure"/>
4. @Bean 【推荐】

导入配置文件的方式就有四种

8、纯注解版AOP编程

8.1 搭建环境

1. 应⽤配置Bean
2. 注解扫描

8.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上

8.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整合

9.1 基础配置 (配置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中的@Bean的组件
	@Bean
	public DataSource dataSource(){
		DruidDataSource dataSource = new DruidDataSource();
		dataSource.setDriverClassName("");
		dataSource.setUrl();
		...
		return dataSource;
	}

1. 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中的@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>
	# 需要配置在配置文件中的 替换成配置Bean中的@Bean的组件
	@MapperScan(basePackages={"com.baizhiedu.dao"}) ---> 配置bean完成

9.2 编码

1. 实体
2. 表
3. DAO接⼝
4. Mapper⽂件

9.2.1 MapperLocations编码时通配的写法

//设置Mapper⽂件的路径
1. 单独的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>

2. ⼀组Mapper⽂件
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("com.baizhi.mapper/*Mapper.xml");
sqlSessionFactoryBean.setMapperLocations(resources)

9.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;
}

再把配置类注入Config类中

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.gtTypeAliasesPackages());
		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.DataSourceTransactionManage
r">
	<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 transactionmanager="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的使⽤

11.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.

11.2 Properties进⾏配置问题

1. Properties表达过于繁琐,⽆法表达数据的内在联系.
2. Properties⽆法表达对象 集合类型

11.3 YML语法简介

1. 定义yml⽂件
	xxx.yml xxx.yaml
2. 语法
	1. 基本语法
		name: suns
		password: 123456
	2. 对象概念
	account:
		id: 1
		password: 123456
	3. 定义集合
		service:
			- 11111
			- 22222

11.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注解 注⼊

11.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注解

11.6 Spring与YML集成的问题

1. 集合处理的问题
SpringEL表达式解决
@Value("#{'${list}'.split(',')}")
2. 对象类型的YAML进⾏配置时 过于繁琐
@Value("${account.name}")
​
SpringBoot @ConfigurationProperties