Spring知识点详解

目录

1.了解Spring

1.1.说说你对框架的了解

1.2 Spring是什么

1.3 Spring的优势

1.4 Spring的体系结构

 1.4.1 核心容器

1.4.2 数据访问/集成

1.4.3 Web

1.4.4 其他

2、Spring核心之IoC控制反转

2.1 什么是IoC

2.2 Spring入门案例

2.2.1 创建maven的java项目

 2.2.2 pom.xml文件添加依赖和插件

2.2.3 创建一个实体类

2.3.4 创建Spring的配置文件application.xml

 2.3.5 使用Spring容器创建对象

2.3.6 两种方式获取Spring IoC容器和区别

2.3.7 通过上下文对象获取容器中的对象

2.3.8 创建非自定义对象和容器其它api

 2.3.9 bean标签的属性

2.3 Spring容器创建对象的方式

2.3.1 使用默认的构造方法

2.3.2 使用带参数的构造方法

2.3.3 使用工厂类

2.4 基于XML的DI

2.4.0.什么是DI,与IoC之间的关系

2.4.1 注入分类

 2.5、基于注解实现IoC--重要

2.5.1 声明Bean的注解 @Component

2.5.2 包扫描

属性注入的注解实现

2.5.3 属性注入@Vaule

2.5.4 @Autowired 自动注入 方式一:byType

2.5.5 @Autowired自动注入 方式二:byName

2.5.6 自动注入@Resource

 4、Spring核心之AOP

4.1.说说你对AOP的理解

4.2 AOP的实现机制-动态代理

4.2.1 什么是代理模式

4.3静态代理

4.3.1 原有方式:核心业务和服务方法都编写在一起

4.3.2 基于类的静态代理

4.3.3 基于接口的静态代理

4.3.4 提取出切面代码,作为AOP接口

4.4 动态代理

 4.4.2 基于CGLIB的动态代理

4.5 Spring AOP

4.5.1.说说你对Spring AOP的理解

4.5.2 AspectJ 对 AOP 的实现

4.5.3 Spring中通过注解方式实现AOP

4.5.4 XML方式实现AOP

5、Spring整合JDBC

5.1 使用spring-jdbc操作数据库

5.2. Spring管理JdbcTemplate

6、Spring事务管理

6.1 Spring事务管理API

6.1.1 事务管理器接口

6.1.2 事务定义接口

6.2 声明式事务控制

6.3 基于注解的事务

6.4 基于XML的事务




需要掌握的知识点:

Spring框架能做什么的,Spring三个核心IoC、AOP、事务,如何实现IoC、AOP、事务(步骤)

什么是控制反转IoC、什么是依赖注入DI,之间有什么关系

什么是AOP,为什么要使用AOP,有什么好处呢,底层实现原理是什么,说说代理模式
spring中是如何支持事务的?

1.了解Spring

1.1.说说你对框架的了解

框架( Framework ):框(指其约束性)架(指其支撑性),在软件设计中指为解决一个开放性问题而设计的具有一定约束性的支撑结构。在此结构上可以根据具体问题扩展、安插更多的组成部分,从而更迅速和方便地构建完整的解决问题的方案。
如何学习框架呢?
1 、知道框架能做什么
2 、学习框架的语法,一般框架完成一个功能需要一定的步骤
3 、框架的内部实现原理(扩展)
4 、尝试实现一个框架(提升)

1.2 Spring是什么

是一个是分层的 Java SE/EE full-stack 全栈 开源轻量级 Java 开发框架

Spring 具有 控制反转(IOC) 面向切面(AOP) 两大核心。
通过声明方式灵活进行 事务管理 ,提高开发效率和质量。

1.3 Spring的优势

1、方便解耦,简化开发

Spring 就是一个大工厂,可以将所有对象的创建和依赖关系的维护交给 Spring 管理。

2、方便集成各种优秀框架

Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如 Struts2 Hibernate
MyBatis 等)的直接支持。

3、降低 Java EE API 的使用难度

Spring Java EE 开发中非常难用的一些 API JDBC JavaMail 、远程调用等)都提供了封装,
使这些 API 应用的难度大大降低。

4、方便程序的测试
Spring 支持 JUnit4 ,可以通过注解方便地测试 Spring 程序。

5、AOP 编程的支持
Spring 提供面向切面编程,可以方便地实现对程序进行权限拦截和运行监控等功能。

6、声明式事务的支持
只需要通过配置就可以完成对事务的管理,而无须手动编程。

1.4 Spring的体系结构

Spring 为我们提供了一站式解决方案(想要的解决方案都要),但 它把这些解决方案进行模块化 的,在实际开发中可以根据自己的需求去引入需要的模块/方案,不需要全部引入
Spring 框架提供约 20 个模块,可以根据应用程序的要求来选择。

 1.4.1 核心容器

核心容器由 Spring-core Spring-beans Spring-context Spring-context-support Spring- expression SpEL Spring 表达式语言, Spring Expression Language )等模块组成
core核心模块:spring中的IoC和DI都是由它实现的
beans模块:管理Java对象直接依赖关系
context模块:在由 core beans 模块的基础上建立起来的,ApplicationContext 接口是 Context 模块的焦点
SpEL模块: 提供了强大的表达式语言,用于在运行时查询和操作对象
它们的完整依赖关系如下图所示:

