一、Spring

1.1 Web项目开发中的耦合度的问题

  • 在Servlet中调用service的方法,则需要在Servlet类中通过new关键字来创建Service的实例
1
2
3
public interface ProductService{
public List<Product> listProducts();
}
1
2
3
4
5
public class ProductServiceImpl1 implements ProductService{
public List<Product> listProducts(){
//查询热销商品
}
}
1
2
3
4
5
public class ProductServiceImpl2 implements ProductService{
public List<Product> listProducts(){
//查询好评商品
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class ProductListServlet extends HttpServlet{

//在servlet中使用new关键字创建ProductServiceImpl1对象,增加了servlet和service的耦合度
private ProductService productService = new ProductServiceImpl1();

protected void doGet(HttpServletRequest request,HttpServletResponse response){
doPost(request,response);
}
protected void doPost(HttpServletRequest request,HttpServletResponse response){
productService.listProducts();
}
}
  • 在service实现类中需要调用DAO中的方法,也需要在servcie实现类通过new关键字创建DAO实现类对象
  • 如果使用new关键字创建对象:
    • 失去了面向接口编程的灵活性
    • 代码的侵入性增强(增加了耦合度)、降低了代码的灵活性
    • 增强项目的扩展性

1.2 面向接口编程

面向接口编程

解决方案:在Servlet中定义Service接口的对象变量,不使用new关键字创建实现类对象,在servlet的实例化的时候,通过反射动态的给Service对象变量赋值。

如何实现:Spring可以做到!!!

1.3 Spring简介

Spring是一个轻量级的控制反转和面向切面的容器框架,用来解决企业项目开发的复杂度问题—解耦

  • 轻量级:体积小,对代码没有侵入性
  • 控制反转:IoC(Inverse of Control),把创建对象的工作交由Spring完成,Spring在创建对象的时候同时可以完成对象属性赋值(DI)
  • 面向切面:AOP(Aspect Oriented Programming)面向切面编程,可以在不改变原有业务逻辑的情况下实现对业务的增强
  • 容器:实例的容器,管理创建的对象

1.4 Spring架构

  • 官网 https://spring.io/

  • Spring架构图

    • Spring Framework是Spring生态圈最基础的项目,是其他项目的基础
1. Core Container

Spring核心容器组件,用于完成实例的创建和管理

  • core
  • beans 实例管理
  • context 容器上下文
2. AOP、Aspects

Spring AOP组件,实现面向切面编程

  • aop
  • aspects
3. web

Spring web组件实际指的是SpringMVC框架,实现web项目的MVC控制

  • web (Spring对web项目的支持)
  • webmvc (SpringMVC组件)
4. Data Access

Spring数据访问组件,也是一个基于JDBC封装的持久层框架(即使没有mybatis,Spring也可以完成持久化操作)

  • Data Access:数据访问
  • Data Integeration:数据集成
5. Test

Spring的单元测试组件,提供了Spring环境下的单元测试支持

  • test

1.5 Spring Framework学习路线

  1. 核心容器
    1. 核心概念(IOC、DI)
    2. 容器基本操作
  2. 整合
    1. 整合MyBatis
  3. AOP
    1. 核心概念
    2. AOP基础操作
    3. AOP使用开发
  4. 事务
    1. 事务实用开发
  5. 家族
    1. SpringMVC
    2. SpringBoot
    3. SpringCloud

二、Spring IoC — 基于XML

2.1 Spring框架部署(Ioc)

SpringIoC容器组件,可以完成对象的创建、对象属性的赋值、对象管理

1.创建Maven工程

  • Java
  • Web

2. 添加SpringIoC依赖

  • core
  • beans
  • aop
  • context
  • expression
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>

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

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>

3. 创建Spring配置文件

通过配置文件“告诉”Spring容器创建什么对象,给对象属性赋什么值

  • 在resources目录下创建名为applicationContext.xml的文件(文件名可以自定义,但是约定俗成通用使用这个)
1
2
3
4
5
6
7
8
9
10
<?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">
<!-- 对于一个xml文件如果作为框架的配置文件,需要遵守框架的配置规则 -->
<!-- 通常一个框架为了让开发者能够正确的配置,都会提供xml的规范文件(dtd\xsd) -->


</beans>

3.1 如何定制配置文件模板

2.2 SpringIOC使用

使用SpringIOC组件创建并管理对象

1.创建一个实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public class Student {
private String stuNum;
private String stuName;
private String stuGender;
private int stuAge;
private Date enterenceTime;//入学日期

public String getStuNum() {
return stuNum;
}

public void setStuNum(String stuNum) {
this.stuNum = stuNum;
}

public String getStuName() {
return stuName;
}

public void setStuName(String stuName) {
this.stuName = stuName;
}

@Override
public String toString() {
return "Student{" +
"stuNum='" + stuNum + '\'' +
", stuName='" + stuName + '\'' +
", stuGender='" + stuGender + '\'' +
", stuAge=" + stuAge +
", enterenceTime=" + enterenceTime +
'}';
}

public String getStuGender() {
return stuGender;
}

public void setStuGender(String stuGender) {
this.stuGender = stuGender;
}

public int getStuAge() {
return stuAge;
}

public void setStuAge(int stuAge) {
this.stuAge = stuAge;
}

public Date getEnterenceTime() {
return enterenceTime;
}

public void setEnterenceTime(Date enterenceTime) {
this.enterenceTime = enterenceTime;
}
}

2.在Spring配置文件中配置实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?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">



<!--通过bean标签将实体类配置给Spring进行管理,id表示实体类的唯一标识-->
<bean id="student" class="com.qfedu.ioc.bean.Student">
<property name="stuNum" value="10002"></property>
<property name="stuName" value="水中月"></property>
<property name="stuGender" value="女"></property>
<property name="stuAge" value="18"></property>

</bean>



</beans>

3.初始化Spring对象工厂,获取对象

  • ClassPathXMLApplicationContext
1
2
3
4
5
6
7
8
//通过Spring容器创建对象
//1.初始化Spring容器,加载Spring配置文件,可以把context理解为Spring容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.通过Spring容器获取Student对象
Student student2 = (Student) context.getBean("student");


System.out.println(student2);

2.3 IoC和DI

  • IoC(Inverse of Control)控制反转,通过Spring对象工厂完成对象的创建
  • DI(Dependency Injection)依赖注入,在Spring完成对象创建的统一依赖Spring容器完成对对象属性的赋值

1. IoC

当我们需要通过Spring对象工厂创建某个类的对象的时候,需要将这个对象交给Spring管理–通过bean标签配置

1
2
3
4
<!--通过bean将实体类配置给Spring进行管理,id表示实体类的唯一表示-->
<bean id="stu" class="com.qfedu.ioc.bean.Student"></bean>

<bean id="book" class="com.qfedu.ioc.bean.Book"></bean>

2. DI

通过Spring容器给对象的属性进行赋值

1
2
3
4
5
6
7
8
9
10
<!--通过bean标签将实体类配置给Spring进行管理,id表示实体类的唯一标识-->
<bean id="student" class="com.qfedu.ioc.bean.Student">
<property name="stuNum" value="10002"></property>
<property name="stuName" value="水中月"></property>
<property name="stuGender" value="女"></property>
<property name="stuAge" value="18"></property>
<property name="enterenceTime" ref="date"></property>

</bean>
<bean id="date" class="java.util.Date"></bean>

2.4 DI依赖注入

1.依赖注入的三种方式

Spring容器加载配置文件之后通过反射创建类的对象,并给属性赋值;

Spring容器通过反射实现属性注入有三种方式:

  • set方法助注入
  • 构造器注入
  • 接口注入(不常用)

2. set方法注入

在bean标签中通过配置property标签给对象属性赋值,实际上就是通过反射调用set方法完成属性的注入

简单类型及字符串

  • 直接通过property标签的value属性赋值
1
2
3
4
5
6
7
8
9
<!--通过bean标签将实体类配置给Spring进行管理,id表示实体类的唯一标识-->
<bean id="student" class="com.qfedu.ioc.bean.Student">
<!--字符串类型-->
<property name="stuNum" value="10007"></property>
<!--简单类型-->
<property name="stuAge" value="12"></property>
<property name="wight" value="74.56"></property>

</bean>

日期类型

  • 方式1:在property标签中通过ref引用Spring容器中的对象
1
2
3
4
5
6
7
8
9
10
11
<!--通过bean标签将实体类配置给Spring进行管理,id表示实体类的唯一标识-->
<bean id="student" class="com.qfedu.ioc.bean.Student">
<!--日期类型-->
<property name="enterenceTime" ref="date"></property>

</bean>
<bean id="date" class="java.util.Date">
<property name="year" value="121"></property>
<property name="month" value="10"></property>
<property name="date" value="11"></property>
</bean>
  • 方式2:在property标签中添加字标签bean来指定对象
1
2
3
4
5
6
7
<bean id="student" class="com.qfedu.ioc.bean.Student">
<!--日期类型-->
<property name="enterenceTime">
<bean class="java.util.Date"/>
</property>

</bean>

自定义类对象属性

  • 方式1:在property标签中通过ref引用Spring容器中的对象
1
2
3
4
5
6
7
8
9
<bean id="student" class="com.qfedu.ioc.bean.Student">
<!--自定义类对象类型-->
<property name="clazz" ref="cla"></property>
</bean>

<bean id="cla" class="com.qfedu.ioc.bean.Clazz">
<property name="classId" value="2020"></property>
<property name="className" value="计算机技术一班"></property>
</bean>
  • 方式2:在property标签中添加字标签bean来指定对象
1
2
3
4
5
6
7
8
9
<bean id="student" class="com.qfedu.ioc.bean.Student">
<!--自定义类对象类型-->
<property name="clazz">
<bean id="cla" class="com.qfedu.ioc.bean.Clazz">
<property name="classId" value="2020"></property>
<property name="className" value="计算机技术一班"></property>
</bean>
</property>
</bean>

集合类型

  • List

    • List List中的元素是字符串类型或者简单类型的封装类时
    • 采用如下两种写法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <!--写法1-->
    <property name="hobbies" value="旅游,电影,游泳"/>
    <!--写法2-->
    <property name="hobbies">
    <list>
    <value>旅游</value>
    <value>打游戏</value>
    <value>Python</value>
    </list>
    </property>
    • ListList中的元素是对象类型时
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      <!--写法1 bean标签-->
      <property name="hobbies">
      <list>
      <bean class="com.qfedu.ioc.bean.Book"/>
      <bean class="com.qfedu.ioc.bean.Book"/>
      <bean class="com.qfedu.ioc.bean.Book"/>
      </list>
      </property>


      <!--写法2 引用-->
      <bean id="book" class="com.qfedu.ioc.bean.Book"/>
      <property name="hobbies">
      <list>
      <ref bean="book"></ref>
      <ref bean="book"></ref>
      <ref bean="book"></ref>
      <ref bean="book"></ref>
      </list>
      </property>

    • Set

      1
      2
      3
      4
      5
      <property name="sets">
      <set>
      <!--和List元素注入方式相同-->
      </set>
      </property>
    • Map

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      <property name="maps">
      <map>
      <entry>
      <key>
      <value>key1</value>
      </key>
      <value>value1</value>
      </entry>

      <entry>
      <key>
      <value>key2</value>
      </key>
      <value>value2</value>
      </entry>
      </map>
      </property>
    • Properties:properties继承自Hashtable类可以直接给键值对,因为properties里面的键值对只能是字符串类型

      1
      2
      3
      4
      5
      6
      <property name="properties">
      <props>
      <prop key="k1">aaa</prop>
      <prop key="k2">bbb</prop>
      </props>
      </property>
    • 3.构造器注入

      简单类型、字符串、Object类型

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      <bean id="clazz" class="com.qfedu.ioc.bean.Clazz"></bean>
      <bean id="date" class="java.util.Date"></bean>


      <bean id="stu" class="com.qfedu.ioc.bean.Student">
      <constructor-arg name="stuNum" value="10008"/> <!--字符串类型-->
      <constructor-arg name="stuGender" value="女"/>
      <constructor-arg name="stuName" value="焰灵姬"/>


      <constructor-arg name="stuAge" value="21"/> <!--简单类型-->
      <constructor-arg name="wight" value="46.53"/>
      <constructor-arg name="enterenceTime" ref="date"/> <!--对象类型-->
      <constructor-arg name="clazz"> <!--对象类型-->
      <bean class="com.qfedu.ioc.bean.Clazz"></bean>
      </constructor-arg>

      </bean>

      集合类型属性

      类代码:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      public class Student{
      private List<String> hobbies;
      private Set<String> sets;
      private Map<String,Object> maps;
      private Properties properties;

      public Student(List<String> hobbies, Set<String> sets, Map<String, Object> maps, Properties properties) {
      this.hobbies = hobbies;
      this.sets = sets;
      this.maps = maps;
      this.properties = properties;
      }
      }

      配置:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      <bean id="stu1" class="com.qfedu.ioc.bean.Student">
      <constructor-arg index="0">
      <list>
      <value>11</value>
      <value>22</value>
      <value>33</value>
      </list>
      </constructor-arg>
      <constructor-arg index="1">
      <set>
      <value>aa</value>
      <value>bb</value>
      <value>cc</value>
      </set>
      </constructor-arg>
      <constructor-arg index="2">
      <map>
      <entry>
      <key><value>key1</value></key>
      <value>value1</value>
      </entry>
      <entry>
      <key><value>key2</value></key>
      <value>value2</value>
      </entry>
      </map>
      </constructor-arg>
      <constructor-arg index="3">
      <props>
      <prop key="k1">v1</prop>
      <prop key="k2">v2</prop>
      </props>
      </constructor-arg>
      </bean>

      2.5 Bean的作用域

      在bean标签可以通过scope属性指定对象的的作用域

      • scope=”singleton” 表示当前bean是单例模式(默认饿汉模式,Spring容器初始化阶段就会完成此对象的创建;当在bean标签中设置 lazy-init=”true”变为懒汉模式)
      • scope=”prototype” 表示当前bean为非单例模式,每次通过Spring容器获取此bean的对象时都会创建一个新的对象
      • 单例
      1
      <bean id="book" class="com.qfedu.ioc.bean.Book" scope="singleton" lazy-init="true"></bean>
      • 多例
      1
      <bean id="book" class="com.qfedu.ioc.bean.Book" scope="prototype"></bean>

      2.6 Bean的生命周期方法

      • Bean类
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      public class Book {
      private int bookId;
      private String bookName;

      public void init(){
      //初始化方法:在创建当前类对象时调用的方法,进行一些资源准备工作
      System.out.println("Init.....");

      this.bookId = 1;
      this.bookName = "初始值:三国演义";
      }
      public void destory(){
      //销毁方法:在Spring容器销毁对象时调用此方法,进行一些资源回收性的工作
      }
      public Book(){
      System.out.println("我是构造器");
      }
      }
      • Spring配置文件
      1
      2
      3
      4
      5
      <!--在bean标签中通过init-method属性指定当前bean的初始化方法,初始化方法在构造器执行之后执行-->
      <!--在bean标签中通过destory-method属性指定当前bean的销毁方法,销毁方法在对象销毁之前执行-->

      <bean id="book" class="com.qfedu.ioc.bean.Book" scope="prototype"
      init-method="init" destroy-method="destory"></bean>

      2.7 自动装配

      自动装配:Spring在实例化当前bean的时候从Spring容器中找到匹配的实例赋值给当前bean的属性

      自动装配策略有两种:

      • byName 根据当前Bean的属性名在Spring容器中寻找匹配的对象 ,如果根据name找打了bean但是类型不匹配则抛出异常
      • byType 根据当前Bean的属性类型在Spring容器中寻找匹配的对象,如果根据类型找到了多个bean也会抛出异常
      • byName
      1
      2
      3
      4
      <!--byName   根据当前bean的属性名在Spring容器中寻找匹配的对象,如果根据name找到了bean但是类型不匹配则会抛出异常-->
      <bean id="clazz" class="com.qfedu.ioc.bean.Clazz"></bean>

      <bean id="stu1" class="com.qfedu.ioc.bean.Student" autowire="byName"></bean>
      • byType
      1
      2
      3
      4
      <!--byName   根据当前bean的属性名在Spring容器中寻找匹配的对象,如果根据name找到了bean但是类型不匹配则会抛出异常-->
      <bean id="clazz2" class="com.qfedu.ioc.bean.Clazz"></bean>

      <bean id="stu1" class="com.qfedu.ioc.bean.Student" autowire="byType"></bean>

      2.8 SpringIoC工作原理

      image-20220415090803104

      三、Spring IoC-基于注解

      SpringIoC的使用,需要通过xml将类声明给Spring容器进行管理,进而通过Spring工厂完成对象的创建以及属性值的注入;

      Spring除了提供基于xml的配置方式,同时还提供了注解的配置:直接在实体类中添加注解声明给Spring容器管理,以简化开发步骤。

      3.1 Spring 框架部署

      1.创建Maven项目

      2.添加SpringIoC依赖

      1
      2
      3
      4
      5
      <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.13.RELEASE</version>
      </dependency>

      3.创建Spring配置文件

      • Spring容器初始化时,只会加载applicationContext.xml文件,在实体类中添加的注解不会被Spring扫描,所以需要在applicationContext.xml声明Spring的扫描范围,以达到Spring初始化扫描带有注解的实体类并完成初始化工作。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:context="http://www.springframework.org/schema/context"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context.xsd">
      <!-- 对于一个xml文件如果作为框架的配置文件,需要遵守框架的配置规则 -->
      <!-- 通常一个框架为了让开发者能够正确的配置,都会提供xml的规范文件(dtd\xsd) -->

      <!--声明使用注解配置-->
      <context:annotation-config/>

      <!--声明Spring工厂注解的扫描范围-->
      <context:component-scan base-package="com.qfedu.beans"/>

      </beans>

      3.2 SpringIoC常用注解

      1. @Component

      • 类注解,声明此实体类被Spring容器进行管理,相当于bean标签的作用
      • @Component(value = "stu"):value属性用于指定当前bean的id,相当于bean标签的id属性;value属性可以忽略不写,如果省略则当前bean的id默认为类名的首字母小写
      • 除了@Component之外,@Service@Controller@Repository这三个注解也可以将类声明给Spring管理,但是有语义上的区别:
        • @Controller 注解主要声明将控制器类配置给Spring管理,例如Servlet
        • @Service 注解主要声明业务处理类配置Spring管理,Service接口的实现类
        • @Repository 直接主要声明持久化类配置给Spring管理,DAO接口
        • @Component 除了控制器、servcie和DAO之外的类一律使用此注解声明

      2. @Scope

      • 类注解,用于声明当前类单例模式还是 非单例模式,相当于bean标签的scope属性
      • @Scope(“prototype”) 表示声明当前类为非单例模式(默认单例模式)

      3. @Lazy

      • 类注解,用于声明一个单例模式的Bean是否为懒汉模式
      • @Lazy(true) 表示声明为懒汉模式,默认为饿汉模式

      4. @PostConstruct

      • 方法注解,声明一个方法为当前类的初始化方法(在构造器之后执行),相当于bean标签的init-method属性

      5. @PreDestroy

      • 方法注解,声明一个方法为当前类的销毁方法(在对象从容器中释放之前执行),相当于bean标签的destory-method属性

      6. @Autowired

      • 属性注解、方法注解(set方法),声明当前属性为自动装配,默认是byType

      • @Autowired(required = false)通过required属性来设置当前自动装配是否是必须的(默认为必须–如果在Spring容器中没有找到与属性类型相匹配的bean会抛出异常),自动装配的两种方式:

        • byType
        • ref引用
        1
        2
        3
        4
        @Autowired
        public void setClazz(@Qualifier("c1") Clazz clazz) {
        this.clazz = clazz;
        }

      7.@Resource

      • 属性注解,也用于声明属性自动装配
      • 默认装配方式为byName,如果根据byName没有找到对应的bean,则继续根据byType继续寻找对应的bean,根据byType也没有找到对应的bean或者找到了不止一个类型匹配的bean,则抛出异常。

      四、代理设计模式

      4.1 生活中的代理

      1616999162162

      代理设计模式的优点:将通用性的工作都交给代理对象完成,被代理对象只需专注自己的核心业务。

      4.2 静态代理

      静态代理,代理类只能够为特定的类生产代理对象,不能代理任意类

      1617001027208

      使用代理的好处:

      1. 被代理类中只需要关注核心业务的实现,将通用的管理型逻辑(事务处理、日志管理等操作)和业务逻辑分离
      2. 将通用的代码放在代理类中实现,提供了代码的复用性
      3. 通过在代理类中添加代码业务逻辑,实现对原有业务逻辑的扩展增强

      4.3 动态代理

      动态代理,几乎可以为所有的类产生代理对象

      实现方式有两种:

      • JDK动态代理
      • CGLib动态代理

      1. JDK动态代理

      • JDK动态代理类实现:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      /**
      * @auther xiaochen
      * @create 2022-04-18 8:53
      * JDk动态代理:使用过被代理对象实现的接口产生其代理对象的
      * 只能为实现了接口的类产生代理对象
      *
      * 1.创建一个类,实现InvocationHandler接口,重写invoke方法
      * 2.在类中定义一个Object类型的变量,并提供这个变量的有参构造器,用于将被代理对象传递进来
      * 3.定义getProxy方法,用于创建并返回代理对象
      *
      */
      public class JDKDynamicProxy implements InvocationHandler {

      //被代理对象
      private Object obj;

      //通过构造器传入被代理对象
      public JDKDynamicProxy(Object obj) {
      this.obj = obj;
      }


      //产生并返回代理对象
      public Object getProxy() {
      //1.获取被代理对象的类加载器
      ClassLoader classLoader = obj.getClass().getClassLoader();
      //2.获取被代理对象的类实现的接口
      Class<?>[] interfaces = obj.getClass().getInterfaces();
      //3.产生代理对象(通过被代理对象的类加载器及实现的接口)
      //第一个参数:被代理对象的类加载器
      //第二个参数:被代理对象实现的接口
      //第三个参数:使用产生的代理对象,调用方法时,用于拦截方法执行的拦截器

      Object proxy = Proxy.newProxyInstance(classLoader, interfaces, this);
      return proxy;
      }
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

      begin();
      Object returnVal = method.invoke(obj,args);//执行method方法(insert)
      commit();

      return returnVal;
      }

      public void begin(){
      System.out.println("======开启事务=====");
      }

      public void commit(){
      System.out.println("======提交事务=====");
      }



      }
      • 测试:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      public class TestDynamicProxy {
      public static void main(String[] args) {

      //1.创建被代理对象
      BookDAOImpl bookDAO = new BookDAOImpl();

      //2.创建动态代理对象,并将被代理对象传递到代理类中,赋值给obj
      JDKDynamicProxy jdkDynamicProxy = new JDKDynamicProxy(bookDAO);

      //proxy就是产生的代理对象,产生的代理对象可以强制转换成被代理对象实现的接口类型
      GenaralDao proxy = (GenaralDao) jdkDynamicProxy.getProxy();
      //使用代理对象调用方法,并不会执行调用的方法,而是进入到创建代理对象时指定InvocationHandler类中的invoke方法
      //调用的方法作为一个参数,传递给了invoke方法
      proxy.delete();
      // bookDAO.delete();
      }
      }

