资料来源:https://www.bilibili.com/video/BV185411477k?p=63

第一部分 — Spring工厂


第一章 引言

1. EJB存在的问题

image.png

2. 什么是Spring

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

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

image.png

  • 整合设计模式
  1. 工厂
    2. 代理
    3. 模板
    4. 策略

    3. 设计模式

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

    4. 工厂设计模式

    4.1 什么是工厂设计模式

  3. 概念:通过工厂类,创建对象
    User user = new User();
    UserDAO userDAO = new UserDAOImpl();
    2. 好处:解耦合
    耦合:指定是代码间的强关联关系,一方的改变会影响到另一方
    问题:不利于代码维护
    简单:把接口的实现类,硬编码在程序中
    UserService userService = new UserServiceImpl();

    4.2 简单工厂的设计

    ```java package com.baizhiedu.basic;

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

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

  1. static{
  2. try {
  3. //第一步:获得IO输入流
  4. InputStream inputStream = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
  5. //第二步:文件内容封装到Properties集合中:key = userService、value = com.baizhixx.UserServiceImpl
  6. env.load(inputStream);
  7. inputStream.close();
  8. } catch (IOException e) {
  9. e.printStackTrace();
  10. }
  11. }
  12. /*
  13. 对象的创建方式:
  14. 1. 直接调用构造方法创建对象:UserService userService = new UserServiceImpl();
  15. 2. 通过反射的形式创建对象:解耦合
  16. Class clazz = Class.forName("com.baizhiedu.basic.UserServiceImpl");
  17. UserService userService = (UserService)clazz.newInstance();
  18. */
  19. public static UserService getUserService() {
  20. UserService userService = null;
  21. try {
  22. //com.baizhiedu.basic.UserServiceImpl
  23. Class clazz = Class.forName(env.getProperty("userService"));
  24. userService = (UserService) clazz.newInstance();
  25. } catch (ClassNotFoundException e) {
  26. e.printStackTrace();
  27. } catch (InstantiationException e) {
  28. e.printStackTrace();
  29. } catch (IllegalAccessException e) {
  30. e.printStackTrace();
  31. }
  32. return userService;
  33. }
  34. public static UserDAO getUserDAO(){
  35. UserDAO userDAO = null;
  36. try {
  37. Class clazz = Class.forName(env.getProperty("userDAO"));
  38. userDAO = (UserDAO) clazz.newInstance();
  39. } catch (ClassNotFoundException e) {
  40. e.printStackTrace();
  41. } catch (InstantiationException e) {
  42. e.printStackTrace();
  43. } catch (IllegalAccessException e) {
  44. e.printStackTrace();
  45. }
  46. return userDAO;
  47. }

}

  1. <a name="TJnsL"></a>
  2. #### 4.3 通用工厂的设计
  3. - 问题
  4. - 简单工厂会存在大量的代码冗余
  5. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/22523384/1637679859727-22318b62-9e2b-48b7-8dea-b8f2df3f0b61.png#clientId=uf6972996-2c65-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=856&id=u48aa9036&margin=%5Bobject%20Object%5D&name=image.png&originHeight=856&originWidth=913&originalType=binary&ratio=1&rotation=0&showTitle=false&size=195018&status=done&style=none&taskId=uc85f6b2d-34ad-4af9-aede-077cf8f9897&title=&width=913)
  6. - 通用工厂的代码
  7. ```java
  8. 创建一切想要的对象
  9. public class BeanFactory{
  10. public static Object getBean(String key){
  11. Object ret = null;
  12. try {
  13. Class clazz = Class.forName(env.getProperty(key));
  14. ret = clazz.newInstance();
  15. } catch (Exception e) {
  16. e.printStackTrace();
  17. }
  18. return ret;
  19. }
  20. }

4.4 通用工厂的使用方式
  1. 定义类型 (类)
    2. 通过配置文件的配置告知工厂(applicationContext.properties)
    key = value
    3. 通过工厂获得类的对象
    Object ret = BeanFactory.getBean(“key”)

    5.总结

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

    第二章、第一个Spring程序

    1. 软件版本

  2. JDK1.8+
    2. Maven3.5+
    3. IDEA2018+
    4. SpringFramework 5.1.4
    官方网站 www.spring.io

    2. 环境搭建

  • Spring的jar包#设置pom 依赖

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

      1. 配置文件的放置位置:任意位置 没有硬性要求
      1. 配置文件的命名 :没有硬性要求 建议:applicationContext.xml

思考:日后应用Spring框架时,需要进行配置文件路径的设置。

3. Spring的核心API

  • ApplicationContext
    • 作用:Spring提供的ApplicationContext这个工厂,用于对象的创建
    • 好处:解耦合
  • ApplicationContext接口类型
    • 接口:屏蔽实现的差异,提供以下两种实现
    • 非web环境 : ClassPathXmlApplicationContext (main junit) - 不启用服务器
    • web环境 : XmlWebApplicationContext

image.png

  • 重量级资源
    • ApplicationContext工厂的对象占用大量内存。
    • 不会频繁的创建对象 : 一个应用只会创建一个工厂对象。
    • ApplicationContext工厂:一定是线程安全的(多线程并发访问)

      4. 程序开发

  1. 创建类型
    2. 配置文件的配置 applicationContext.xml

    3. 获取spring的工厂
    ApplicationContext
    |- ClassPathXmlApplicationContext
    ApplicationContext ctx = new ClassPathXmlApplicationContext(“/applicationContext.xml”);
    4. 通过工厂类,获得对象
    Person person = (Person)ctx.getBean(“person”);

    5. 细节分析

  • 名词解释Spring工厂创建的对象,叫做bean或者组件(componet)

    5.1 Spring工厂的相关的方法

    //通过id=”person”获取对象,需要强转
    Person person = (Person)ctx.getBean(“person”);
    System.out.println(“person = “ + person);

//通过这种方式获得对象,就不需要强制类型转换
Person person = ctx.getBean(“person”, Person.class);

//当前Spring的配置文件中 只能有一个是Person类型
Person person = ctx.getBean(Person.class);

//获取的是Spring工厂配置文件中所有bean标签的id值:person person1
String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(“beanDefinitionName = “ + beanDefinitionName);
}

//根据类型获得Spring配置文件中对应的id值
String[] beanNamesForType = ctx.getBeanNamesForType(Person.class);
for (String id : beanNamesForType) {
System.out.println(“id = “ + id);
}

//用于判断是否存在指定id值得bean
if (ctx.containsBeanDefinition(“a”)) {
System.out.println(“true = “ + true);
}else{
System.out.println(“false = “ + false);
}

//用于判断是否存在指定id值得bean
if (ctx.containsBean(“person”)) {
System.out.println(“true = “ + true);
}else{
System.out.println(“false = “ + false);
}

5.2 配置文件中需要注意的细节

  1. 配置文件的配置 applicationContext.xml
    默认配置bean:


    1. 只配置class属性

    a) 上述这种配置会默认生成id值:com.baizhiedu.basic.Person#0
    b) 应用场景: 如果这个bean只需要使用一次,那么就可以省略id值
    如果这个bean会使用多次,或者被其他bean引用则需要设置id值

  2. name属性
    作用:用于在Spring的配置文件中,为bean对象定义别名(小名)
    相同:
    1. ctx.getBean(“id|name”) —> object,通过id、name都可以获取对象
    2. 等效
    区别:
    1. name可以定义多个(name=”p,p1”),但是id属性只能有一个值
    2. XML的id属性的值,命名要求:必须以字母开头,字母 数字 下划线 连字符 不能以特殊字符开头,如:/person
    name属性的值,命名没有要求,可以使用:/person
    name属性会应用在特殊命名的场景下:/person (spring+struts1)

    XML发展到了今天:ID属性的限制也不存在了,可以使用:/person
    3. 代码
    //用于判断是否存在指定id值得bean,不能判断name值
    if (ctx.containsBeanDefinition(“person”)) {
    System.out.println(“true = “ + true);
    }else{
    System.out.println(“false = “ + false);
    }

    1. //用于判断是否存在指定id值得bean,也可以判断name值<br /> if (ctx.containsBean("p")) {<br /> System.out.println("true = " + true);<br /> }else{<br /> System.out.println("false = " + false);<br /> }

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

    Spring工厂是可以调用对象私有的构造方法创建对象(底层还是通过反射调用私有属性和方法)

    7. 思考

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

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

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

  • Spring如何整合日志框架默认 Spring1.2.3早期都是于commons-logging.jar Spring5.x默认整合的日志框架 logback log4j2 Spring5.x整合log4j(与log4j2差异大) 1. 引入log4j jar包 2. 引入log4.properties配置文件
  • pom//slf4j的作用:为了让spring摒弃默认的日志框架,只支持log4j org.slf4j slf4j-log4j12 1.7.25 log4j log4j 1.2.17
  • log4j.properties# resources文件夹根目录下 ### 配置根,输出模式 log4j.rootLogger = debug,console ### 日志输出到控制台显示 log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.Target=System.out log4j.appender.console.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

    第四章、注入(Injection)

    1. 什么是注入

    通过Spring工厂及配置文件,为所创建对象的成员变量赋值

    1.1 为什么需要注入

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

    1.2 如何进行注入[开发步骤]

  • 类的成员变量必须提供set、get方法

  • 配置spring的配置文件

    10


    xiaojr

    1.3 注入好处

    解耦合

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

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

    第五章、Set注入详解

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

    1. JDK内置类型

    1.1 String+8种基本类型

    suns

    1.2 数组

    suns@zparkhr.com.cn liucy@zparkhr.com.cn chenyn@zparkhr.com.cn

    1.3 Set集合

    (重复数据会过滤掉,set集合无序、不重复) 11111 112222 112222 —里面的标签不一定都为value,应根据set<>泛型的具体类型确定,值可能是set、list、value

    1.4 List集合

    (数据可以重复、按顺序输出) 11111 2222

    1.5 Map集合

    注意: map — entry(对应一个键值对) — key有特定的标签:,内部嵌套具体的类型标签 值根据对应类型选择对应类型的标签 suns //suns是根据key的类型为String 3434334343 chenyn

    1.6 Properites

    Properties类型 特殊的Map key=String value=String
    value1 //省略了value标签 value2

    1.7 复杂的JDK类型 (Date)

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

    2. 用户自定义类型

    2.1 第一种方式

  • 为成员变量提供set get方法

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

    2.2 第二种方式

  • 第一种赋值方式存在的问题1. 配置文件代码冗余 2. 被注入的对象(UserDAO),多次创建,浪费(JVM)内存资源

  • 为成员变量提供set get方法
  • 配置文件中进行配置 #Spring4.x 废除了 基本等效

    3. Set注入的简化写法

    3.1 基于属性简化

    JDK类型注入 suns 注意:value属性 只能简化 8种基本类型+String 注入标签 用户自定义类型

    3.2 基于p命名空间简化

    JDK类型注入 suns //p就是property的缩写 注意:value属性 只能简化 8种基本类型+String 注入标签 用户自定义类型

    第六章、构造注入

    注入:通过Spring的配置文件,为成员变量赋值 Set注入:Spring调用Set方法 通过配置文件 为成员变量赋值 构造注入:Spring调用构造方法 通过配置文件 为成员变量赋值

    1. 开发步骤

    1.1 提供有参构造方法

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

    1.2 Spring的配置文件


    //一对constructor-arg标签只对应一个参数,且参数的顺序与构造方法中参数的顺序一致
    suns


    102

    2. 构造方法重载

    2.1 参数个数不同时

    通过控制标签的数量进行区分

    2.1 构造参数个数相同时

    通过在标签引入 type属性 进行类型的区分