1.4.2 数据访问/集成

JDBC=Java Data Base Connectivity
ORM=Object Relational Mapping
OXM=Object XML Mapping
JMS=Java Message Service

JDBC 模块提供  JDBC 抽象层,它消除冗长  JDBC 编码和对数据库供应商特定错误代码的解析。

ORM 模块提供了对流行的对象关系映射 API 的集成,包括 JPA JDO Hibernate 等。通过此模
块可以让这些 ORM 框架和 Spring 的其它功能整合,比如前面提及的事务管理。

OXM 模块提供了对 OXM 实现的支持,比如 JAXB Castor XML Beans JiBX XStream 等。

JMS 模块包含生产( produce )和消费( consume )消息的功能。从 Spring 4.1 开始,集成了
Spring-messaging 模块。

事务 模块为实现特殊接口类及所有的 POJO 支持编程式和声明式事务管理。

1.4.3 Web

Web 模块提供面向 web 的基本功能和面向 web 的应用上下文,比如多部分( multipart )文件上
传功能、使用 Servlet 监听器初始化 IoC 容器等。它还包括 HTTP 客户端以及 Spring 远程调用中与web 相关的部分。
Web-MVC 模块为 web 应用提供模型视图控制( MVC )和 REST Web 服务的实现。 Spring
MVC 框架可使领域模型代码和 web 表单完全分离,且可与 Spring 框架的其它所有功能进行成。
Web-Socket 模块为 WebSocket-based 提供了支持,而且在 web 应用程序中提供了客户端和服
务器端之间通信的两种方式。
Web-Portlet 模块提供了用于 Portlet 环境的 MVC 实现,并反映了 Spring-webmvc 模块的功能

1.4.4 其他

AOP 模块提供了面向方面(切面)的编程实现,允许你定义方法拦截器和切入点对代码进行干净
地解耦,从而使实现功能的代码彻底的解耦出来。
Aspects 模块提供了与 AspectJ 的集成,这是功能强大且成熟的面向切面编程( AOP )框架。
Instrumentation 模块在一定应用服务器中提供了类 instrumentation 的支持和类加载器的实现。

Messaging 模块为 STOMP 提供了支持作为在应用程序中 WebSocket 子协议的使用。它也支持一 个注解编程模型,它是为了选路和处理来自 WebSocket 客户端的 STOMP 信息。

测试 模块支持对具有 JUnit TestNG 框架的 Spring 组件的测试。

2Spring核心之IoC控制反转

什么是控制反转IoC、什么是依赖注入DI,之间有什么关系

2.1 什么是IoC

Ioc—Inversion of Control,即控制反转,不是什么技术,而是一种设计思想。

IoC 是指在程序开发中,实例创建不再由调用者管理,而是由 Spring 容器创建。 Spring 容器会负责控制程序之间的关系,而不是由程序代码直接控制,因此, 控制权由程序代码转移到了 Spring 容器中,控制权发生了反转 ,这就是 Spring IoC 思想,spring容器是通过DI依赖管理实现IOC。
生成对象几种方式:构造方法new   反射   克隆  序列化  动态代理
javaweb  servlet --doGet  doPost  --实例方法  没有new Servlet  由tomcat容器创建  

2.2 Spring入门案例

2.2.1 创建maven的java项目

 2.2.2 pom.xml文件添加依赖和插件

<dependencies>
    <!--单元测试-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.10</version>
        <scope>test</scope>
    </dependency>
    <!--spring依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.6</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <!--maven编译插件-->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.0</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
    </plugins>
</build>

2.2.3 创建一个实体类

2.3.4 创建Spring的配置文件application.xml

 2.3.5 使用Spring容器创建对象

配置文件中创建对象

2.3.6 两种方式获取Spring IoC容器和区别

Spring 提供了两种 IoC 容器
         分别为 BeanFactory ApplicationContext.
主要区别:
BeanFactory获取容器后,只有在getBean获取对象的时候才生成对象
ApplicationContext获取容器后就会生成对象
        
2.3.6.1 BeanFactory接口
BeanFactory 是基础类型的 IoC 容器 , 是一个管理 Bean 的工厂,它主要负责初始化各种 Bean ,并调用它们的生命周期方法。
BeanFactory 接口有多个实现类,最常见的是 XmlBeanFactory 
它是根据 XML 配置文件中的定义装配Bean 的。
BeanFactory beanFactory = new XmlBeanFactory(new FileSystemResource(Spring配置文件
的名称));

2.3.6.2 ApplicationContext接口

ApplicationContext BeanFactory 的子接口,也被称为 应用上下文
它在 BeanFactory 基础上,还添加了对 i18n (国际化)、资源访问、事件传播等方面的良好支持。
ApplicationContext 接口有两个常用的实现类 :
2.6.2.2.1 ClassPathXmlApplicationContext —— 常用
该类从类路径 ClassPath 中寻找指定  XML 文件,找到并装载完成 ApplicationContext 实例化工作
2.6.2.2.2 FileSystemXmlApplicationContext
它与 ClassPathXmlApplicationContext 区别是
在读取 Spring 的配置文件时,FileSystemXmlApplicationContext 不再从类路径中读取配置文件,而是通过参数指定配置文件的位置,它可以获取类路径之外的资源,如“D:\application.xml”
简单说就是一个需要指定具体的xml路径,一个只需要xml文件名称即可。