      2. CGLib动态代理

      由于JDK动态代理是通过被代理类实现的接口来创建代理对象的,因此JDK动态代理只能代理实现了接口的类的对象。如果一个类没有实现任何接口,该如何产生代理对象呢?

      CGLib动态代理,是通过创建被代理类的子类来创建代理对象的,因此即使没有实现任何接口的类也可以通过CGLib产生代理对象

      注:CGLib动态代理不能为final类创建代理对象,因为被final关键字修饰的类不能被继承

      • 添加CGlib的依赖
      1
      2
      3
      4
      5
      6
      <!-- https://mvnrepository.com/artifact/cglib/cglib -->
      <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>3.3.0</version>
      </dependency>
      • CGLib动态代理类实现:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      /**
      * @auther xiaochen
      * @create 2022-04-18 9:51
      * 1.添加CGLib依赖
      * 2.创建一个类,实现MethodInterceptor,同时实现接口中的intercept方法
      * 3.在类中定义一个Object类型的变量,并提供这个变量的you参构造器,用于传递被代理对象
      * 4.定义getProxy方法创建并返回代理对象(代理对象是通过创建被代理类的子类来创建的)
      *
      */
      public class CGLibDynamicProxy implements MethodInterceptor {
      private Object obj;

      public CGLibDynamicProxy(Object obj) {
      this.obj = obj;
      }