37

### 3. 注入的总结 未来的实战中,应用set注入还是构造注入?
答案:set注入更多
1. 构造注入麻烦 (重载)
2. Spring框架底层:大量应用了set注入 ## 第七章、反转控制与依赖注入 ### 1. 反转(转移)控制(IOC Inverse of Control) 控制:对于成员变量赋值的控制权 反转控制:把对于成员变量赋值的控制权,从代码中反转(转移)到Spring工厂和配置文件中完成 好处:解耦合 底层实现:工厂设计模式 ### 2. 依赖注入 (Dependency Injection DI) 注入:通过Spring的工厂及配置文件,为对象(bean,组件)的成员变量赋值 依赖注入:当一个类需要另一个类时,就意味着依赖,一旦出现依赖,就可以把另一个类作为本类的成员变量,最终通过Spring配置文件进行注入(赋值)。 好处:解耦合 ## 第八章、Spring工厂创建复杂对象 ### 1. 什么是复杂对象 复杂对象:指的就是不能直接通过new构造方法创建的对象 Connection SqlSessionFactory ### 2. Spring工厂创建复杂对象的三种方式 #### 2.1 FactoryBean接口 - 开发步骤 - 实现FactoryBean接口 - Spring配置文件的配置# 如果Class中指定的类型是FactoryBean接口的实现类,那么通过id值获得的是这个类所创建的复杂对象 Connection - 创建对象ApplicationContext ctx = new ClassPathXmlApplicationContext(“/applicationContext.xml”); Connection conn = (Connection) ctx.getBean(“conn”); - 细节 ConnectionFactoryBean conn = (ConnectionFactoryBean) ctx.getBean(“&conn”); - 如果就想获得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个字符串信息 ,进行配置文件的注入 好处:解耦合 - FactoryBean的实现原理[简易版]接口回调 1. 为什么Spring规定FactoryBean接口 实现 并且 getObject()? 2. ctx.getBean(“conn”) 获得是复杂对象Connection而没有获得 ConnectionFactoryBean(通过加&可以获得) Spring内部运行流程 1. 通过conn获得ConnectionFactoryBean类的对象 ,进而通过instanceof 判断出是FactoryBean接口的实现类 2. Spring按照规定调用getObject()方法,返回Connection对象 3. 返回Connection 接口+反射 = 什么都能做 - FactoryBean总结Spring中用于创建复杂对象的一种方式,也是Spring原生提供的,后续讲解Spring整合其他框架,大量应用FactoryBean #### 2.2 实例工厂 1. 避免Spring框架的侵入
2. 整合遗留系统 - 开发步骤一:创建实例工厂public class ConnectionFactory { //遗留系统创建的类 public Connection getConnection(){
Connection conn = null;
try {
Class.forName(“com.mysql.jdbc.Driver”);
conn = DriverManager.getConnection(“jdbc:mysql://localhost:3306/suns?useSSL=false”, “root”, “123456”);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
} - 开发步骤二:Spring配置文件的配置 //类不能直接调用非静态方法,所以需要先创建connFactory - 开发步骤三:测试实例工厂 ApplicationContext ctx = new ClassPathXmlApplicationContext(“/applicationContext.xml”);
Connection conn = (Connection) ctx.getBean(“conn”); #### 2.3 静态工厂 - 开发步骤一:创建静态工厂public class StaticConnectionFactory { public static Connection getConnection(){ //类可以直接调用静态方法
Connection conn = null;
try {
Class.forName(“com.mysql.jdbc.Driver”);
conn = DriverManager.getConnection(“jdbc:mysql://localhost:3306/suns?useSSL=false”, “root”, “123456”);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
} - 开发步骤二:Spring配置文件的配置 - 开发步骤三:测试实例工厂ApplicationContext ctx = new ClassPathXmlApplicationContext(“/applicationContext.xml”);
Connection conn = (Connection) ctx.getBean(“conn”); ### 3. Spring工厂创建对象的总结 ## 第九章、控制Spring工厂创建对象的次数 ### 1. 如何控制简单对象的创建次数 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 —- ## 第十章、对象的生命周期 ### 1. 什么是对象的生命周期 指的是一个对象创建、存活、消亡的一个完整过程 ### 2. 为什么要学习对象的生命周期 由Spring负责对象的创建、存活、销毁,了解生命周期,有利于我们使用好Spring为我们创建的对象 ### 3. 生命周期的三个阶段 - 创建阶段Spring工厂何时创建对象 - scope=”singleton”Spring工厂创建的同时,对象的创建 创建工厂:ApplicationContext ctx = new ClassPathXmlApplicationContext(“/applicationContext.xml”); 注意:若设置scope=singleton这种情况下也需要在获取对象的同时,创建对象 增加属性:即可 对象创建成功,会执行对应的构造方法,可以加以验证 public Product() { System.out.println(“Product.Product”); } - scope=”prototype”Spring工厂会在获取对象的同时,创建对象 ctx.getBean(“”)
- 初始化阶段(了解)Spring工厂在创建完对象后,调用对象的初始化方法,完成对应的初始化操作 1. 初始化方法提供:程序员根据需求,提供初始化方法,最终完成初始化操作 2. 初始化方法调用:Spring工厂进行调用 - 实现InitializingBean接口的方法afterProperitesSet()public class Product implements InitializingBean, DisposableBean { public Product() { System.out.println(“Product.Product”); } //这个就是初始化方法:做一些初始化操作 //Spring会进行调用(InitializingBean接口由spring提供) @Override public void afterPropertiesSet() throws Exception { System.out.println(“Product.afterPropertiesSet”); } } ApplicationContext ctx = new ClassPathXmlApplicationContext(“/applicationContext.xml”); //输出:Product.afterPropertiesSet - 对象中提供一个普通的方法public class Product{ public Product() { System.out.println(“Product.Product”); } public void myInit() { System.out.println(“Product.myInit”); } } spring配置文件: - 细节分析 1. 如果一个对象即实现InitializingBean,同时又提供的普通的初始化方法 两个初始化操作都会执行,执行顺序为: 1. InitializingBean 2. 普通初始化方法 1. 注入一定发生在初始化操作的前面,创建对象后,先进行属性注入,再进行初始化操作 1. 什么叫做初始化操作 资源的初始化:数据库 IO 网络 …..(耗费时间,消耗资源) - 销毁阶段Spring销毁对象前,会调用对象的销毁方法,完成销毁操作 1. Spring什么时候销毁所创建的对象? ctx.close(); 2. 销毁方法:程序员根据自己的需求,定义销毁方法,完成销毁操作 调用:Spring工厂完成调用 - 实现DisposableBean接口的destroy()方法public void destroy()throws Exception{ } - 定义一个普通的销毁方法public void myDestroy()throws Exception{ } - 细节分析 1. 销毁方法的操作只适用于 scope=”singleton” 1. 什么叫做销毁操作主要指的就是资源的释放操作 io.close() connection.close(); ## 第十一章、配置文件参数化 把Spring配置文件中需要经常修改的字符串信息,转移到一个更小的配置文件中 1. Spring的配置文件中存在需要经常修改的字符串? 存在 以数据库连接相关的参数 代表 2. 经常变化字符串,在Spring的配置文件中,直接修改 不利于项目维护(修改) 3. 转移到一个小的配置文件(.properties) 利于维护(修改) 配置文件参数化:利于Spring配置文件的维护(修改) ### 1. 配置文件参数的开发步骤 - 提供一个小的配置文件(.properities),与spring配置文件同级目录下名字:随便 放置位置:随便 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配置文件中通过${key}获取小配置文件中对应的值 ## 第十二章、自定义类型转换器 ### 1. 类型转换器 作用:Spring通过类型转换器把配置文件中字符串类型的数据,转换成了对象中成员变量对应类型的数据,进而完成了注入 ### 2. 自定义类型转换器 原因:当Spring内部没有提供特定类型转换器时,而程序员在应用的过程中还需要使用,那么就需要程序员自己定义类型转换器 - 自定义类实现Converter接口public class MyDateConverter implements Converter { //Converter<原始类型, 目标类型> / convert方法作用:将String类型转换为Date类型 SimpleDateFormat sdf = new SimpleDateFormat(); sdf.parset(String) —-> Date param:source代表的是配置文件中的日期字符串:2020-10-11 return : 当把转换好的Date作为convert方法的返回值后,Spring自动的为birthday属性进行注入(赋值) / @Override public Date convert(String source) { Date date = null; try { SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd”); date = sdf.parse(source); } catch (ParseException e) { e.printStackTrace(); } return date; } } - 在Spring的配置文件中进行配置1、Spring创建MyDateConverter类型对象 2、注册类型转换器 3、参数注入 - 测试自定义类型转换器 ApplicationContext ctx = new ClassPathXmlApplicationContext(“/applicationContext2.xml”); Person person = (Person) ctx.getBean(“person”); System.out.println(“birthday = “ + person.getBirthday()); ### 3. 细节分析 - MyDateConverter中的日期的格式,通过依赖注入的方式,由配置文件完成赋值。public class MyDateConverter implements Converter { private String pattern; public String getPattern() { return pattern; } public void setPattern(String pattern) { this.pattern = pattern; } @Override public Date convert(String source) { Date date = null; try { SimpleDateFormat sdf = new SimpleDateFormat(pattern); date = sdf.parse(source); } catch (ParseException e) { e.printStackTrace(); } return date; } } //配置文件中指定转换的格式 - ConversionSeviceFactoryBean定义的id属性值必须为conversionService - Spring框架内置日期类型的转换器日期格式:2020/05/01 (不支持 :2020-05-01) ## 第十三章、后置处理Bean BeanPostProcessor作用:对Spring工厂所创建的对象,进行再加工。 AOP底层实现: 注意:BeanPostProcessor接口 xxxx(){ } ### 1、后置处理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方法即可 注意: postProcessBeforeInitiallization return bean对象 ### 2、BeanPostProcessor的开发步骤 1. 类 实现 BeanPostProcessor接口public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { Categroy categroy = (Categroy) bean; categroy.setName(“xiaowb”); return categroy; } } 1. Spring的配置文件中进行配置 1. BeanPostProcessor细节BeanPostProcessor会对Spring工厂中所有创建的对象进行加工。 # 第二部分 — AOP编程 —- ## 第一章、静态代理设计模式 ### 1. 为什么需要代理设计模式 1.1 在JavaEE分层开发开发中,哪个层次对于我们来讲最重要
DAO —-> Service —> Controller JavaEE分层开发中,最为重要的是Service层(用户需求代码)
1.2 Service层中包含了哪些代码?
Service层中 = 核心功能(几十行 上百代码) + 额外功能(附加功能) 1. 核心功能 业务运算 DAO调用 2. 额外功能 1. 不属于业务 2. 可有可无 3. 代码量很小 事务、日志、性能…
1.3 额外功能书写在Service层中好不好?
Service层的调用者的角度(Controller):需要在Service层书写额外功能。 —-事务必须要 软件设计者:Service层不需要额外功能
1.4 现实生活中的解决方式 ### 2. 代理设计模式 #### 1.1 概念 通过代理类,为原始类(目标)增加额外的功能 好处:利于原始类(目标)的维护 #### 1.2名词解释 1. 目标类、原始类 指的是业务类(核心功能 —> 业务运算 DAO调用) —-“房东” 2. 目标方法、原始方法 目标类(原始类)中的方法,就是目标方法(原始方法) —-“出租房屋” 3. 额外功能 (附加功能) —-“广告+看房” 日志,事务,性能 #### 1.3 代理开发的核心要素 代理类 = 目标类(原始类) + 额外功能 + 原始类(目标类)实现相同的接口 public interface UserService{ —->房东 m1 m2 } UserServiceImpl implements UserService{ —->房东 m1 —-> 原始方法:业务运算 DAO调用 m2 } UserServiceProxy implements UserService{ —->中介(代理) m1 m2 m3 —-> 附加方法 } #### 1.4 编码 静态代理:为每一个原始类,手工编写一个代理类 (.java、.class)
UserService userService = new UserServiceProxy(); userService.login(“suns”, “123456”); userService.register(new User()); #### 1.5 静态代理存在的问题 1. 静态类文件数量过多,不利于项目管理 UserServiceImpl UserServiceProxy OrderServiceImpl OrderServiceProxy 2. 额外功能维护性差 代理类中的额外功能修改复杂(麻烦) ## 第二章、Spring的动态代理开发 ### 1. Spring动态代理的概念 概念:通过代理类为原始类(目标类)增加额外功能 好处:利于原始类(目标类)的维护 动态代理的开发步骤和底层实现与静态代理有区别 ### 2. 搭建开发环境 org.springframework spring-aop 5.1.14.RELEASE org.aspectj aspectjrt 1.8.8 org.aspectj aspectjweaver 1.8.3 ### 3. Spring动态代理的开发步骤 #### 3.1 创建原始对象(目标对象) public class UserServiceImpl implements UserService { @Override public void register(User user) { System.out.println(“UserServiceImpl.register 业务运算 + DAO “); } @Override public boolean login(String name, String password) { System.out.println(“UserServiceImpl.login”); return true; } }
通过spring配置文件创建原始类的对象: #### 3.2 额外功能 MethodBeforeAdvice接口:额外功能书写在接口的实现中,在原始方法执行之前运行额外功能。
public class Before implements MethodBeforeAdvice { / 作用:需要把运行在原始方法执行之前运行的额外功能,书写在before方法中 / @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println(“——-method before advice log———“); } }
#### 3.3 定义切入点 切入点:额外功能加入的位置 目的:由程序员根据自己的需要,决定额外功能加入给那个原始方法 register login 简单的测试:所有方法都做为切入点,都加入额外的功能。
—-所有类的方法都作为切入点,加入额外功能 #### 3.4 组装 (整合3.2、3.3) 表达的含义:所有的方法都加入before的额外功能 #### 3.5 调用 目的:获得Spring工厂创建的动态代理对象,并进行调用 ApplicationContext ctx = new ClassPathXmlApplicationContext(“/applicationContext.xml”); 注意: 1. Spring的工厂通过原始对象的id值获得的是代理对象 2. 获得代理对象后,可以通过声明接口类型,进行对象的存储 UserService userService=(UserService)ctx.getBean(“userService”); userService.login(“”) userService.register() ### 4. 动态代理细节分析 1. Spring创建的动态代理类在哪里?动态代理类:Spring框架在运行时,通过动态字节码技术,在JVM创建的,运行在JVM内部,等程序结束后,会和JVM一起消失 什么叫动态字节码技术:通过第三个动态字节码框架,在JVM中创建对应类的字节码,进而创建对象,当虚拟机结束,动态字节码跟着消失。 结论:动态代理不需要定义类文件,都是JVM运行过程中动态创建的,所以不会造成静态代理,类文件数量过多,影响项目管理的问题。 1. 动态代理编程简化代理的开发在额外功能不改变的前提下,创建其他目标类(原始类)的代理对象时,只需要指定原始(目标)对象即可。 —-目标类1 —-目标类2 —-额外功能1 —-额外功能2 1. 动态代理额外功能的维护性大大增强 ## 第三章、Spring动态代理详解 ### 1. 额外功能的详解 #### 1.1 MethodBeforeAdvice分析 1. MethodBeforeAdvice接口作用:额外功能运行在原始方法执行之前,进行额外功能操作。 public class Before1 implements MethodBeforeAdvice { / 作用:需要把运行在原始方法执行之前运行的额外功能,书写在before方法中 Method: 额外功能所增加给的那个原始方法 login方法 register方法 showOrder方法 Object[]: 额外功能所增加给的那个原始方法的参数。String name,String password new User Object: 额外功能所增加给的那个原始对象 UserServiceImpl OrderServiceImpl / @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println(“——-new method before advice log———“); } } 2. before方法的3个参数在实战中,该如何使用。 before方法的参数,在实战中,会根据需要进行使用,不一定都会用到,也有可能都不用。 Servlet{ service(HttpRequest request,HttpResponse response){ request.getParameter(“name”) —> response.getWriter() —-> } } #### 1.2 MethodInterceptor(方法拦截器) methodinterceptor接口:额外功能可以根据需要运行在原始方法执行前、后、前后。 - 额外功能运行在原始方法执行之前 public class Arround implements MethodInterceptor { / invoke方法的作用:额外功能书写在invoke 额外功能 原始方法之前 原始方法之后 原始方法执行之前 之后 确定:原始方法怎么运行 参数:MethodInvocation (Method):额外功能所增加给的那个原始方法 login register invocation.proceed() 代表原始方法的运行—-> login运行 register运行 返回值:Object: 原始方法的返回值 Date convert(String name) / @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println(“——-额外功能 log——“); Object ret = invocation.proceed(); —-原始方法—- return ret; } } - 额外功能运行在原始方法执行之后 @Override public Object invoke(MethodInvocation invocation) throws Throwable { Object ret = invocation.proceed(); System.out.println(“——-额外功能运行在原始方法执行之后——“); return ret; } - 额外功能运行在原始方法执行之前,之后 什么样的额外功能 运行在原始方法执行之前,之后都要添加? 事务 @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println(“——-额外功能运行在原始方法执行之前——“); Object ret = invocation.proceed(); System.out.println(“——-额外功能运行在原始方法执行之后——“); return ret; } - 额外功能运行在原始方法抛出异常的时候 @Override public Object invoke(MethodInvocation invocation) throws Throwable { Object ret = null; try { ret = invocation.proceed(); } catch (Throwable throwable) { System.out.println(“——-原始方法抛出异常 执行的额外功能 —— “); throwable.printStackTrace(); } return ret; } - MethodInterceptor影响原始方法的返回值 原始方法的返回值,直接作为invoke方法的返回值返回,MethodInterceptor不会影响原始方法的返回值 MethodInterceptor影响原始方法的返回值:Invoke方法的返回值,不要直接返回原始方法的运行结果即可。 @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println(“———log——-“); Object ret = invocation.proceed(); return false; } ### 2. 切入点详解 切入点决定额外功能加入位置(方法) exection( (..)) —-> 匹配了所有类的所有方法 a b c 1. execution() 切入点函数 2. (..) 切入点表达式 #### 2.1 切入点表达式 ##### 2.1.1 方法切入点表达式 (..) —> 所有方法 —-> 修饰符 返回值 —-> 方法名 ()—-> 参数表 ..—-> 对于参数没有要求 (参数有没有,参数有几个都行,参数是什么类型的都行) - 定义login方法作为切入点 login(..) # 定义register作为切入点 register(..) - 定义login方法且login方法有两个字符串类型的参数 作为切入点 login(String,String) #注意:非java.lang包中的类型,必须要写全限定名 register(com.baizhiedu.proxy.User) # ..可以和具体的参数类型连用 login(String,..) —> login(String),login(String,String),login(String,com.baizhiedu.proxy.User) - 精准方法切入点限定修饰符 返回值 包.类.方法(参数) com.baizhiedu.proxy.UserServiceImpl.login(..) com.baizhiedu.proxy.UserServiceImpl.login(String,String) ##### 2.1.2 类切入点 指定特定类作为切入点(额外功能加入的位置),自然这个类中的所有方法,都会加上对应的额外功能 - 语法1#类中的所有方法加入了额外功能 com.baizhiedu.proxy.UserServiceImpl.(..)
- 语法2 #忽略包 1. 类只存在一级包 com.UserServiceImpl
.UserServiceImpl.(..) 2. 类存在多级包 com.baizhiedu.proxy.UserServiceImpl ..UserServiceImpl.(..) ##### 2.1.3 包切入点表达式(实战) 指定包作为额外功能加入的位置,自然包中的所有类及其方法都会加入额外的功能 - 语法1#切入点包中的所有类,必须在proxy中,不能在proxy包的子包中 com.baizhiedu.proxy..(..) - 语法2#切入点当前包及其子包都生效 com.baizhiedu.proxy...(..) #### 2.2 切入点函数 切入点函数:用于执行切入点表达式 ##### 2.2.1 execution 最为重要的切入点函数,功能最全。 可以执行:方法切入点表达式、类切入点表达式、包切入点表达式 弊端:execution执行切入点表达式 ,书写麻烦 execution( com.baizhiedu.proxy...(..)) 注意:其他的切入点函数 简化是execution书写复杂度,功能上完全一致 ##### 2.2.2 args 作用:主要用于函数(方法)参数的匹配 切入点:方法参数必须得是2个字符串类型的参数 execution( (String,String)) args(String,String) —-不关心包、类、方法 ##### 2.2.3 within 作用:主要用于进行类、包切入点表达式的匹配 切入点:UserServiceImpl这个类 execution( ..UserServiceImpl.(..)) within(..UserServiceImpl) execution( com.baizhiedu.proxy...(..)) within(com.baizhiedu.proxy..) ##### 2.2.4 @annotation 作用:为具有特殊注解的方法加入额外功能 ##### 2.2.5 切入点函数的逻辑运算 指的是整合多个切入点函数一起配合工作,进而完成更为复杂的需求 - and与操作案例:方法为login、同时参数2个字符串 1. execution( login(String,String)) 2. execution( login(..)) and args(String,String) 注意:与操作不能用于同种类型的切入点函数 案例:register方法 和 login方法作为切入点(方法不可能既叫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 TestJDKProxy { / 1. 借用类加载器 TestJDKProxy UserServiceImpl 2. JDK8.x前 final UserService userService = new UserServiceImpl(); / public static void main(String[] args) { //1 创建原始对象 UserService userService = new UserServiceImpl(); //2 JDK创建动态代理 / / InvocationHandler handler = new InvocationHandler(){ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(“———proxy log ————“); //原始方法运行 Object ret = method.invoke(userService, args); return ret; } }; UserService userServiceProxy = (UserService)Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(),userService.getClass().getInterfaces(),handler); userServiceProxy.login(“suns”, “123456”); userServiceProxy.register(new User()); } } #### 2.2 CGlib的动态代理 CGlib创建动态代理的原理:父子继承关系创建代理对象,原始类作为父类,代理类作为子类,这样既可以保证2者方法一致,同时在代理类中提供新的实现(额外功能+原始方法) - CGlib编码 package com.baizhiedu.cglib; import com.baizhiedu.proxy.User; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class TestCglib { public static void main(String[] args) { //1 创建原始对象 UserService userService = new UserService(); / 2 通过cglib方式创建动态代理对象 Proxy.newProxyInstance(classloader,interface,invocationhandler) Enhancer.setClassLoader() Enhancer.setSuperClass() Enhancer.setCallback(); —-> MethodInterceptor(cglib) Enhancer.create() —-> 代理 / Enhancer enhancer = new Enhancer(); enhancer.setClassLoader(TestCglib.class.getClassLoader()); enhancer.setSuperclass(userService.getClass()); MethodInterceptor interceptor = new MethodInterceptor() { //等同于 InvocationHandler —- invoke @Override public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println(“—-cglib log——“); Object ret = method.invoke(userService, args); return ret; } }; enhancer.setCallback(interceptor); UserService userServiceProxy = (UserService) enhancer.create(); userServiceProxy.login(“suns”, “123345”); userServiceProxy.register(new User()); } } - 总结1. JDK动态代理 Proxy.newProxyInstance() 通过接口创建代理的实现类 2. Cglib动态代理 Enhancer 通过继承父类创建的代理类 ### 3. Spring工厂如何加工原始对象 - 思路分析 - 编码public class ProxyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
} @Override
/
Proxy.newProxyInstance();
/
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(“——- new Log——-“);
Object ret = method.invoke(bean, args); return ret;
}
};
return Proxy.newProxyInstance(ProxyBeanPostProcessor.class.getClassLoader(),bean.getClass().getInterfaces(),handler);
}
} ## 第六章、基于注解的AOP编程 ### 1. 基于注解的AOP编程的开发步骤 1. 原始对象 1. 额外功能 1. 切入点 1. 组装切面# 通过切面类定义了额外功能 @Around
定义了切入点 @Around(“execution( login(..))”)
@Aspect 切面类