2.3.7 通过上下文对象获取容器中的对象

package com.bowei.test;

import com.bowei.pojo.Team;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.core.io.FileSystemResource;

/**
 * 使用Spring容器BeanFactory和ApplicationContext创建对象
 */
public class Test1 {
    //1.指定spring配置文件名称
    String springConfig="application.xml";

    /**
     * 方式一:使用BeanFactory容器创建对象,已过时,不推荐,只了解
     *        它是根据 XML 配置文件中的定义装配Bean 的
     */
    @Test
    public void test01(){
        //1、创建spring容器的对象:
        BeanFactory beanFactory = new XmlBeanFactory(new FileSystemResource("I:..src\\main\\resources\\application.xml"));
        //2.根据ID从IOC容器获取对象
        Object team1 = beanFactory.getBean("team1");
    }

    /**
     * 方式二:使用ApplicationContext容器创建对象
     *     1.ClassPathXmlApplicationContext 从类路径中寻找指定XML文件,完成实例化工作  常用
     *     2.FileSystemXmlApplicationContext 指定具体xml路径  只了解
     */
    @Test
    public void test02(){
        //2、创建spring容器的对象:
       ApplicationContext applicationContext = new ClassPathXmlApplicationContext(springConfig);
       //3.根据ID从IOC容器获取对象
        Team team1 = (Team) applicationContext.getBean("team1");
        //4.容器其它api
        int beanDefinitionCount = applicationContext.getBeanDefinitionCount();
        System.out.println("spring容器中对象的个数:"+beanDefinitionCount);
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        System.out.println("spring容器中所有对象名称:");
        for (String name : beanDefinitionNames) {
            System.out.println(name);
        }
        //另一种方式
        //ApplicationContext applicationContext1 = new FileSystemXmlApplicationContext("I:..src\\main\\resources\\application.xml");
    }

}

2.3.8 创建非自定义对象和容器其它api

pox.xml文件中补充 

  上面的测试方法中添加如下内容: 

 

 2.3.9 bean标签的属性

演示
Team 实体类补充如下自定义方法:
application.xml 配置文件添加如下内容:

 

创建ApplicationContext容器后,对象就会被生成,如何去控制对象生成时间,达到现用现创建?
在bean标签中设置对象为单例对象(默认),然后添加lazy-init:true 懒加载属性
或者直接设置对象为多例对象。

2.3 Spring容器创建对象的方式

2.3.1 使用默认的构造方法

2.3.2 使用带参数的构造方法

实体类

 配置文件

 测试

 结果

2.3.3 使用工厂类

实体类还是Team
创建工厂类

 createType.xml中bean标签

测试类

 

2.4 基于XMLDI

2.4.0.什么是DI,与IoC之间的关系

什么是DI

DI—Dependency Injection ,即 依赖注入 ”:Spring容器动态的将某个依赖关系注入到组件之中
依赖注入的目的
是为了提升组件重用的频率; 通过依赖注入机制,我们只需要通过 简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资 源来自何处,由谁实现。
比如A、B、C都是service对象,D是需要的dao对象,对象创建完成后再把dao对象注入到需要dao的service中
IoC与DI之间关系
Ioc DI 是同一个概念的不同角度描述 IoC 是一种思想,概念, DI 是实现它的手段。 Spring 框架使用依赖注入实现IoC
比如工厂方法创建对象过程中就使用了依赖注入,将创建好的factory对象注入到team4中使用。
Spring 容器是一个超级大工厂,除了负责创建  Java 对象外,还管理着这些java Bean之间的依赖关系,Spring 使用 依赖注入 的方式来管理 Bean 之间的依赖关系,使对象之间实现了松耦合。

2.4.1 注入分类

bean 实例在调用无参构造器创建对象后,就要对 bean 对象的属性进行初始化。初始化是由容器自动完成的,称为注入。 注入就是给类中引入你想要的资源然后使用,常见就是给类中属性引入对象进行属性的初始化操作。
2.4.1.1 通过 set 方法
set 注入也叫设值注入是指, 在创建调用者的实例service时通过 setter 方法传入被调用者dao的实例 。这种注入方式简单、直观,因而在Spring 的依赖注入中大量使用。
示例:
创建 TeamDao.java

之前我们在service中是通过new 方式创建dao来在service中使用的,但在框架中是有spring创建管理对象的,这种方法就不可用了。

TeamDao teamDao=new TeamDao();

创建TeamService.java

 

 这需要我们通过spring容器创建TeamService对象时给属性teamDao通过set方式注入一个dao对象

创建配置文件 DI.xml

 此时TeamService中的属性teamDao就是个实例了,可以调用对象

 

2.4.1.2 通过构造方法
构造注入是指, 在创建调用者实例service的同时,通过构造方法完成被调用者dao的实例化。使用构造器设置依赖关系。
需要在service中添加构造器

在DI.xml中配置bean

 测试

 