      public Object getProxy(){
      Enhancer enhancer = new Enhancer();
      enhancer.setSuperclass(obj.getClass());
      enhancer.setCallback(this);
      Object proxy = enhancer.create();
      return proxy;
      }

      public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
      begin();
      //通过反射调用被代理类中的方法
      Object returnVal = method.invoke(obj,objects);
      commit();

      return returnVal;
      }

      public void begin(){
      System.out.println("======开启事务=====");
      }

      public void commit(){
      System.out.println("======提交事务=====");
      }

      }
      • 测试:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      public class TestDynamicProxy {
      public static void main(String[] args) {

      //1.创建被代理对象
      BookDAOImpl bookDAO = new BookDAOImpl();
      //2.通过CGLib动态代理类创建代理对象
      CGLibDynamicProxy cgLibDynamicProxy = new CGLibDynamicProxy(bookDAO);
      //3.代理对象实际上是被代理对象的子类,因此代理对象可以强转为被代理类类型
      BookDAOImpl proxy = (BookDAOImpl) cgLibDynamicProxy.getProxy();
      //4.使用代理对象调用方法,实际上并没有执行这个方法,而是执行了代理类中的intercept方法,将当前调用的方法以及方法中的
      //参数传递到intercept方法中
      proxy.update();
      }

      五、 Spring AOP

      5.1 AOP概念

      Aspect Oriented Programming ,面向切面编程,是一种利用”横切”的技术(底层实现就是动态代理),对原有的业务逻辑进行拦截,并且可以在这个拦截的横切面上添加特定的业务逻辑,对原有的业务进行增强。

      基于动态代理的实现可以在不改变原有业务的情况下对业务进行增强。

      image-20220415184022329

      5.2 Spring AOP框架部署

      1. 创建Maven项目

      2. 添加依赖

      • context
      • aspect
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.13.RELEASE</version>
      </dependency>

      <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.2.13.RELEASE</version>
      </dependency>

      3.创建Spring配置文件

      • 引入aop的命名空间
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:aop="http://www.springframework.org/schema/aop"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/aop
      http://www.springframework.org/schema/aop/spring-aop.xsd">


      </aop:config>
      </beans>

      5.3 AOP配置- 语句XML

      给DAO的方法添加开启事务和提交事务的逻辑

      1.创建一个类,定义要添加的业务逻辑

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      public class TxManager {
      public void begin() {
      System.out.println("开启事务");

      }

      public void commit() {
      System.out.println("提交事务");

      }
      }

      2. 配置AOP

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:aop="http://www.springframework.org/schema/aop"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/aop
      http://www.springframework.org/schema/aop/spring-aop.xsd">
      <!-- 对于一个xml文件如果作为框架的配置文件,需要遵守框架的配置规则 -->
      <!-- 通常一个框架为了让开发者能够正确的配置,都会提供xml的规范文件(dtd\xsd) -->



      <bean id="bookDAO" class="com.qfedu.dao.BookDAOImpl"></bean>

      <bean id="txManager" class="com.qfedu.dao.utils.TxManager"></bean>
      <aop:config>
      <!--声明切入点-->
      <aop:pointcut id="book_all" expression="execution(* com.qfedu.dao.BookDAOImpl.*(..))"/>

      <!--声明txManager为切面-->
      <aop:aspect ref="txManager">
      <!--增强-->
      <aop:before method="begin" pointcut-ref="book_all"></aop:before>
      <aop:after method="commit" pointcut-ref="book_all"></aop:after>


      </aop:aspect>

      </aop:config>
      </beans>

      AOP开发步骤:

      1. 创建切面类,在切面类定义切点方法
      2. 将切面类配置给Spring容器
      3. 声明切入点
      4. 配置AOP的通知策略

      5.4 切入点的声明

      1. 各种切入点声明方式

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      <!--使用aop:pointcut标签声明切入点:切入点可以是一个方法-->
      <aop:pointcut id="book_insert" expression="execution(* com.qfedu.dao.BookDAOImpl.insert())"/>
      <!--BookDAOImpl类中所有无参数无返回值的方法-->
      <aop:pointcut id="book_pc1" expression="execution(void com..qfedu.dao.BookDAOImpl.*())"/>

      <!--BookDAOImpl类中所有无返回值的方法,对参数有没有没有限制-->
      <aop:pointcut id="book_pc2" expression="execution(void com..qfedu.dao.BookDAOImpl.*(..))"/>

      <!--BookDAOImpl类中所有无参数的方法-->
      <aop:pointcut id="book_pc3" expression="execution(* com..qfedu.dao.BookDAOImpl.*())"/>


      <!--BookDAOImpl类中所有的方法-->
      <aop:pointcut id="book_pc4" expression="execution(* com..qfedu.dao.BookDAOImpl.*(..))"/>

      <!--dao包中所有类中所有的方法-->
      <aop:pointcut id="pc5" expression="execution(* com..qfedu.dao.*.*(..))"/>

      <!--dao包中所有类中所有的方法-->
      <aop:pointcut id="pc6" expression="execution(* com..qfedu.dao.*.insert(..))"/>

      <!--所有包下的所有类的所有方法-->
      <aop:pointcut id="pc7" expression="execution(* *(..))"/>

      2. AOP使用注意事项

      • 如果使用Spring AOP面向切面编程,调用切入点方法的对象必须通过Spring容器获取
      • 如果一个类中的方法被声明为切入点并且织入了切点之后,通过Spring容器获取到的该类对象其实获取到的是一个该类的代理对象
      • 如果一个类中的方法没有声明为切入点,通过Spring容器获取的就是这个类真实创建的对象

      5.5 AOP通知策略

      AOP通知策略:就是声明将切面类中的切点方法如何织入到切入点

      • before
      • after-returning
      • after-throwing
      • around
      • after

      1.定义切面类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      package com.qfedu.dao.utils;

      import org.aspectj.lang.ProceedingJoinPoint;

      /**
      * @auther xiaochen
      * @create 2022-04-19 9:10
      */
      public class MyAspect {
      public void method1() {
      System.out.println("method1");
      }

      public void method2() {
      System.out.println("method2");
      }

      public void method3() {
      System.out.println("method3");
      }

      public void method4() {
      System.out.println("method4");
      }

      //环绕通知的切点方法,必须准守如下的定义规则:
      //1.必须带有一个ProceedingJoinPoint类型的参数
      //2.必须有Object类型的返回值
      //3.在前后增强的业务逻辑之间执行Object v = point.proceed();
      //4.方法最后返回v
      public Object method5(ProceedingJoinPoint point) throws Throwable {
      System.out.println("method5-before");
      //此代码的执行,就表示切入点方法的执行
      Object v = point.proceed();
      System.out.println("method5-after");
      return v;
      }

      }

      2. 配置切面类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      <aop:aspect ref="myAspect">
      <!--aop:before 用于配置前置通知,切入到指定切入点之前执行-->
      <aop:before method="method1" pointcut-ref="book_insert"/>

      <!--aop:after-throwing 用于配置异常通知,切入点方法抛出异常后执行-->
      <aop:after-throwing method="method3" pointcut-ref="book_insert"/>

      <!--aop:after-returning 用于配置后置通知,切入到指定切入点之后执行。
      对于一个Java方法而言,return返回值也是方法的一部分
      因此“方法返回值返回之后”和“方法执行结束”是同一个时间点,所以after和after-returning是根据
      配置的顺序决定执行顺序-->
      <aop:after-returning method="method4" pointcut-ref="book_insert"/>

      <!--环绕通知 用于配置环绕通知。指定增强的方法在切入点方法之前和之后都执行-->
      <aop:around method="method5" pointcut-ref="book_insert"/>

      <!--aop:after 用于配置最终通知。无论增强方式时候执行是否有异常都会执行-->
      <aop:after method="method2" pointcut-ref="book_insert"/>

      </aop:aspect>

      六、Spring AOP注解配置

      6.1 Spring AOP注解配置框架部署

      1.创建Maven工程

      2.添加Spring依赖

      • context
      • aspects

      3.Spring配置文件

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:context="http://www.springframework.org/schema/context"
      xmlns:aop="http://www.springframework.org/schema/aop"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context.xsd
      http://www.springframework.org/schema/aop
      http://www.springframework.org/schema/aop/spring-aop.xsd">


      <context:annotation-config></context:annotation-config>
      <context:component-scan base-package="com.qfedu"></context:component-scan>
      <!--基于注解配置的aop代理-->
      <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

      </beans>

      6.2 AOP注解配置案例

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      package com.qfedu.utils;

      import org.aspectj.lang.ProceedingJoinPoint;
      import org.aspectj.lang.annotation.*;
      import org.springframework.stereotype.Component;

      /**
      * @auther xiaochen
      * @create 2022-04-19 14:52
      */

      @Component
      @Aspect
      public class TransactionManager {

      @Pointcut("execution(* com.qfedu.dao.*.*(..))")
      public void pc1() {

      }


      @Before("pc1()")
      public void begin() {
      System.out.println("开启事务");
      }

      @After("pc1()")
      public void commit() {
      System.out.println("提交事务");
      }

      @Around("pc1()")
      public Object printExecuteTime(ProceedingJoinPoint point) throws Throwable {
      {
      long time1 = System.currentTimeMillis();
      Object v = point.proceed();
      long time2 = System.currentTimeMillis();
      System.out.println("方法执行时间:" + (time2 - time1));
      return v;
      }

      }
      }

      注意:注解使用虽然方便,但是只能在源码上添加注解,因此我们的自定义类提倡使用注解配置;但如果使用到第三方提供的类则需要通过xml配置形式完成配置。

      七、Spring整合MyBatis

      Spring 两大核心思想:IoC 和AOP

      IoC:控制反转,Spring容器可以完成对象的创建、属性注入、对象管理等工作

      AOP:面向切面,在不修改原有业务逻辑的情况下,实现原有业务的增强

      7.1 Spring可以对MyBatis提供哪些支持

      • IoC支持:SpringIoC可以为MyBatis完成DataSource、SQLSessionFactory、SQLSession以及DAO对象的创建。
      • AOP支持:使用Spring提供的事务管理切面类完成对MyBatis数据库操作中的事务管理

      7.2 Spring整合MyBatis准备工作

      1.创建Maven工程

      2.部署MyBatis框架

      • 添加依赖

        • MySQL驱动
        • MyBatis
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.4.6</version>
        </dependency>
      • 创建MyBatis配置文件mybatis-config.xml(创建配置文件之后无需进行任何配置)

      1
      2
      3
      4
      5
      6
      7
      <?xml version="1.0" encoding="UTF-8" ?>
      <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
      "http://mybatis.org/dtd/mybatis-3-config.dtd">
      <configuration>


      </configuration>

      3.部署Spring框架

      • 添加依赖
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.13.RELEASE</version>
      </dependency>

      <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.2.13.RELEASE</version>
      </dependency>

      <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.13.RELEASE</version>
      </dependency>
      • 创建Spring配置文件:applicationContext.xml
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:context="http://www.springframework.org/schema/context"
      xmlns:aop="http://www.springframework.org/schema/aop"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context.xsd
      http://www.springframework.org/schema/aop
      http://www.springframework.org/schema/aop/spring-aop.xsd">


      </beans>

      4.添加Spring整合MyBatis的依赖

      • mybatis-spring 就是mybatis提供的兼容Spring的补丁
      1
      2
      3
      4
      5
      6
      <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
      <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.2</version>
      </dependency>

      7.3 Spring整合MyBatis整合IoC配置

      1.整合Druid连接池

      • 添加druid的依赖
      1
      2
      3
      4
      5
      6
      <!--druid的依赖-->
      <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.10</version>
      </dependency>
      • 创建druid.properties属性文件
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      druid.driver=com.mysql.jdbc.Driver
      druid.url=jdbc:mysql://localhost:3306/db1?characterEncoding=utf-8
      druid.username=root
      druid.password=123456

      ## 连接池参数
      #初始化连接数
      druid.pool.init=1
      #初始化连接数
      druid.pool.minIdle=3
      druid.pool.maxActive=20
      druid.pool.timeout=30000
      • 在applicationContext.xml中配置DruidDataSource
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      <!--加载durid.properties属性文件-->
      <context:property-placeholder location="classpath:druid.properties"/>


      <!--依赖Spring容器完成数据源DataSource的创建-->
      <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
      <property name="driverClassName" value="${druid.driver}"/>
      <property name="url" value="${druid.url}"/>
      <property name="username" value="${druid.username}"/>
      <property name="password" value="${druid.password}"/>

      <property name="initialSize" value="${druid.pool.init}"/>
      <property name="minIdle" value="${druid.pool.minIdle}"/>
      <property name="maxActive" value="${druid.pool.maxActive}"/>
      <property name="maxWait" value="${druid.pool.timeout}"/>
      </bean>

      2.整合MyBatis-创建SQLSessionFactory

      依赖Spring容器创建MyBatis的SQLSessionFactory对象

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      <!--依赖Spring容器完成MyBatis的SQLSessionFactory对象的创建-->
      <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
      <!--配置数据源-->
      <property name="dataSource" ref="druidDataSource"/>
      <!--配置mapper映射文件的路径-->
      <property name="mapperLocations" value="classpath:mappers/*Mapper.xml"/>
      <!--配置需要定义别名的实体类的包-->
      <property name="typeAliases" value="com.qfedu.pojo"/>
      <!--可选:配置MyBatis的主配置文件-->
      <property name="configLocation" value="classpath:mybatis-config.xml"/>
      </bean>

      3. 整合MyBatis-创建Mapper

      1
      2
      3
      4
      5
      6
      <!--加载dao包中的所有DAO接口,通过sqlSessionFactory获取SQLSession,然后创建所有的DAO接口对象
      存储在Spring容器中-->
      <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
      <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
      <property name="basePackage" value="com.qfedu.dao"/>
      </bean>

      7.4 Spring整合MyBatis整合AOP配置

      使用Spring提供的事务管理切面类,完成DAO中增删改操作的事务管理

      1.事务的隔离级别

      I'm so cute. Please give me money.
      有没有什么要和我说的呢?
      本博客已萌萌哒地运行(*╹▽╹*)