package com.baizhiedu.aspect; import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
/

1. 额外功能
public class MyArround implements MethodInterceptor{
public Object invoke(MethodInvocation invocation){
额外功能1
Object ret = invocation.proceed();
额外功能2
return ret;
}
}
2. 切入点

/
@Aspect
public class MyAspect { //定义切面类 @Around(“execution(
login(..))”) //切入点
public Object arround(ProceedingJoinPoint joinPoint) throws Throwable { //joinPoint等同于invocation System.out.println(“——aspect log ———“);
Object ret = joinPoint.proceed();
return ret;
}
} //创建切面对象 ### 2. 细节分析 #### 2.1 切入点复用 切入点复用:在切面类中定义一个函数:上面@Pointcut注解,通过这种方式,定义切入点表达式,后续更加有利于切入点复用。 @Aspect
public class MyAspect {

@Pointcut(“execution( login(..))”)
public void myPointcut(){} @Around(value=”myPointcut()”)
public Object arround(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println(“——aspect log ———“); Object ret = joinPoint.proceed();
return ret;
} @Around(value=”myPointcut()”)
public Object arround1(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println(“——aspect tx ———“); Object ret = joinPoint.proceed();
return ret;
}
} #### 2.2 动态代理的创建方式 AOP底层实现 2种代理创建方式
1. JDK 通过实现接口 做新的实现类方式 创建代理对象
2. Cglib 通过继承父类 做新的子类 创建代理对象