2.4.1.3. 自动注入
对于引用类型属性的注入,也可不在配置文件中显示的注入。可以在标签中添加  autowire (自动装配)属性,为引用类型属性进行隐式自动注入(默认是不自动注入引用类型属 性)。根据 自动注入判断标准的不同,可以分为两种
        byName:根据名称自动注入
        byType: 根据类型自动注入
1 byName
查找与属性名相同的容器中的对象进行自动注入
2 byType
查找与属性类型相同的容器中对象,但是要求类型相同的bean唯一,否则抛出异常,不知用哪一个进行自动注入
 

 Spring容器通过构造方法和工厂方法这两种创建对象的方式就是使用依赖注入的方式进行bean初始化和资源的引入。基本类型的引入使用value,对象的引入常用ref。

 2.5、基于注解实现IoC--重要

问题一:如何通过注解告知容器,哪些类的对象创建交由spring容器处理?--基于注解实现IoC

1.在类上标识 声明Bean 的注解

2.在配置文件中配置进行包扫描,找到含Component注解的类交由spring创建对象

问题二:spring中创建对象的注解有哪些?/或声明Bean的注解

@Component

@Repository

@Service

@Controller

之前都是在spring配置文件xml中声明bean,由spring容器创建bean对象,并通过依赖注入方式管理bean之间的依赖关系,(注入方式引入资源,初始化属性操作),通过依赖注入实现了IoC,也就是对象的创建管理交由spring容器负责。

使用注解实现IOC,就是通过注解方式实现spring容器创建对象并通过DI管理bean之间的依赖关系。

对于 DI 使用注解,将不再需要在 Spring 配置文件中声明 bean 实例。 Spring 中使用注解,需要在原有Spring 运行环境基础上再做一些改变。

2.5.1 声明Bean的注解 @Component

在类上添加注解 @Component 表示该类创建对象的权限交给 Spring 容器。注解的 value 属性用于指定bean的 id 值, value 可以省略。
@Component 不指定 value 属性, bean id 是类名的首字母小写。

 除此之外,Spring中还提供了其他3个用于创建对象/声明Bean的注解

@Repository : 用于 dao实现类的的注解   [rɪˈpɒzətri]
@Service: 用于 service 实现类的注解
@Controller: 用于 controller 实现类的注解
这三个注解与 @Component 都可以创建对象,但这三个注解还有其他的含义
@Service 创建业务层对象,业务层对象可以加入事务功能。
@Controller 注解创建的对象可以作为处理器接收用户的请求
@Repository @Service @Controller 是对 @Component 注解的细化,子字注解,标注不同层的对象。即持久层对象,业务层对象,控制层对象。
@Component注解常用于实体类等非持久层、业务层和处理层的类上。

2.5.2 包扫描

需要在 Spring 配置文件中 配置组件扫描器 ,用于在指定的基本包中扫描注解。 如果没有包扫描,添加的创建对象的注解不生效

需要告诉注解你在哪些类上加了注解,将哪些对象交给Spring容器,就需要包扫描,扫描指定包中的类上如果加上了@Component注解,这些加了注解的类就交给Spring容器创建。

示例:

1. 标识@Component注解的类

 2.包扫描

配置文件中配置组件扫描器component-scan 注意要添加context命名空间

 测试

多个包的扫描方式:
1 、使用多个 context:component-scan 指定不同的包路径
2 、指定 base-package 的值使用分隔符
3 base-package 是指定到父包名
base-package 的值表是基本包,容器启动会扫描包及其子包中的注解,当然也会扫描到子包下级的子包。所以 base-package 可以指定一个父包就可以。
但不建议使用顶级的父包,扫描的路径比较多,导致容器启动时间变慢。指定到目标包和合适的。也就是注解所在包全路径

属性注入的注解实现

2.5.3 属性注入@Vaule

我们在配置文件中通过bean标签声明对象的方式创建对象时,是通过标签的方式实现属性注入,比如set注入、构造注入、自动注入(byName、byType)。

那么当使用注解声明bean由容器创建对象时,是如何实现属性的注入?(属性的初始化)

当容器启动后,进行包扫描,找到还有@Component注解的类,交由容器调用默认构造方法创建对象,此时对象的属性没有值,可以通过value注解给属性注入值,@Value写在属性是上,它其实是通过set方法进行的属性注入,可以将@value注解写在set方法上。
Team实体类(只截了部分)

 配置文件设置组件扫描器进行包扫描

 测试

 

2.5.4 @Autowired 自动注入 方式一:byType

需要在引用属性上使用注解 @Autowired ,该注解默认使用按类型自动装配 Bean 的方式。使用该注解完成属性注入时,类中无需 setter 。当然,若属性有 setter ,则也可将其加到 setter 上。
这种根据属性类型去获取容器中类型相同的对象注入到属性的方式,当有多个相同类型的对象时就会报出异常,不知道选择哪一个对象。
TeamDao类

 TeamService类

 配置文件中进行包扫描

 测试

 

2.5.5 @Autowired自动注入 方式二:byName

需要在引用属性上联合使用注解 @Autowired @Qualifier @Qualifier 的 value 属性用于指定要匹配的 Bean id 值。类中无需 set 方法,也可加到 set 方法上。
@Autowired 还有一个属性 required ,默认值为 true ,表示当匹配失败后,会终止程序运行。若将其值设置为 false ,则匹配失败,将被忽略,未匹配的属性值为 null

2.5.6 自动注入@Resource

@Autowired 是Spring提供的自动注入的注解

@Resource是jdk中提供的自动注入的注解

@Resource 注解 既可以按名称匹配Bean,也可以按类型匹配 Bean 默认是按名称注入
@Resource 可在属性上,也可在 set 方法上。
1 byType 注入引用类型属性
@Resource 注解若不带任何参数,默认先按名称的方式注入,按名称不能注入 bean ,则会按照类型进行 Bean 的匹配注入。就是先去容器中有没有和属性名teamService相同的对象,有就注入给属性,没有那么和属性类型相同的对象也可以注入进来。
2 byName 注入引用类型属性
@Resource 注解也可以指定要注入对象的名称,此时最好加上type属性声明对象的类型,避免同名对象。

高版本的jdk中无法使用Resource,通过maven引入

 4Spring核心之AOP

4.1.说说你对AOP的理解

AOP Aspect Oriented Programming 的缩写,意思为 面向切面编程 ,是 通过预编译方式和运行期动态代理实现程序功能 的统一维护的一种技术。
 

AOP面向切面编程就是将核心业务代码和服务性代码(日志、权限、事务)拆开编写,编程人员可以各自做自己擅长的部分,程序运行的时候是通过spring工厂自动实现将服务性代码切面的方式加入到核心业务代码中实现完整的功能,实现了程序运行期间对方法进行功能增强。(通过动态代理将核心代码和服务器性代码编织在一起)

开发阶段:关注核心业务和 AOP 代码
运行阶段: spring 框架会在运行的时候将核心业务和 AOP 代码通过动态代理的方式编织在一起
代理方式的选择:目标对象是否实现了接口:有接口就选择 JDK 动态代理;没有就选择 CGLIB 动态代理。
好处:
1 、减少代码的重复,提高开发效率,便于维护。
2 、专注核心业务的开发。

4.2 AOP的实现机制-动态代理

4.2.1 什么是代理模式

代理:自己不做,找人帮你做。
代理模式:在一个原有功能的基础上添加新的功能。
分类:静态代理和动态代理

4.3静态代理

4.3.1 原有方式:核心业务和服务方法都编写在一起

4.3.2 基于类的静态代理

基于类的静态代理是如何实现的?
在被代理类中编写核心业务,代理类要继承被代理类,在代理类中编写服务代码,并 通过super 关键字调用被代理类中核心业务代码。

弊端:一个代理类只能代理一个被代理对象(单继承)

被代理类(编写核心业务)

 代理类(编写服务性代码)

4.3.3 基于接口的静态代理

基于接口的静态代理是如何实现的?

为核心业务类创建一个接口,代理类和被代理类都实现了同一个接口,通过接口暴露被代理的方法,在代理类中通过含参构造方法传进来被代理对象,调用被代理对象中方法完成核心业务。代理帮我们把非核心内容做了,被代理对象还是去干核心的任务。

一个代理类可以代理多个被代理对象,分别执行被代理对象中方法

定义接口声明核心业务方法

被代理类1

 被代理类2

 代理类

 测试:一个代理类代理多个被代理对象

 

 二级代理:将多个服务性业务与核心业务编织在一起

小明(被代理对象)会演戏,他找了经纪人(代理人1)负责他的演戏事宜,小明还想发展演唱业务,所以他的经纪人再去找能负责演唱业务的经纪人(代理人2),最后小明既可以进行演戏业务,又可以进行演唱业务。

多级代理,最后实现的功能越强大。

接口定义核心方法,通过接口暴露被代理的方法

 被代理类

 事务代理类

 日志代理类

 二级代理 测试:

二级代理对象中add方法的核心业务是通过一级代理对象中的add方法完成核心业务,而一级代理对象中的add方法的核心业务是通过被代理对象中的add方法完成的。

4.3.4 提取出切面代码,作为AOP接口

从上面可以看到,代理类中服务业务和核心业务还是混合在一起的,如何将服务业务和核心业务拆开?---提取出切面代码,作为AOP接口。

共有4个位置可以将切面代码编织进入核心业务代码中。

编写AOP切面接口

 编写日志切面

编写事务切面

 现在切面代码已经单独编写好了,要将切面代码编织到核心业务中,这里我们使用的是面向接口的静态代理,被代理类和代理类实现同一个接口,通过接口暴露你要代理的方法,在代理类中传入被代理对象和切面,在代理类中将切面代码和核心业务编织在一起。

接口

 被代理类

 代理类

测试:

 

 

 二级代理:核心业务中既有日志切面又有事务切面,被代理对象被多个代理类层级代理

 

 切面代码和核心业务代码都是分开单独编写,最后通过代理将它们编织在一起,代理的时候传进来代理对象和切面,然后选择合适的位置将切面切入核心业务中。

总结静态代理:
1 )可以做到在不修改目标对象的功能前提下,对目标对象功能扩展。
2 )缺点:
因为代理对象,需要与目标对象(被代理对象)实现一样的接口。所以会有很多代理类,类太多。
一旦接口增加方法,目标对象与代理对象都要维护。

4.4 动态代理

4.4.0.基于JDK/CGLIB的动态代理区别