默认情况 AOP编程 底层应用JDK动态代理创建方式
如果切换Cglib
1. 基于注解AOP开发

2. 传统的AOP开发

## 第七章、AOP开发中的一个坑 坑:在同一个业务类中,进行业务方法间的相互调用,只有最外层的方法,才是加入了额外功能(内部的方法,通过普通的方式调用,都调用的是原始方法)。如果想让内层的方法也调用代理对象的方法,就要AppicationContextAware获得工厂,进而获得代理对象。
public class UserServiceImpl implements UserService, ApplicationContextAware {
private ApplicationContext ctx; @Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ctx = applicationContext;
} @Log
@Override
public void register(User user) {
System.out.println(“UserServiceImpl.register 业务运算 + DAO “);
//throw new RuntimeException(“测试异常”); //调用的是原始对象的login方法 —-> 核心功能
/

设计目的:代理对象的login方法 —-> 额外功能+核心功能
ApplicationContext ctx = new ClassPathXmlApplicationContext(“/applicationContext2.xml”);
UserService userService = (UserService) ctx.getBean(“userService”);
userService.login(); Spring工厂重量级资源 一个应用中 应该只创建一个工厂
/ UserService userService = (UserService) ctx.getBean(“userService”);
userService.login(“suns”, “123456”);
} @Override
public boolean login(String name, String password) {
System.out.println(“UserServiceImpl.login”);
return true;
}
} ## 第八章、AOP阶段知识总结 # 第三部分 — 持久层整合 —- ## 第一章、持久层整合 ### 1.Spring框架为什么要与持久层技术进行整合 1. JavaEE开发需要持久层进行数据库的访问操作。
2. JDBC Hibernate MyBatis进行持久开发过程存在大量的代码冗余
3. Spring基于模板设计模式对于上述的持久层技术进行了封装 ### 2. Spring可以与那些持久层技术进行整合? 1. JDBC
|- JDBCTemplate
2. Hibernate (JPA)
|- HibernateTemplate
3. MyBatis
|- SqlSessionFactoryBean MapperScannerConfigure ## 第二章、Spring与MyBatis整合 ### 1. MyBatis开发步骤的回顾 1. 实体
2. 实体别名
3. 表
4. 创建DAO接口
5. 实现Mapper文件
6. 注册Mapper文件
7. MybatisAPI调用 ### 2. Mybatis在开发过程中存在问题 配置繁琐 代码冗余 1. 实体
2. 实体别名 配置繁琐
3. 表
4. 创建DAO接口
5. 实现Mapper文件
6. 注册Mapper文件 配置繁琐
7. MybatisAPI调用 代码冗余 ### 3. Spring与Mybatis整合思路分析 ### 4. Spring与Mybatis整合的开发步骤 - 配置文件(ApplicationContext.xml) 进行相关配置#配置 是需要配置一次 指定 实体类所在的包 com.baizhiedu.entity User Product 指定 配置文件(映射文件)的路径 还有通用配置 com.baizhiedu.mapper/
Mapper.xml
session.getMapper() —- xxxDAO实现类对象 XXXDAO —-> xXXDAO —> 指定 DAO接口放置的包 com.baizhiedu.dao - 编码# 实战经常根据需求 写的代码 1. 实体 2. 表 3. 创建DAO接口 4. 实现Mapper文件 ### 5. Spring与Mybatis整合编码 - 搭建开发环境(jar) org.springframework spring-jdbc 5.1.14.RELEASE org.mybatis mybatis-spring 2.0.2 com.alibaba druid 1.1.18 mysql mysql-connector-java 5.1.48 org.mybatis mybatis 3.4.6 - Spring配置文件的配置