静态代理:要求代理类存在(需要我们去编写代理类),
动态代理:程序运行的时候,根据要被代理的对象动态生成代理类(不需我们编写)。
类型:
1 、基于 JDK 的动态代理:
        代理对象不需要我们去实现(代理)接口(动态生成代理类时已经指定了接口),但目标对象(被代理对象)需要实现(代理)接口,否则不能用JDK动态代理(被代理类和代理类必须实现同一个接口),如果想要功能扩展,但目标对象没有实现接口,怎样功能扩展?
JDK 的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有
实现接口的类,就可以使用 CGLIB 实现
2 、基于 CGLIB 的动态代理
Cglib 代理,也叫做子类代理。不需要实现代理接口,是在在内存中构建一个子类对象从而实现对目标对象功能的扩展。
CGLIB 是一个强大的高性能的代码生成包,它可以在运行期扩展 Java 类与实现 Java 接口。它广泛的被许多AOP 的框架使用,例如 Spring AOP dynaop ,为他们提供方法的 interception
4.4.1 基于 JDK 的动态代理

就是jdk中给我们提供好的代理类Proxy,该类中提供静态newProxyInstance方法根据要被代理的对象动态生成代理类,生成代理对象,该方法创建代理对象需要指定3个参数:

1.类加载器:借助目标对象的类加载器,因为代理对象是动态生成的,现在没有

2.接口类对象的集合:针对接口的代理,针对哪个接口做代理,一般使用的就是被代理对象的接口。如果我们提供了这样一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。

3.句柄/回调函数,通过重写InvocationHandler接口中invoke方法编写代理规则,向核心业务中切入需要的切面,代理事务、日志等

4.4.1.1 直接编写测试类

被代理对象的接口

 被代理类

 基于JDK的动态代理类--

 

 4.4.1.2 结构化设计--提取切面代码,作为AOP接口

方式一:

将动态代理中使用匿名内部类编写代理规则的部分抽出来编写在类中并实现InvocationHandler

然后提取切面代码,作为AOP接口,将服务性代码以切面的方式切入到核心业务中。

切面接口

 事务切面

 句柄/回调程序--编写代理规则(代理事务、日志等)

 测试:

相对上面而言,我们获取代理对象需要编写的代码就减少了,我们可以再简化,创建一个工厂类专门来获取代理对象。

 方式二: 工厂类继续简化

准备切面接口AOP(见上面): 

准备切面(事务、日志、权限等略)

准备代理接口:通过接口暴露代理的方法

准备被代理类(核心业务)

准备获取动态代理对象的工厂类

 测试:

 

 4.4.2 基于CGLIB的动态代理

Cglib 代理,也叫做子类代理。在内存中构建一个子类对象从而实现对目标对象功能的扩展。

cglib动态代理和jdk动态代理相似,通过Enhancer类中静态方法create进行创建代理对象,在接口MethodInterceptor中重写intercept定义代理规则。

JDK 的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有
实现接口的类,就可以使用 CGLIB 实现。
CGLIB 是一个强大的高性能的代码生成包,它可以在运行期扩展 Java 类与实现 Java 接口。它广泛的被许多AOP 的框架使用,例如 Spring AOP dynaop ,为他们提供方法的 intercept

CGLIB 包的底层是通过使用一个小而快的字节码处理框架 ASM ,来转换字节码并生成新的类。不鼓励直接使用ASM ,因为它要求你必须对 JVM 内部结构包括 class 文件的格式和指令集都很熟悉。

4.4.2.1 直接编写测试类

首先导入cglib依赖

 被代理类(不需要实现接口)

 cglib动态代理实现AOP

4.4.2.2 结构化设计方式--简化代码
切面接口AOP
事务切面
被代理类

 代理工厂类(如果有多个目标对象,写个父接口来接收目标对象,这里只能接收NBAService及其子类的目标对象)

 测试

 

4.5 Spring AOP

4.5.1.说说你对Spring AOP的理解

Spring AOP就是基于cglib动态代理实现的AOP框架。

Spring AOP 实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强
我们先来介绍 AOP 的相关术语:
Target( 目标对象 )
要被增强的对象,一般是业务逻辑类的对象(就是被代理对象,如service)
Proxy (代理)
一个类被 AOP 织入增强后,就产生一个结果代理类。(明星找经纪人,两人强强联合,就是代理类,相当于是编织了服务代码后的service)
Aspect( 切面 )
表示增强的功能,就是一些代码完成的某个功能,非业务功能。是切入点和通知的结合。
Joinpoint( 连接点 )
所谓连接点是指那些被拦截到的点。在 Spring , 这些点指的是方法(一般是类中的业务方法) , 因为Spring只支持方法类型的连接点。(就是被拦截的要被切入切面的业务方法,一个service中业务方法有很多)
Pointcut( 切入点 )
切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。(切入点定义切入的位置)
比如有10个service,每个service中有3个业务方法,这30个业务方法中要拦住哪些方法进行切入,这就是切入点。
被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的
Advice( 通知 / 增强 )
所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。(就是拦截到业务方法后,通知切面切入到核心业务的时间,通知类型不同,切入时间不同)
通知的类型:前置通知 , 后置通知 , 异常通知 , 最终通知 , 环绕通知。
切入点定义切入的位置,通知定义切入的时间。
Weaving( 织入 ).
是指把增强应用到目标对象来创建新的代理对象的过程。 spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入
切面的三个关键因素:
1 、切面的功能 -- 切面能干啥
2 、切面的执行位置 -- 使用 Pointcut 表示切面执行的位置
3 、切面的执行时间 -- 使用 Advice 表示时间,在目标方法之前还是之后执行。

4.5.2 AspectJ AOP 的实现

对于 AOP 这种编程思想,很多框架都进行了实现。 Spring 就是其中之一,可以完成面向切面编程。 AspectJ 也实现了 AOP 的功能,且其实现方式更为简捷而且还支持注解式开发。所以, Spring 又将 AspectJ 的对于 AOP 的实现也引入到了自己的框架中。
Spring 中使用 AOP 开发时,一般使用 AspectJ 的实现方 式
AspectJ 是一个优秀面向切面的框架,它扩展了 Java 语言,提供了强大的切面实现。
4.5.2 .1 AspectJ 的通知类型
AspectJ 中常用的通知有 5种类型:通知定义切入的时间
1. 前置通知
2. 后置通知
3. 环绕通知
4. 异常通知
5. 最终通知
4.5.2.2 AspectJ 的切入点表达式
AspectJ 定义了专门的表达式用于指定切入点。
以上表达式共 4 个部分
execution( 访问权限 方法返回值    方法声明 ( 参数 ) 异常类型 )
切入点表达式要匹配的对象就是目标方法的方法名。所以, execution 表达式中就是方法的签名。
PS: 表达式中黑色文字表示可省略部分,各部分间用空格分开。在其中可以使用以下符号:

4.5.3 Spring中通过注解方式实现AOP

开发阶段:关注核心业务和 AOP 代码
运行阶段: spring 框架会在运行的时候将核心业务和 AOP 代码通过动态代理的方式编织在一起
代理方式的选择:是否实现了接口:有接口就选择 JDK 动态代理;没有就选择 CGLIB 动态代理。
通过注解实现的AOP,所有东西都在底层封装好了,这个底层就是上面的动态代理,动态代理想要理解还要去理解静态代理。
spring如何将核心业务和切面代码编织起来,通知spring哪些是核心业务类,哪些是切面类,哪些切面要切入到哪些业务方法中的哪些位置?
在定义好切面 Aspect 切面 后,需要通知 Spring 容器,让容器生成 目标类 + 切面 的代理对象。这个代理是由容器自动生成的。只需要在 Spring 配置文件中注册一个基于 aspectj 的自动代理生成器,其就会自动扫描到@Aspect 注解找到切面类,并按通知类型与切入点,将其织入,并生成代理。
1 、创建项目引入依赖
引入spring-context spring-AspectJ依赖

 单元测试

maven编译插件(默认1.7)

 

2. 创建配置文件,在<beans>标签中引入aop和context约束

 3.创建核心业务类(目标对象实现了接口)

3.1.编写代理接口,通过接口暴露要代理的方法

 3.2.编写被代理类,编写核心业务

 

 4.编写AOP切面内容

4.1.编写切面接口(可以不用接口,直接用类,不需5个方法都写)

 4.2.编写事务切面类

前置通知

 后置通知

 环绕通知

异常通知

最终通知

 至此,核心业务和AOP已经编写完成。

spring如何将核心业务和切面代码编织起来,通知spring哪些是核心业务类,哪些是切面类,哪些切面要切入到哪些业务方法中的哪些位置?
在定义好切面 Aspect 后,需要通知 Spring 容器,让容器生成 目标类 + 切面 的代理对象。这个代理是由容器自动生成的。只需要在 Spring 配置文件中注册一个基于 aspectj 的自动代理生成器,其就会自动扫描到@Aspect 注解找到切面类,并按通知类型与切入点,将其织入,并生成代理。
5 spring.xml 配置文件中开启包扫描和 注册 aspectj 的自动代理

aop:aspectj-autoproxy 的底层是由 AnnotationAwareAspectJAutoProxyCreator 实现的 , 是基于 AspectJ 的注解适配自动代理生成器。
其工作原理是, aop:aspectj-autoproxy 通过扫描找到 @Aspect 定义的切面类,再由切面类根据切入点找到目标类的目标方法,再由通知类型找到切入的时间点。
测试:

 对上述切面类中切入点表达式进行简化,给方法标注@Pointcut注解用方法表示切入点表达式,这样当修改切入点表达式时就可以统一修改一次就可以

例如:

 

4.5.4 XML方式实现AOP

对象还是容器创建的,还是使用AspectJ自动代理(底层是cglib动态代理),@AspectJ还是要去标识切面类,配置文件中在config标签下通过aop:pointcut标签声明切入点表达式,在aop:aspect标签中声明切面、切面方法以及其对应的通知类型和切入点

 

5Spring整合JDBC

5.1 使用spring-jdbc操作数据库

主要内容 :
学习使用JdbcTemplate API和 如何使用Spring管理 JdbcTemplate
1 、创建项目引入依赖

2 、测试连接

 

5.2. Spring管理JdbcTemplate