classpath:com.baizhiedu.mapper/*Mapper.xml





- 编码1. 实体
2. 表
3. DAO接口
4. Mapper文件配置 ### 6. Spring与Mybatis整合细节 - 问题:Spring与Mybatis整合后,为什么DAO不提交事务,但是数据能够插入数据库中?Connection —> tx
Mybatis(Connection) 本质上控制连接对象(Connection) —-> 连接池(DataSource)
1. Mybatis提供的连接池对象 —-> 创建Connection
Connection.setAutoCommit(false) 手工的控制了事务 , 操作完成后,手工提交
2. Druid(C3P0 DBCP)作为连接池 —-> 创建Connection
Connection.setAutoCommit(true) true默认值 保持自动控制事务,一条sql 自动提交
答案:因为Spring与Mybatis整合时,引入了外部连接池对象,保持自动的事务提交这个机制(Connection.setAutoCommit(true)),不需要手工进行事务的操作,也能进行事务的提交 注意:未来实战中,还会手工控制事务(多条sql一起成功,一起失败),后续Spring通过事务控制解决这个问题。 ## 第三章、Spring的事务处理 ### 1. 什么是事务? 保证业务操作完整性的一种数据库机制 事务的4特点: A C I D 1. A 原子性 2. C 一致性 3. I 隔离性 4. D 持久性 ### 2. 如何控制事务 JDBC: Connection.setAutoCommit(false); Connection.commit(); Connection.rollback(); Mybatis: Mybatis自动开启事务 sqlSession(Connection).commit(); sqlSession(Connection).rollback(); 结论:控制事务的底层 都是Connection对象完成的。 ### 3.Spring控制事务的开发 Spring是通过AOP的方式进行事务开发 #### 1. 原始对象 public class XXXUserServiceImpl{ private xxxDAO xxxDAO set get 1. 原始对象 —-》 原始方法 —-》核心功能 (业务处理+DAO调用) 2. DAO作为Service的成员变量,依赖注入的方式进行赋值 } #### 2. 额外功能 1. org.springframework.jdbc.datasource.DataSourceTransactionManager 2. 注入DataSource 1. MethodInterceptor public Object invoke(MethodInvocation invocation){ try{ Connection.setAutoCommit(false); Object ret = invocation.proceed(); Connection.commit(); }catch(Exception e){ Connection.rollback(); } return ret; } 2. @Aspect @Around #### 3. 切入点 @Transactional 事务的额外功能加入给那些业务方法。 1. 类上:类中所有的方法都会加入事务 2. 方法上:这个方法会加入事务 #### 4 组装切面 1. 切入点 2. 额外功能 ### 4. Spring控制事务的编码 - 搭建开发环境 (jar) org.springframework spring-tx 5.1.14.RELEASE - 编码 @Transactional public class UserServiceImpl implements UserService { private UserDAO userDAO; - 细节 进行动态代理底层实现的切换 proxy-target-class 默认 false JDK true Cglib ## 第四章、 Spring中的事务属性(Transaction Attribute) ### 1. 什么是事务属性 属性:描述物体特征的一系列值 性别 身高 体重 … 事务属性:描述事务特征的一系列值 1. 隔离属性 2. 传播属性 3. 只读属性 4. 超时属性 5. 异常属性 ### 2. 如何添加事务属性 @Transactional(isloation=,propagation=,readOnly=,timeout=,rollbackFor=,noRollbackFor=,) ### 3. 事务属性详解 #### 1. 隔离属性 (ISOLATION) - 隔离属性的概念概念:他描述了事务解决并发问题的特征 1. 什么是并发 多个事务(用户)在同一时间,访问操作了相同的数据 同一时间:0.000几秒 微小前 微小后 2. 并发会产生那些问题 1. 脏读 2. 不可重复读 3. 幻影读 3. 并发问题如何解决 通过隔离属性解决,隔离属性中设置不同的值,解决并发处理过程中的问题。 - 事务并发产生的问题 - 脏读一个事务,读取了另一个事务中没有提交的数据。会在本事务中产生数据不一致的问题 解决方案 @Transactional(isolation=Isolation.READ_COMMITTED) - 不可重复读一个事务中,多次读取相同的数据,但是读取结果不一样。会在本事务中产生数据不一致的问题 注意:1 不是脏读 2 一个事务中 解决方案 @Transactional(isolation=Isolation.REPEATABLE_READ) 本质: 一把行锁 - 幻影读一个事务中,多次对整表进行查询统计,但是结果不一样,会在本事务中产生数据不一致的问题 解决方案 @Transactional(isolation=Isolation.SERIALIZABLE) 本质:表锁 - 总结并发安全: SERIALIZABLE>REPEATABLE_READ>READ_COMMITTED 运行效率: READ_COMMITTED>REPEATABLE_READ>SERIALIZABLE - 数据库对于隔离属性的支持Oracle不支持REPEATABLE_READ值 如何解决不可重复读 采用的是多版本比对的方式 解决不可重复读的问题 | 隔离属性的值 | MySQL | Oracle | | —- | —- | —- | | ISOLATION_READ_COMMITTED | ✅ | ✅ | | IOSLATION_REPEATABLE_READ | ✅ | ❎ | | ISOLATION_SERIALIZABLE | ✅ | ✅ | - 默认隔离属性ISOLATION_DEFAULT:会调用不同数据库所设置的默认隔离属性 MySQL : REPEATABLE_READ Oracle: READ_COMMITTED
- 查看数据库默认隔离属性 - MySQLselect @@tx_isolation; - OracleSELECT s.sid, s.serial#, CASE BITAND(t.flag, POWER(2, 28)) WHEN 0 THEN ‘READ COMMITTED’ ELSE ‘SERIALIZABLE’ END AS isolation_level FROM v$transaction t JOIN v$session s ON t.addr = s.taddr AND s.sid = sys_context(‘USERENV’, ‘SID’); - 隔离属性在实战中的建议推荐使用Spring指定的ISOLATION_DEFAULT 1. MySQL repeatable_read 2. Oracle read_commited 未来实战中,并发访问情况 很低 如果真遇到并发问题,乐观锁 Hibernate(JPA) Version MyBatis 通过拦截器自定义开发
#### 2. 传播属性(PROPAGATION) - 传播属性的概念概念:他描述了事务解决嵌套问题的特征 什么叫做事务的嵌套:他指的是一个大的事务中,包含了若干个小的事务 问题:大事务中融入了很多小的事务,他们彼此影响,最终就会导致外部大的事务,丧失了事务的原子性 - 传播属性的值及其用法 | 传播属性的值 | 外部不存在事务 | 外部存在事务 | 用法 | 备注 | | —- | —- | —- | —- | —- | | REQUIRED | 开启新的事务 | 融合到外部事务中 | @Transactional(propagation = Propagation.REQUIRED) | 增删改方法 | | SUPPORTS | 不开启事务 | 融合到外部事务中 | @Transactional(propagation = Propagation.SUPPORTS) | 查询方法 | | REQUIRES_NEW | 开启新的事务 | 挂起外部事务,创建新的事务 | @Transactional(propagation = Propagation.REQUIRES_NEW) | 日志记录方法中 | | NOT_SUPPORTED | 不开启事务 | 挂起外部事务 | @Transactional(propagation = Propagation.NOT_SUPPORTED) | 及其不常用 | | NEVER | 不开启事务 | 抛出异常 | @Transactional(propagation = Propagation.NEVER) | 及其不常用 | | MANDATORY | 抛出异常 | 融合到外部事务中 | @Transactional(propagation = Propagation.MANDATORY) | 及其不常用 | - 默认的传播属性REQUIRED是传播属性的默认值 - 推荐传播属性的使用方式增删改 方法:直接使用默认值REQUIRED 查询 操作:显示指定传播属性的值为SUPPORTS
#### 3. 只读属性(readOnly) 针对于只进行查询操作的业务方法,可以加入只读属性,提供运行效率 默认值:false #### 4. 超时属性(timeout) 指定了事务等待的最长时间 1. 为什么事务进行等待? 当前事务访问数据时,有可能访问的数据被别的事务进行加锁的处理,那么此时本事务就必须进行等待。 2. 等待时间 秒 3. 如何应用 @Transactional(timeout=2) 4. 超时属性的默认值 -1 最终由对应的数据库来指定 #### 5. 异常属性 Spring事务处理过程中 默认 对于RuntimeException及其子类 采用的是回滚的策略 默认 对于Exception及其子类 采用的是提交的策略 rollbackFor = {java.lang.Exception,xxx,xxx} noRollbackFor = {java.lang.RuntimeException,xxx,xx} @Transactional(rollbackFor = {java.lang.Exception.class},noRollbackFor = {java.lang.RuntimeException.class}) 建议:实战中使用RuntimeExceptin及其子类 使用事务异常属性的默认值 ### 4. 事务属性常见配置总结 1. 隔离属性 默认值 2. 传播属性 Required(默认值) 增删改 Supports 查询操作 3. 只读属性 readOnly false 增删改 true 查询操作 4. 超时属性 默认值 -1 5. 异常属性 默认值 增删改操作 @Transactional 查询操作 @Transactional(propagation=Propagation.SUPPORTS,readOnly=true) ### 5. 基于标签的事务配置方式(事务开发的第二种形式) 基于注解 @Transaction的事务配置回顾





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

基于标签的事务配置





事务属性




等效于
@Transactional(isolation=,propagation=,)
public void register(){

}





  • 基于标签的事务配置在实战中的应用方式




编程时候 service中负责进行增删改操作的方法 都以modify开头
查询操作 命名无所谓






应用的过程中,service放置到service包中



第四部分 — MVC框架整合


第一章、MVC框架整合思想

1. 搭建Web运行环境

javax.servlet javax.servlet-api 3.1.0 provided javax.servlet jstl 1.2 javax.servlet.jsp javax.servlet.jsp-api 2.3.1 provided org.springframework spring-web 5.1.14.RELEASE org.springframework spring-core 5.1.14.RELEASE org.springframework spring-beans 5.1.14.RELEASE org.springframework spring-tx 5.1.14.RELEASE org.springframework spring-jdbc 5.1.14.RELEASE org.mybatis mybatis-spring 2.0.2 com.alibaba druid 1.1.18 mysql mysql-connector-java 5.1.48 org.mybatis mybatis 3.4.6 junit junit 4.11 test org.springframework spring-context 5.1.4.RELEASE org.springframework spring-aop 5.1.14.RELEASE org.aspectj aspectjrt 1.8.8 org.aspectj aspectjweaver 1.8.3 org.slf4j slf4j-log4j12 1.7.25 log4j log4j 1.2.17 ### 2. 为什么要整合MVC框架 1. MVC框架提供了控制器(Controller)调用Service DAO —-》 Service 2. 请求响应的处理 3. 接受请求参数 request.getParameter(“”) 4. 控制程序的运行流程 5. 视图解析 (JSP JSON Freemarker Thyemeleaf ) ### 3. Spring可以整合那些MVC框架 1. struts1 2. webwork 3. jsf 4. struts2 5. springMVC ### 4.Spring整合MVC框架的核心思路 #### 1. 准备工厂 1. Web开发过程中如何创建工厂
ApplicationContext ctx = new ClassPathXmlApplicationContext(“/applicationContext.xml”);
WebXmlApplicationContext()
2. 如何保证工厂唯一同时被共用
被共用:Web request|session|ServletContext(application)
工厂存储在ServletContext这个作用域中 ServletContext.setAttribute(“xxxx”,ctx);

唯一:ServletContext对象 创建的同时 —-》 ApplicationContext ctx = new ClassPathXmlApplicationContext(“/applicationContext.xml”);

ServletContextListener —-> ApplicationContext ctx = new ClassPathXmlApplicationContext(“/applicationContext.xml”);
ServletContextListener 在ServletContext对象创建的同时,被调用(只会被调用一次) ,把工厂创建的代码,写在ServletContextListener中,也会保证只调用
一次,最终工厂就保证了唯一性
3. 总结
ServletContextListener(唯一)
ApplicationContext ctx = new ClassPathXmlApplicationContext(“/applicationContext.xml”);
ServletContext.setAttribute(“xxx”,ctx) (共用)

4. Spring封装了一个ContextLoaderListener
1. 创建工厂
2. 把工厂存在ServletContext中
ContextLoaderListener使用方式 web.xml
org.springframework.web.context.ContextLoaderListener

contextConfigLocation
classpath:applicationContext.xml
#### 2. 代码整合 依赖注入:把Sevice对象注入个控制器对象。 ## 第二章、Spring与Struts2框架整合 (选学) ### 1. Spring与Struts2整合思路分析 1. Struts2中的Action需要通过Spring的依赖注入获得Service对象。 ### 2. Spring与Struts2整合的编码实现 - 搭建开发环境 - 引入相关jar (Spring Struts2) org.apache.struts struts2-spring-plugin 2.3.8 - 引入对应的配置文件 - applicationContext.xml - struts.xml - log4j.properties - 初始化配置 org.springframework.web.context.ContextLoaderListener contextConfigLocation classpath:applicationContext.xml struts2 org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter struts2 /* - Spring (ContextLoaderListener —> Web.xml) - Struts2(Filter —> Web.xml) - 编码 - 开发Service对象最终在Spring配置文件中创建Service对象 - 开发Action对象 - 开发类public class RegAction implements Action{ private UserService userService; set get public String execute(){ userService.register(); return Action.SUCCESS; } } - Spring (applicationContext.xml) url reg.action —-> 会接受到用户的请求后,创建RegAction这个类的对象 进行相应的处理 /index.jsp ### 3. Spring+Struts2+Mybatis整合(SSM) #### 1. 思路分析 SSM = Spring+Struts2 Spring+Mybatis #### 2. 整合编码 - 搭建开发环境 - 引入相关jar (Spring Struts2 Mybatis)
org.apache.struts
struts2-spring-plugin
2.3.8
  1. <dependency><br /> <groupId>javax.servlet</groupId><br /> <artifactId>javax.servlet-api</artifactId><br /> <version>3.1.0</version><br /> <scope>provided</scope><br /> </dependency>
  2. <dependency><br /> <groupId>javax.servlet</groupId><br /> <artifactId>jstl</artifactId><br /> <version>1.2</version><br /> </dependency>
  3. <!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api --><br /> <dependency><br /> <groupId>javax.servlet.jsp</groupId><br /> <artifactId>javax.servlet.jsp-api</artifactId><br /> <version>2.3.1</version><br /> <scope>provided</scope><br /> </dependency>
  4. <dependency><br /> <groupId>org.springframework</groupId><br /> <artifactId>spring-web</artifactId><br /> <version>5.1.14.RELEASE</version><br /> </dependency>
  5. <dependency><br /> <groupId>org.springframework</groupId><br /> <artifactId>spring-core</artifactId><br /> <version>5.1.14.RELEASE</version><br /> </dependency>
  6. <dependency><br /> <groupId>org.springframework</groupId><br /> <artifactId>spring-beans</artifactId><br /> <version>5.1.14.RELEASE</version><br /> </dependency>
  7. <dependency><br /> <groupId>org.springframework</groupId><br /> <artifactId>spring-tx</artifactId><br /> <version>5.1.14.RELEASE</version><br /> </dependency>
  8. <dependency><br /> <groupId>org.springframework</groupId><br /> <artifactId>spring-jdbc</artifactId><br /> <version>5.1.14.RELEASE</version><br /> </dependency>
  9. <dependency><br /> <groupId>org.mybatis</groupId><br /> <artifactId>mybatis-spring</artifactId><br /> <version>2.0.2</version><br /> </dependency>
  10. <dependency><br /> <groupId>com.alibaba</groupId><br /> <artifactId>druid</artifactId><br /> <version>1.1.18</version><br /> </dependency>
  11. <dependency><br /> <groupId>mysql</groupId><br /> <artifactId>mysql-connector-java</artifactId><br /> <version>5.1.48</version><br /> </dependency>
  12. <dependency><br /> <groupId>org.mybatis</groupId><br /> <artifactId>mybatis</artifactId><br /> <version>3.4.6</version><br /> </dependency>
  13. <dependency><br /> <groupId>junit</groupId><br /> <artifactId>junit</artifactId><br /> <version>4.11</version><br /> <scope>test</scope><br /> </dependency>
  14. <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --><br /> <dependency><br /> <groupId>org.springframework</groupId><br /> <artifactId>spring-context</artifactId><br /> <version>5.1.4.RELEASE</version><br /> </dependency>
  15. <dependency><br /> <groupId>org.springframework</groupId><br /> <artifactId>spring-aop</artifactId><br /> <version>5.1.14.RELEASE</version><br /> </dependency>
  16. <dependency><br /> <groupId>org.aspectj</groupId><br /> <artifactId>aspectjrt</artifactId><br /> <version>1.8.8</version><br /> </dependency>
  17. <dependency><br /> <groupId>org.aspectj</groupId><br /> <artifactId>aspectjweaver</artifactId><br /> <version>1.8.3</version><br /> </dependency>
  18. <dependency><br /> <groupId>org.slf4j</groupId><br /> <artifactId>slf4j-log4j12</artifactId><br /> <version>1.7.25</version><br /> </dependency>
  19. <dependency><br /> <groupId>log4j</groupId><br /> <artifactId>log4j</artifactId><br /> <version>1.2.17</version><br /> </dependency>
  • 引入对应的配置文件
    • applicationContext.xml
    • struts.xml
    • log4j.properties
    • xxxxMapper.xml
  • 初始化配置 org.springframework.web.context.ContextLoaderListener contextConfigLocation classpath:applicationContext.xml struts2 org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter struts2 /*
    • Spring (ContextLoaderListener —> Web.xml)
    • Struts2(Filter —> Web.xml)
      • 编码
  • DAO (Spring+Mybatis)1. 配置文件的配置 1. DataSource 2. SqlSessionFactory ——> SqlSessionFactoryBean 1. dataSource 2. typeAliasesPackage 3. mapperLocations 3. MapperScannerConfigur —-> DAO接口实现类 2. 编码 1. entity 2. table 3. DAO接口 4. 实现Mapper文件
  • Service (Spring添加事务)1. 原始对象 —-》 注入DAO 2. 额外功能 —-》 DataSourceTransactionManager —-> dataSource 3. 切入点 + 事务属性 @Transactional(propagation,readOnly…) 4. 组装切面 <tx:annotation-driven
  • Controller (Spring+Struts2)1. 开发控制器 implements Action 注入Service 2. Spring的配置文件 1. 注入 Service 2. scope = prototype 3. struts.xml

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

    Spring会根据需要,把配置信息分门别类的放置在多个配置文件中,便于后续的管理及维护。 DAO ——— applicationContext-dao.xml Service —- applicationContext-service.xml Action —- applicationContext-action.xml 注意:虽然提供了多个配置文件,但是后续应用的过程中,还要进行整合
  • 通配符方式1. 非web环境 ApplicationContext ctx = new ClassPathXmlApplicationContext(“/applicationContext-.xml”); 2. web环境 contextConfigLocation classpath:applicationContext-.xml
  • 1. 非web环境 ApplicationContext ctx = new ClassPathXmlApplicationContext(“/applicationContext.xml”); 2. web环境 contextConfigLocation classpath:applicationContext.xml

    第五部分 —- 注解编程


第一章、注解基础概念

1. 什么是注解编程

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

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

  1. 注解开发方便 代码简洁 开发速度大大提高 2. Spring开发潮流 Spring2.x引入注解 Spring3.x完善注解 SpringBoot普及 推广注解编程

    3. 注解的作用

  • 替换XML这种配置形式,简化配置
  • 替换接口,实现调用双方的契约性 通过注解的方式,在功能调用者和功能提供者之间达成约定,进而进行功能的调用。因为注解应用更为方便灵活,所以在现在的开发中,更推荐通过注解的形式,完成

    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. 对象创建相关注解

  • 搭建开发环境 作用:让Spring框架在设置包及其子包中扫描对应的注解,使其生效。
  • 对象创建相关注解
    • @Component作用:替换原有spring配置文件中的<bean标签 注意: id属性 component注解 提供了默认的设置方式 首单词首字母小写 class属性 通过反射获得class内容
    • @Component 细节
      • 如何显示指定工厂创建对象的id值@Component(“u”)
      • Spring配置文件覆盖注解配置内容applicationContext.xml 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
  • @Lazy注解作用:延迟创建单实例对象 注意:一旦使用了@Lazy注解后,Spring会在使用这个对象时候,进行这个对象的创建
  • 生命周期方法相关注解1. 初始化相关方法 @PostConstruct InitializingBean 2. 销毁方法 @PreDestroy DisposableBean 注意:1. 上述的2个注解并不是Spring提供的,JSR(JavaEE规范)520 2. 再一次的验证,通过注解实现了接口的契约性

    2. 注入相关注解

  • 用户自定义类型 @Autowired@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 javax.inject javax.inject 1

  • JDK类型 @Value注解完成 1. 设置xxx.properties id = 10 name = suns 2. Spring的工厂读取这个配置文件 3. 代码 属性 @Value(“${key}”)

    • @PropertySource1. 作用:用于替换Spring配置文件中的标签 2. 开发步骤 1. 设置xxx.properties id = 10 name = suns 2. 应用@PropertySource 3. 代码 属性 @Value()
    • @Value注解使用细节
      • @Value注解不能应用在静态成员变量上如果应用,赋值(注入)失败
      • @Value注解+Properties这种方式,不能注入集合类型Spring提供新的配置形式 YAML YML (SpringBoot)

        3. 注解扫描详解

        当前包 及其 子包

        1. 排除方式

        type:assignable:排除特定的类型 不进行扫描 annotation:排除特定的注解 不进行扫描 aspectj:切入点表达式 包切入点: com.baizhiedu.bean.. 类切入点: ..User regex:正则表达式 custom:自定义排除策略框架底层开发 排除策略可以叠加使用

        2. 包含方式

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

        4. 对于注解开发的思考

  • 配置互通Spring注解配置 配置文件的配置 互通 @Repository public class UserDAOImpl{ } public class UserServiceImpl{ private UserDAO userDAO; set get }

  • 什么情况下使用注解 什么情况下使用配置文件@Component 替换

    5. SSM整合开发(半注解开发)

  • 搭建开发环境

    • 引入相关jar 【SSM POM】
    • 引入相关配置文件
      • applicationContext.xml
      • struts.xml
      • log4.properties
      • XXXMapper.xml
    • 初始化配置
      • Web.xml Spring (ContextLoaderListener)
      • Web.xml Struts Filter
  • 编码
    • DAO (Spring+Mybatis)1. 配置文件的配置
      1. DataSource
      2. SqlSessionFactory ——> SqlSessionFactoryBean
      1. dataSource
      2. typeAliasesPackage
      3. mapperLocations
      3. MapperScannerConfigur —-> DAO接口实现类
      2. 编码
      1. entity
      2. table
      3. DAO接口
      4. 实现Mapper文件
    • Service1. 原始对象 —-》 注入DAO
      @Service —-> @Autowired
  1. 额外功能 —-》 DataSourceTransactionManager —-> dataSource
    3. 切入点 + 事务属性
    @Transactional(propagation,readOnly…)
    4. 组装切面
    <tx:annotation-driven

    • Controller (Spring+Struts2)1. @Controller
      @Scope(“prototype”)
      public class RegAction implements Action{
      @Autowired
      private UserService userServiceImpl;

      }
      2. struts.xml

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

      1. 配置Bean

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

      @Configuration
      public class AppConfig{

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

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

    • 基于注解开发使用日志不能集成Log4j 集成logback

      • 引入相关jar
        org.slf4j
        slf4j-api
        1.7.25

      org.slf4j
      jcl-over-slf4j
      1.7.25

      ch.qos.logback
      logback-classic
      1.2.3

      ch.qos.logback
      logback-core
      1.2.3


      org.logback-extensions
      logback-ext-spring
      0.1.4

      • 引入logback配置文件 (logback.xml)<?xml version=”1.0” encoding=”UTF-8”?>





        %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n



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

可以应用

2. @Bean注解

@Bean注解在配置bean中进行使用,等同于XML配置文件中的

1. @Bean注解的基本使用

  • 对象的创建1. 简单对象
    直接能够通过new方式创建的对象
    User UserService UserDAO
    2. 复杂对象
    不能通过new的方式直接创建的对象
    Connection SqlSessionFactory
    • @Bean注解创建复杂对象的注意事项遗留系统整合 @Bean public Connection conn1() { Connection conn = null; try { ConnectionFactoryBean factoryBean = new ConnectionFactoryBean(); conn = factoryBean.getObject(); } catch (Exception e) { e.printStackTrace(); } return conn; }
  • 自定义id值@Bean(“id”)
  • 控制对象创建次数@Bean @Scope(“singleton|prototype”) 默认值 singleton

    2. @Bean注解的注入

  • 用户自定义类型@Bean public UserDAO userDAO() { return new UserDAOImpl(); } @Bean public UserService userService(UserDAO userDAO) { UserServiceImpl userService = new UserServiceImpl(); userService.setUserDAO(userDAO); return userService; } //简化写法 @Bean public UserService userService() { UserServiceImpl userService = new UserServiceImpl(); userService.setUserDAO(userDAO()); return userService; }

  • JDK类型的注入@Bean
    public Customer customer() {
    Customer customer = new Customer();
    customer.setId(1);
    customer.setName(“xiaohei”);

    return customer;
    }

    • JDK类型注入的细节分析如果直接在代码中进行set方法的调用,会存在耦合的问题

@Configuration
@PropertySource(“classpath:/init.properties”)
public class AppConfig1 {

  1. @Value("${id}")<br /> private Integer id;<br /> @Value("${name}")<br /> private String name;<br /> <br /> @Bean<br /> public Customer customer() {<br /> Customer customer = new Customer();<br /> customer.setId(id);<br /> customer.setName(name);
  2. return customer;<br /> }<br />}

3. @ComponentScan注解

@ComponentScan注解在配置bean中进行使用,等同于XML配置文件中的标签

目的:进行相关注解的扫描 (@Component @Value …@Autowired)

1. 基本使用

@Configuration
@ComponentScan(basePackages = “com.baizhiedu.scan”)
public class AppConfig2 {

}

2. 排除、包含的使用

  • 排除

@ComponentScan(basePackages = “com.baizhiedu.scan”,
excludeFilters = {@ComponentScan.Filter(type= FilterType.ANNOTATION,value={Service.class}),
@ComponentScan.Filter(type= FilterType.ASPECTJ,pattern = “*..User1”)})

type = FilterType.ANNOTATION value
.ASSIGNABLE_TYPE value
.ASPECTJ pattern
.REGEX pattern
.CUSTOM value

  • 包含

@ComponentScan(basePackages = “com.baizhiedu.scan”,
useDefaultFilters = false,
includeFilters = {@ComponentScan.Filter(type= FilterType.ANNOTATION,value={Service.class})})

type = FilterType.ANNOTATION value
.ASSIGNABLE_TYPE value
.ASPECTJ pattern
.REGEX pattern
.CUSTOM value

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

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

2. 配置优先级

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

@Component
public class User{

}

@Bean
public User user(){
return new 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 ### 5. 整合多个配置信息 - 为什么会有多个配置信息 拆分多个配置bean的开发,是一种模块化开发的形式,也体现了面向对象各司其职的设计思想 - 多配置信息整合的方式 - 多个配置Bean的整合 - 配置Bean与@Component相关注解的整合 - 配置Bean与SpringXML配置文件的整合 - 整合多种配置需要关注那些要点 - 如何使多配置的信息 汇总成一个整体 - 如何实现跨配置的注入 #### 1. 多个配置Bean的整合 - 多配置的信息汇总 - base-package进行多个配置Bean的整合 - @Import1. 可以创建对象 2. 多配置bean的整合 - 在工厂创建时,指定多个配置Bean的Class对象 【了解】ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig1.class,AppConfig2.class); - 跨配置进行注入在应用配置Bean的过程中,不管使用哪种方式进行配置信息的汇总,其操作方式都是通过成员变量加入@Autowired注解完成。
@Configuration
@Import(AppConfig2.class)
public class AppConfig1 { @Autowired
private UserDAO userDAO; @Bean
public UserService userService() {
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDAO(userDAO);
return userService;
}
} @Configuration
public class AppConfig2 { @Bean
public UserDAO userDAO() {
return new UserDAOImpl();
}
} #### 2. 配置Bean与@Component相关注解的整合 @Component(@Repository)
public class UserDAOImpl implements UserDAO{

} @Configuration
@ComponentScan(“”)
public class AppConfig3 {

@Autowired
private UserDAO userDAO; @Bean
public UserService userService() {
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDAO(userDAO);
return userService;
}
} ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig3.class); #### 3. 配置Bean与配置文件整合 1. 遗留系统的整合 2. 配置覆盖

public class UserDAOImpl implements UserDAO{

}
@Configuration
@ImportResource(“applicationContext.xml”)
public class AppConfig4 {

@Autowired
private UserDAO userDAO; @Bean
public UserService userService() {
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDAO(userDAO);
return userService;
}
} ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig4.class); ### 6. 配置Bean底层实现原理 Spring在配置Bean中加入了@Configuration注解后,底层就会通过Cglib的代理方式,来进行对象相关的配置、处理 ### 7. 四维一体的开发思想 #### 1. 什么是四维一体 Spring开发一个功能的4种形式,虽然开发方式不同,但是最终效果是一样的。
1. 基于schema
2. 基于特定功能注解
3. 基于原始4. 基于@Bean注解 #### 2. 四维一体的开发案例 1. 2. @PropertySource 【推荐】
3.
4. @Bean 【推荐】 ### 8. 纯注解版AOP编程 #### 1. 搭建环境 1. 应用配置Bean
2. 注解扫描 #### 2. 开发步骤 1. 原始对象
@Service(@Component)
public class UserServiceImpl implements UserService{

}
2. 创建切面类 (额外功能 切入点 组装切面)
@Aspect
@Component
public class MyAspect { @Around(“execution( login(..))”)
public Object arround(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println(“——aspect log ———“); Object ret = joinPoint.proceed(); return ret;
}
}
3. Spring的配置文件中

@EnableAspectjAutoProxy —-> 配置Bean #### 3. 注解AOP细节分析 1. 代理创建方式的切换 JDK Cglib

@EnableAspectjAutoProxy(proxyTargetClass)
2. SpringBoot AOP的开发方式
@EnableAspectjAutoProxy 已经设置好了

1. 原始对象
@Service(@Component)
public class UserServiceImpl implements UserService{ }
2. 创建切面类 (额外功能 切入点 组装切面)
@Aspect
@Component
public class MyAspect { @Around(“execution(
login(..))”)
public Object arround(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println(“——aspect log ———“); Object ret = joinPoint.proceed(); return ret;
}
}
Spring AOP 代理默认实现 JDK SpringBOOT AOP 代理默认实现 Cglib ### 9. 纯注解版Spring+MyBatis整合 - 基础配置 (配置Bean)1. 连接池








@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(“”);
dataSource.setUrl();

return dataSource;
} 2. SqlSessionFactoryBean






classpath:com.baizhiedu.mapper/*Mapper.xml


  1. @Bean<br /> public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){<br /> SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();<br /> sqlSessionFactoryBean.setDataSource(dataSource);<br /> sqlSessionFactoryBean.setTypeAliasesPackage("");<br /> ...<br /> return sqlSessionFactoryBean;<br /> }
  1. MapperScannerConfigure






    @MapperScan(basePackages={“com.baizhiedu.dao”}) —-> 配置bean完成
  • 编码1. 实体
    2. 表
    3. DAO接口
    4. Mapper文件 //设置Mapper文件的路径
    sqlSessionFactoryBean.setMapperLocations(Resource..);
    Resource resouce = new ClassPathResouce(“UserDAOMapper.xml”)

    sqlSessionFactoryBean.setMapperLocations(new ClassPathResource(“UserDAOMapper.xml”));



classpath:com.baizhiedu.mapper/*Mapper.xml


一组Mapper文件

ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources(“com.baizhi.mapper/Mapper.xml”);
sqlSessionFactoryBean.setMapperLocations(resources)mybatis.driverClassName = com.mysql.jdbc.Driver
mybatis.url = jdbc:mysql://localhost:3306/suns?useSSL=false
mybatis.username = root
mybatis.password = 123456
mybatis.typeAliasesPackages = com.baizhiedu.mybatis
mybatis.mapperLocations = com.baizhiedu.mapper/
Mapper.xml

@Component
@PropertySource(“classpath:mybatis.properties”)
public class MybatisProperties {
@Value(“${mybatis.driverClassName}”)
private String driverClassName;
@Value(“${mybatis.url}”)
private String url;
@Value(“${mybatis.username}”)
private String username;
@Value(“${mybatis.password}”)
private String password;
@Value(“${mybatis.typeAliasesPackages}”)
private String typeAliasesPackages;
@Value(“${mybatis.mapperLocations}”)
private String mapperLocations;
}

public class MyBatisAutoConfiguration {

  1. @Autowired<br /> private MybatisProperties mybatisProperties;
  2. @Bean<br /> public DataSource dataSource() {<br /> DruidDataSource dataSource = new DruidDataSource();<br /> dataSource.setDriverClassName(mybatisProperties.getDriverClassName());<br /> dataSource.setUrl(mybatisProperties.getUrl());<br /> dataSource.setUsername(mybatisProperties.getUsername());<br /> dataSource.setPassword(mybatisProperties.getPassword());<br /> return dataSource;<br /> }
  3. @Bean<br /> public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {<br /> SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();<br /> sqlSessionFactoryBean.setDataSource(dataSource);<br /> sqlSessionFactoryBean.setTypeAliasesPackage(mybatisProperties.getTypeAliasesPackages());<br /> //sqlSessionFactoryBean.setMapperLocations(new ClassPathResource("UserDAOMapper.xml"));
  4. try {<br /> ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();<br /> Resource[] resources = resolver.getResources(mybatisProperties.getMapperLocations());<br /> sqlSessionFactoryBean.setMapperLocations(resources);<br /> } catch (IOException e) {<br /> e.printStackTrace();<br /> }
  5. return sqlSessionFactoryBean;<br /> }<br />}

1. MapperLocations编码时通配的写法

2. 配置Bean数据耦合的问题

10. 纯注解版事务编程

  1. 原始对象 XXXService


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

  2. 额外功能





    @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的事务配置

    @EnableTransactionManager —-> 配置Bean
    1. ApplicationContext ctx = new AnnotationConfigApplicationContext(“com.baizhiedu.mybatis”);
    SpringBoot 实现思想
    2. 注解版MVC整合,SpringMVC中进行详细讲解
    SpringMyBatis —->DAO 事务基于注解 —> Service Controller
    org.springframework.web.context.ContextLoaderListener —-> XML工厂 无法提供 new AnnotationConfigApplicationContext

    11. Spring框架中YML的使用

    1. 什么是YML

    YML(YAML)是一种新形式的配置文件,比XML更简单,比Properties更强大。

YAML is a nice human-readable format for configuration, and it has some useful hierarchical properties. It’s more or less a superset of JSON, so it has a lot of similar features.

2. Properties进行配置问题

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

    3. YML语法简介

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

    4. Spring与YML集成思路的分析

  3. 准备yml配置文件
    init.yml
    name: suns
    password: 123456
    2. 读取yml 转换成 Properties
    YamlPropertiesFactoryBean.setResources( yml配置文件的路径 ) new ClassPathResource();
    YamlPropertiesFactoryBean.getObject() —-> Properties
    3. 应用PropertySourcesPlaceholderConfigurer
    PropertySourcesPlaceholderConfigurer.setProperties();
    4. 类中 @Value注解 注入

    5. Spring与YML集成编码

  • 环境搭建
    org.yaml
    snakeyaml
    1.23

    最低版本 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();

    1. PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();<br /> configurer.setProperties(properties);<br /> return configurer;<br /> }<br />3. 加入 @Value注解

    6. Spring与YML集成的问题

  1. 集合处理的问题
    SpringEL表达式解决
    @Value(“#{‘${list}’.split(‘,’)}”)
    2. 对象类型的YAML进行配置时 过于繁琐
    @Value(“${account.name}”)

    SpringBoot @ConfigurationProperties