spring 整合 jdbc 的时候我们都是直接让 dao 继承 Spring 提供的 JdbcDaoSupport类,该类中提供了 JdbcTemplate模板可用。JdbcDaoSupport类中JdbcTemplate没有值,我们需要去创建一个JdbcTemplate对象,然后创建一个数据源注入到JdbcTempate中,再将JdbcTemplate对象注入到dao中使用,这些对象的创建和依赖注入都是有spring管理的,需要在配置文件中通bean标签声明对象。
spring 的配置文件 application.xml 中需要创建数据源和给 TeamDao 中的 jdbcTemplate 赋值

 

 测试

 

6Spring事务管理

事务原本是数据库中的概念,在 Dao 层。但在实际开发中,一般将事务提升到业务层,即 Service 层。这样做是为了能够使用事务的特性来管理具体的业务

6.1 Spring事务管理API

Spring 的事务管理,主要用到两个事务相关的接口。

6.1.1 事务管理器接口

事务管理器是 PlatformTransactionManager 接口对象。其主要用于完成事务的提交、回滚,及获取事务的状态信息。

PlatformTransactionManager 接口常用的实现类

DataSourceTransactionManager :使用 JDBC MyBatis 进行数据库操作时使用。

Spring 的回滚方式
Spring 事务的默认回滚方式是:发生运行时异常和 error 时回滚,发生受查 ( 编译 ) 异常时提交。不过, 对于受查异常,程序员也可以手工设置其回滚方式。

6.1.2 事务定义接口

事务定义接口 TransactionDefinition 中定义了事务描述相关的三类常量:事务隔离级别、事务传播行为、事务默认超时时限,及对它们的操作

6.1.2.1 事务隔离级别常量
这些常量均是以 isolation_开头。即形如isolation_xxx。
default :采用 DB 默认的事务隔离级别。
MySql 默认为repeatable_read Oracle 默认为read_committed 。
read_uncommitted :读未提交。未解决任何并发问题。
read_committed :读已提交。解决脏读,存在不可重复读与幻读。
repeatable_read :可重复读。解决脏读、不可重复读,存在幻读
serializable :串行化。不存在并发问题
6.1.2.2 事务传播行为常量

所谓事务传播行为是指,处于不同事务中的方法在相互调用时,执行期间事务的维护情况。如, A 事务中的方法 doSome() 调用 B 事务中的方法 doOther() ,在调用执行期间事务的维护情况,就称为事务传播行为。事务传播行为是加在方法上的。

事务传播行为常量都是以  propagation_ 开头,形如propagation_XXX。[prɒpə'ɡeɪʃ(ə)n]

Propagation.required
当前没有事务时,就创建一个新事务;如果当前有事务,就直接加入该事务,比较常用设置
Propagation.supports
支持当前事务,如果当前有事务,就直接加入该事务;当前没有事务的时候,就以非事务方式执行
Propagation.mandatory  [ˈmændətəri]强制的
支持当前事务,如果当前有事务,就直接加入该事务;当前没有事务的时候,就抛出异常
Propagation.requires_new
创建新事务,无论当前是否有事务都会创建新的

PROPAGATION_nested
PROPAGATION_never
PROPAGATION_not_support
6.1.2.3 默认事务超时时限
常量 timeout_default  定义了事务底层默认的超时时限, sql 语句的执行时长。
注意,事务的超时时限起作用的条件比较多,且超时的时间计算点较复杂。所以,该值一般就使用默认值即可。(在sql执行时间很长的时候使用)

6.2 声明式事务控制

Spring 提供的对事务的管理,就叫做声明式事务管理。
如果用户需要使用 spring 的声明式事务管理,在配置文件中配置即可:不想使用的时候直接移除配置。 这种方式实现了对事务控制的最大程度的解耦。
声明式事务管理,核心实现就是基于 AOP,以切面方式加入事务 。(AOP核心实现是动态代理)
Spring 中提供了对事务的管理。开发者只需要按照 spring 的方式去做就行。
事务必须在service层统一控制。
事务的粗细粒度
细粒度:对方法中的某几行的代码进行开启提交回滚;
粗粒度:对整个方法进行开启提交回滚;
Spring 中的 aop 只能对方法进行拦截,所有我们也只能对方法进行事务的控制。
增删改是一定要开启事务的,查询的话, 如果只有单条的查询语句,可以省略事务;如果一次执行的是多条查询语句,例如统计结果、报表查询。必须 开启事务

6.3 基于注解的事务

1.在配置文件中添加tx约束,然后通过DataSourceTransactionManager类创建事务管理器transactionManager,并向其注入数据源dataSource, 然后通过annotation-driven 标签去启动事务的注解。

2.在方法上添加事务注解@Transactional,该注解中常用属性

readOnly: 是否只读
rollbackFor={Exception.class} : 遇到什么异常会回滚
propagation事务的传播,常用 Propagation.required
isolation=Isolation.DEFAULT :事务的隔离级别:默认是数据库的隔离级别

基于注解的事务

在xml中添加tx约束,启动事务注解

 

 在方法上添加Transactional注解

dao 中insert

测试

6.4 基于XML的事务

添加依赖

xml中添加aop约束和tx约束、声明事务通知、切面方式切入

猜你喜欢

转载自blog.csdn.net/qq_40454863/article/details/119304336