栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring-王鹤

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring-王鹤

第1章 Spring 概述 1.1 Spring 框架是什么

Spring 是于 2003 年兴起的一个轻量级的 Java 开发框架,它是为了解决企业应用开发的复杂性而创建的。Spring 的核心是控制反转(IoC)和面向切面编程(AOP)。Spring 是可以在 Java SE/EE 中使用的轻量级开源框架

Spring 的主要作用就是为代码“解耦”,降低代码间的耦合度。就是让对象和对象(模块和模块)之间关系不是使用代码关联,而是通过配置来说明。即在 Spring 中说明对象(模块)的关系。

Spring 根据代码的功能特点,使用 IoC 降低业务对象之间耦合度。IoC 使得主业务在相互调用过程中,不用再自己维护关系了,即不用再自己创建要使用的对象了。而是由 Spring容器统一管理,自动“注入”,注入即赋值。 而 AOP 使得系统级服务得到了最大复用,且不用再由程序员手工将系统级服务“混杂”到主业务逻辑中了,而是由 Spring 容器统一完成“织入”。

依赖:classa中使用classb的属性或者方法, 叫做classa依赖classb

1.2 Spring 优点?

Spring 是一个框架,是一个半成品的软件。有 20 个模块组成。它是一个容器管理对象,容器是装东西的,Spring 容器不装文本,数字。装的是对象。Spring 是存储对象的容器。

(1) 轻量
Spring 框架使用的 jar 都比较小,一般在 1M 以下或者几百 kb。Spring 核心功能的所需的 jar 总共在 3M 左右。
Spring 框架运行占用的资源少,运行效率高。不依赖其他 jar

(2) 针对接口编程,解耦合
Spring 提供了 Ioc 控制反转,由容器管理对象,对象的依赖关系。原来在程序代码中的对象创建方式,现在由容器完成。对象之间的依赖解耦合。

(3) AOP 编程的支持
通过 Spring 提供的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松应付
在 Spring 中,开发人员可以从繁杂的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量。

(4) 方便集成各种优秀框架
Spring 不排斥各种优秀的开源框架,相反 Spring 可以降低各种框架的使用难度,Spring提供了对各种优秀框架(如Struts,Hibernate、MyBatis)等的直接支持。简化框架的使用。
Spring 像插线板一样,其他框架是插头,可以容易的组合到一起。需要使用哪个框架,就把这个插头放入插线板。不需要可以轻易的移除。

1.3 Spring 体系结构

Spring 由 20 多个模块组成,它们可以分为数据访问/集成(Data Access/Integration)、Web、面向切面编程(AOP, Aspects)、提供JVM的代理(Instrumentation)、消息发送(Messaging)、核心容器(Core Container)和测试(Test)。

第2章 IoC 控制反转

控制反转(IoC,Inversion of Control),是一个概念,是一种思想。指将传统上由程序代码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。控制反转就是对对象控制权的转移,从程序代码本身反转到了外部容器。通过容器实现对象的创建,属性赋值,依赖的管理。
IoC 是一个概念,是一种思想,其实现方式多种多样。当前比较流行的实现方式是依赖注入。应用广泛。
依赖:classA 类中含有 classB 的实例,在 classA 中调用 classB 的方法完成功能,即 classA对 classB 有依赖。

控制: 创建对象,对象的属性赋值,对象之间的关系管理。
反转: 把原来的开发人员管理,创建对象的权限转移给代码之外的容器实现。 由容器代替开发人员管理对象。创建对象,给属性赋值。

正转:由开发人员在代码中,使用new 构造方法创建对象, 开发人员主动管理对象。

   public static void main(String args[]){
        Student student = new Student(); // 在代码中, 创建对象。--正转。
	 }

容器:是一个服务器软件, 一个框架(spring)
为什么要使用 ioc : 目的就是减少对代码的改动, 也能实现不同的功能。 实现解耦合。
java中创建对象有哪些方式:

  1. 构造方法 , new Student()
  2. 反射
  3. 序列化
  4. 克隆
  5. ioc :容器创建对象
  6. 动态代理

依赖注入:DI(Dependency Injection),程序代码不做定位查询,这些工作由容器自行完成。

依赖注入 DI 是指程序运行过程中,若需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部容器,由外部容器创建后传递给程序。

Spring 的依赖注入对调用者与被调用者几乎没有任何要求,完全支持对象之间依赖关系的管理。

Spring 框架使用依赖注入(DI)实现 IoC。
Spring 容器是一个超级大工厂,负责创建、管理所有的 Java 对象,这些 Java 对象被称为 Bean。Spring 容器管理着容器中 Bean 之间的依赖关系,Spring 使用“依赖注入”的方式来管理 Bean 之间的依赖关系。使用 IoC 实现对象之间的解耦和。

IoC的技术实现 ,
DI 是ioc的技术实现,

  • DI(Dependency Injection) :依赖注入, 只需要在程序中提供要使用的对象名称就可以, 至于对象如何在容器中创建,赋值,查找都由容器内部实现。
  • spring是使用的di实现了ioc的功能, spring底层创建对象,使用的是反射机制。
  • spring是一个容器,管理对象,给属性赋值, 底层是反射创建对象。
2.1Spring 配置文件

在 src/main/resources/目录现创建一个 xml 文件,文件名可以随意,但 Spring 建议的名称为 applicationContext.xml。

spring 配置中需要加入约束文件才能正常使用,约束文件是 xsd 扩展名。

:
用于定义一个实例对象。一个实例对应一个 bean 元素。
id:该属性是 Bean 实例的唯一标识,程序通过 id 属性访问 Bean,Bean 与 Bean 间的依赖关系也是通过 id 属性关联的。
class:指定该 Bean **所属的类**,注意这里只能是类,不能是接口。
2.2定义测试类

junit : 单元测试, 一个工具类库,做测试方法使用的。 单元:指定的是方法, 一个类中有很多方法,一个方法称为单元。

使用单元测试
1.需要加入junit依赖。

   
      junit
      junit
      4.11
      test
    

2.创建测试作用的类:叫做测试类
src/test/java目录中创建类

3.创建测试方法
1)public 方法
2)没有返回值 void
3)方法名称自定义,建议名称是test + 你要测试方法名称
4)方法没有参数
5)方法的上面加入 @Test ,这样的方法是可以单独执行的。 不用使用main方法。

测试

@Test
public void SomeServicebean()
{
    ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
    SomeService someService = context.getBean("SomeService", SomeService.class);
    someService.doSome();
}
//使用Spring提供的方法,获取容器中定义的对象数量
int nums=context.getBeanDefinitionCount();
System.out.println("容器中对象数量为:"+nums);
//使用Spring提供的方法,获取容器中定义的对象名称
String [] names= context.getBeanDefinitionNames();
for (String name:names) {
    System.out.println(name);
}
  • Spring默认创建对象的时间:在创建Spring容器的时候,会创建配置文件中的所有对象。
  • Spring创建对象:调用的是类的无参构造方法。
  • ApplicationContext就表示Spring的容器
  • ClassPathXmlApplicationContext表示从类路径中加载了Spring的配置文件

spring-conetxt 和 spring-webmvc是spring中的两个模块
spring-context:是ioc功能的,创建对象的。 spring-webmvc做web开发使用的, 是servlet的升级。
spring-webmvc中也会用到spring-context中创 建对象的功能的。

2.3 基于 XML 的 DI

bean 实例在调用无参构造器创建对象后,就要对 bean 对象的属性进行初始化。初始化是由容器自动完成的,称为注入。

根据注入方式的不同,常用的有两类:set 注入、构造注入。

(1) set 注入(掌握)
set 注入也叫设值注入是指,通过 setter 方法传入被调用者的实例。这种注入方式简单、直观,因而在 Spring 的依赖注入中大量使用。需要有setter方法,没有会报错。

A、简单类型

1.set注入(设置注入) :Spring调用类的set方法
1)简单类型的set注入
 
    
    一个property只能给一个属性赋值
    
 

B、 引用类型
当指定 bean 的某属性值为另一 bean 的实例时,通过 ref 指定它们间的引用关系。ref的值必须为某 bean 的 id 值。
对于其它 Bean 对象的引用,使用标签的 ref 属性


    
    



    
    
    
    

(2) 构造注入(理解)

构造注入是指,在构造调用者实例的同时,完成被调用者的实例化。即,使用构造器设置依赖关系。

    
        
        
    

    

        
        
        
        
    

标签中用于指定参数的属性有:
name:指定参数名称。
index:指明该参数对应着构造器的第几个参数,从 0 开始。不过,该属性不要也行,但要注意,若参数类型相同,或之间有包含关系,则需要保证赋值顺序要与构造器中的参数顺序一致。

2.3.2 引用类型属性自动注入

对于引用类型属性的注入,也可不在配置文件中显示的注入。可以通过为标签设置 autowire 属性值,为引用类型属性进行隐式自动注入(默认是不自动注入引用类型属性)。根据自动注入判断标准的不同,可以分为两种:
byName:根据名称自动注入
byType: 根据类型自动注入

(1) byName 方式自动注入

当配置文件中被调用者 bean 的 id 值与代码中调用者 bean 类的属性名相同时,可使用byName 方式,让容器自动将被调用者 bean 注入给调用者 bean。容器是通过调用者的 bean类的属性名与配置文件的被调用者 bean 的 id 进行比较而实现自动注入的。

(2) byType 方式自动注入

使用 byType 方式自动注入,要求:配置文件中被调用者 bean 的 class 属性指定的类,要与代码中调用者 bean 类的某引用类型属性类型同源。即要么相同,要么有 is-a 关系(子类,或是实现类)。但这样的同源的被调用 bean 只能有一个。多于一个,容器就不知该匹配哪一个了。

2.3.3 为应用指定多个 Spring 配置文件

在实际应用里,随着应用规模的增加,系统中 Bean 数量也大量增加,导致配置文件变得非常庞大、臃肿。为了避免这种情况的产生,提高配置文件的可读性与可维护性,可以将Spring 配置文件分解成多个配置文件.

包含关系的配置文件:
多个配置文件中有一个总文件,总配置文件将各其它子文件通过引入。在 Java代码中只需要使用总配置文件对容器进行初始化即可。

多个配置优势
1.每个文件的大小比一个文件要小很多。效率高
2.避免多人竞争带来的冲突。

如果你的项目有多个模块(相关的功能在一起) ,一个模块一个配置文件。 学生考勤模块一个配置文件, 张三 学生成绩一个配置文件,
李四

多文件的分配方式:

  1. 按功能模块,一个模块一个配置文件
  2. 按类的功能,数据库相关的配置一个文件配置文件, 做事务的功能一个配置文件, 做service功能的一个配置文件等
关键字: classpath表示类路径 (class文件所在的目录) 在spring的配置文件中要指定其他文件的位置,需要指定classpath,告诉spring到哪里加载读取。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/fd29fca96e424f45ab4b8bb1c05650b8.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6a2C5bCP54yr,size_20,color_FFFFFF,t_70,g_se,x_16) 2.4 基于注解的 DI

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

context:annotation-config
是用于激活那些已经在spring容器里注册过的bean(无论是通过xml的方式还是通过packagesanning的方式)上面的注解。(激活@Resource和@Autowired注解)

context:component-scan除了具有context:annotation-config的功能之外,context:component-scan还可以在指定的package下扫描以及注册javabean
。(激活@Resource和@Autowired注解,同时可以配置扫描的包以激活@Service、@Controller等注解)

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

2.4.1 定义 Bean 的注解@Component(掌握)

需要在类上使用注解@Component,该注解的 value 属性用于指定该 bean 的 id 值。
另外,Spring 还提供了 3 个创建对象的注解:

@Repository 用于对 DAO 实现类进行注解
@Service 用于对 Service 实现类进行注解
@Controller 用于对 Controller 实现类进行注解
这三个注解与@Component 都可以创建对象,但这三个注解还有其他的含义,@Service创建业务层对象,业务层对象可以加入事务功能,@Controller 注解创建的对象可以作为处理器接收用户的请求。
@Repository,@Service,@Controller 是对@Component 注解的细化,标注不同层的对象。即持久层对象,业务层对象,控制层对象。

@Component 不指定 value 属性,bean 的 id 是类名的首字母小写。

2.4.2 简单类型属性注入@Value(掌握)

需要在属性上使用注解@Value,该注解的 value 属性用于指定要注入的值。
使用该注解完成属性注入时,类中无需 setter。当然,若属性有 setter,则也可将其加到 setter 上。

2.4.3 byType 自动注入@Autowired(掌握)

需要在引用属性上使用注解@Autowired,该注解默认使用按类型自动装配 Bean 的方式。
使用该注解完成属性注入时,类中无需 setter。当然,若属性有 setter,则也可将其加到 setter 上。

2.4.4 byName 自动注入@Autowired 与@Qualifier(掌握)

需要在引用属性上联合使用注解@Autowired 与@Qualifier。@Qualifier 的 value 属性用于指定要匹配的 Bean 的 id 值。类中无需 set 方法,也可加到 set 方法上。

@Autowired 还有一个属性 required,默认值为 true,表示当匹配失败后,会终止程序运行。若将其值设置为 false,则匹配失败,将被忽略,未匹配的属性值为 null。

2.4.5 JDK 注解@Resource 自动注入(掌握)

Spring提供了对 jdk中@Resource注解的支持。@Resource 注解既可以按名称匹配Bean,也可以按类型匹配 Bean。默认是按名称注入。使用该注解,要求 JDK 必须是 6 及以上版本。@Resource 可在属性上,也可在 set 方法上。

(1) byType 注入引用类型属性
@Resource 注解若不带任何参数,采用默认按名称的方式注入,按名称不能注入 bean,则会按照类型进行 Bean 的匹配注入。

(2) byName 注入引用类型属性
@Resource 注解指定其 name 属性,则 name 的值即为按照名称进行匹配的 Bean 的 id。

2.4.6 注解与 XML 的对比

注解优点是:
⚫ 方便
⚫ 直观
⚫ 高效(代码少,没有配置文件的书写那么复杂)。
其弊端也显而易见:以硬编码的方式写入到 Java 代码中,修改是需要重新编译代码的。
XML 方式优点是:
⚫ 配置和代码是分离的
⚫ 在 xml 中做修改,无需编译代码,只需重启服务器即可将新的配置加载。
xml 的缺点是:编写麻烦,效率低,大型项目过于复杂。

第3章AOP 面向切面编程 3.1不使用 AOP 的开发方式(理解)

先定义好接口与一个实现类,该实现类中除了要实现接口中的方法外,还要再写两个非业务方法。非业务方法也称为交叉业务逻辑:


当然,也可以有另一种解决方案:将这些交叉业务逻辑代码放到专门的工具类或处理类中,由主业务逻辑调用

以上的解决方案,还是存在弊端:交叉业务与主业务深度耦合在一起。当交叉业务逻辑较多时,在主业务代码中会出现大量的交叉业务逻辑代码调用语句,大大影响了主业务逻辑的可读性,降低了代码的可维护性,同时也增加了开发难度。
所以,可以采用动态代理方式。在不修改主业务逻辑的前提下,扩展和增强其功能。

3.2AOP 概述

AOP(Aspect Orient Programming)面向切面编程
Aspect: 切面,给你的目标类增加的功能,就是切面。 像上面用的日志,事务都是切面。
切面的特点: 一般都是非业务方法,独立使用的。
Orient:面向, 对着。
Programming:编程

oop: 面向对象编程

怎么理解面向切面编程 ?
1)需要在分析项目功能时,找出切面。
2)合理的安排切面的执行时间(在目标方法前, 还是目标方法后)
3)合理的安全切面执行的位置,在哪个类,哪个方法增加增强功能

术语:
1)Aspect:切面,表示增强的功能, 就是一堆代码,完成某个一个功能。非业务功能,
常见的切面功能有日志, 事务, 统计信息, 参数检查, 权限验证。

2)JoinPoint:连接点 ,连接业务方法和切面的位置。 就某类中的业务方法
3)Pointcut : 切入点 ,指多个连接点方法的集合。多个方法
4)目标对象: 给哪个类的方法增加功能, 这个类就是目标对象
5)Advice:通知,通知表示切面功能执行的时间。

说一个切面有三个关键的要素:
1)切面的功能代码,切面干什么
2)切面的执行位置,使用Pointcut表示切面执行的位置
3)切面的执行时间,使用Advice表示时间,在目标方法之前,还是目标方法之后。

5.aop的实现
aop是一个规范,是动态的一个规范化,一个标准
aop的技术实现框架:
1.spring:spring在内部实现了aop规范,能做aop的工作。
spring主要在事务处理时使用aop。
我们项目开发中很少使用spring的aop实现。 因为spring的aop比较笨重。

2.aspectJ: 一个开源的专门做aop的框架。spring框架中集成了aspectj框架,通过spring就能使用aspectj的功能。
aspectJ框架实现aop有两种方式:
1.使用xml的配置文件 : 配置全局事务
2.使用注解,我们在项目中要做aop功能,一般都使用注解, aspectj有5个注解。

3.3AOP 简介

AOP(Aspect Orient Programming),面向切面编程。面向切面编程是从动态角度考虑程序运行过程。
AOP 底层,就是采用动态代理模式实现的。采用了两种代理:JDK 的动态代理,与 CGLIB的动态代理。

面向切面编程,就是将交叉业务逻辑封装成切面,利用 AOP 容器的功能将切面织入到主业务逻辑中。所谓交叉业务逻辑是指,通用的、与主业务逻辑无关的代码,如安全检查、事务、日志、缓存等。

若不使用 AOP,则会出现代码纠缠,即交叉业务逻辑与主业务逻辑混合在一起。这样,会使主业务逻辑变的混杂不清。

例如,转账,在真正转账业务逻辑前后,需要权限控制、日志记录、加载事务、结束事务等交叉业务逻辑,而这些业务逻辑与主业务逻辑间并无直接关系。但,它们的代码量所占比重能达到总代码量的一半甚至还多。它们的存在,不仅产生了大量的“冗余”代码,还大大干扰了主业务逻辑—转账。

3.4面向切面编程对有什么好处?

1.减少重复;
2.专注业务;

3.5AOP 编程术语(掌握)

(1) 切面(Aspect)
切面泛指交叉业务逻辑。上例中的事务处理、日志处理就可以理解为切面。常用的切面是通知(Advice)。实际就是对主业务逻辑的一种增强。
(2) 连接点(JoinPoint)
连接点指可以被切面织入的具体方法。通常业务接口中的方法均为连接点。
(3) 切入点(Pointcut)
切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。被标记为 final 的方法是不能作为连接点与切入点的。因为 final 的是不能被修改的,不能被增强的。
(4) 目标对象(Target)
目 标 对 象 指 将 要 被 增 强 的 对 象 。 即 包 含 主 业 务 逻 辑 的 类 的 对 象 。 上 例 中 的StudentServiceImpl 的对象若被增强,则该类称为目标类,该类对象称为目标对象。当然,不被增强,也就无所谓目标不目标了。
(5) 通知(Advice)
通知表示切面的执行时间,Advice 也叫增强。上例中的 MyInvocationHandler 就可以理解为是一种通知。换个角度来说,通知定义了增强代码切入到目标代码的时间点,是目标方
法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。切入点定义切入的位置,通知定义切入的时间。

3.6AspectJ 对 AOP 的实现(掌握)

对于 AOP 这种编程思想,很多框架都进行了实现。Spring 就是其中之一,可以完成面向切面编程。然而,AspectJ 也实现了 AOP 的功能,且其实现方式更为简捷,使用更为方便,而且还支持注解式开发。所以,Spring 又将 AspectJ 的对于 AOP 的实现也引入到了自己的框架中。
在 Spring 中使用 AOP 开发时,一般使用 AspectJ 的实现方式。

3.6.1 AspectJ 的通知类型(理解)

AspectJ 中常用的通知有五种类型:
(1)前置通知
(2)后置通知
(3)环绕通知
(4)异常通知
(5)最终通知

3.6.2 AspectJ 的切入点表达式(掌握)

AspectJ 定义了专门的表达式用于指定切入点。表达式的原型是:

execution(modifiers-pattern?
ret-type-pattern
declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)
解释:
modifiers-pattern] 访问权限类型
ret-type-pattern 返回值类型
declaring-type-pattern 包名类名
name-pattern(param-pattern) 方法名(参数类型和参数个数)
throws-pattern 抛出异常类型
?表示可选的部分

以上表达式共 4 个部分。
execution(访问权限 方法返回值 方法声明(参数) 异常类型)

3.6.3 AspectJ 的开发环境(掌握)
(1) maven 依赖

junit
junit
4.11
test


org.springframework
spring-context
5.2.5.RELEASE


org.springframework
spring-aspects
5.2.5.RELEASE

(2) 引入 AOP 约束
在 AspectJ 实现 AOP 时,要引入 AOP 的约束。配置文件中使用的 AOP 约束中的标签,均是 AspectJ 框架使用的,而非 Spring 框架本身在实现 AOP 时使用的。AspectJ 对于 AOP 的实现有注解和配置文件两种方式,常用是注解方式。




    
    
    

    
    

    
    


    
    

3.6.4 AspectJ 基于注解的 AOP 实现(掌握)

AspectJ 提供了以注解方式对于 AOP 的实现。

(1) 实现步骤
A、Step1:定义业务接口与实现类

public interface SomeService {
    void doSome();
}
public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome() {
        System.out.println("执行了业务方法doSome");
    }
}

B、 Step2:定义切面类
类中定义了若干普通方法,将作为不同的通知方法,用来增强功能。

public class Myaspect {
    


    
    @Before(value = "execution(public void com.link.service.impl.SomeServiceImpl.doSome())")
    public void myBefore(){
        //就是你切面要执行的功能代码
        System.out.println("前置通知, 切面功能:在目标方法之前输出执行时间:"+ new Date());
    }

C、 Step3:声明目标对象切面类对象


    

    

D、Step4:注册 AspectJ 的自动代理

在定义好切面 Aspect 后,需要通知 Spring 容器,让容器生成“目标类+ 切面”的代理对象。这个代理是由容器自动生成的。只需要在 Spring 配置文件中注册一个基于 aspectj 的自动代理生成器,其就会自动扫描到@Aspect 注解,并按通知类型与切入点,将其织入,并生成代理。


测试

    @Test
    public void test01()
    {
        ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        SomeServiceImpl serviceImpl = (SomeServiceImpl) context.getBean("someServiceImpl");
        serviceImpl.doSome();
    }


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

(2) [掌握]@Before 前置通知-方法有 JoinPoint 参数
在目标方法执行之前执行。被注解为前置通知的方法,可以包含一个 JoinPoint 类型参数。该类型的对象本身就是切入点表达式。通过该参数,可获取切入点表达式、方法签名、目标对象等。不光前置通知的方法,可以包含一个 JoinPoint 类型参数,所有的通知方法均可包含该参数。

    
    @Before(value = "execution(void *..SomeServiceImpl.doSome(String,Integer))")
    public void myBefore(JoinPoint jp){
        //获取方法的完整定义
        System.out.println("方法的签名(定义)="+jp.getSignature());
        System.out.println("方法的名称="+jp.getSignature().getName());
        //获取方法的实参
        Object args [] = jp.getArgs();
        for (Object arg:args){
            System.out.println("参数="+arg);
        }
        //就是你切面要执行的功能代码
        System.out.println("2=====前置通知, 切面功能:在目标方法之前输出执行时间:"+ new Date());
    }

(3) [掌握]@AfterReturning 后置通知-注解有 returning 属性
在目标方法执行之后执行。由于是目标方法之后执行,所以可以获取到目标方法的返回值。该注解的 returning 属性就是用于指定接收方法返回值的变量名的。所以,被注解为后置通知的方法,除了可以包含 JoinPoint 参数外,还可以包含用于接收返回值的变量。该变量最好为 Object 类型,因为目标方法的返回值可能是任何类型。

    

    
    @AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))",
                    returning = "res")
    public void myAfterReturing(  JoinPoint jp  ,Object res ){
        // Object res:是目标方法执行后的返回值,根据返回值做你的切面的功能处理
        System.out.println("后置通知:方法的定义"+ jp.getSignature());
        System.out.println("后置通知:在目标方法之后执行的,获取的返回值是:"+res);
        if(res.equals("abcd")){
            //做一些功能
        } else{
            //做其它功能
        }
        //修改目标方法的返回值, 看一下是否会影响 最后的方法调用结果
        if( res != null){
            res = "Hello Aspectj";
        }
    //作业
    @AfterReturning(value = "execution(* *..SomeServiceImpl.doOther2(..))",
            returning = "res")
    public void myAfterReturing2(Student res){
        // Object res:是目标方法执行后的返回值,根据返回值做你的切面的功能处理
        System.out.println("后置通知:在目标方法之后执行的,获取的返回值是:"+res);
        res.name="link2";
        res.age=24;
        //修改目标方法的返回值, 看一下是否会影响 最后的方法调用结果
        //如果修改了res的内容,属性值等,是不是会影响最后的调用结果呢

    }

java中方法参数传递方式是按值传递。
如果参数是基本类型,传递的是基本类型的字面量值的拷贝。
如果参数是引用类型,传递的是该参量所引用的对象在堆中地址值的拷贝。
String类型的数据虽然是引用类型,但是如果进行字符串拼接是在方法区内生成一个新的字符串对象,而不是在原来的字符串上修改,类似于基本数据类型的值传递。类似这种的还有基本数据类型以及他们的高级封装类,因为他们本身没有提供修改的方法,所以每次操作都是生成一个新的对象。它们都是immutable类型
所以通过后置通知获得目标方法的返回值不是immutable类型类型的话是可以修改的。
Student是可以通过后置通知进行修改的。因为这是引用传递(其实本质还是值传递,不过传的值是地址),引用传递是指向同一个对象,所以修改的话也会修改传入的对象。

(4)@Around 环绕通知-增强方法ProceedingJoinPoint参数
在目标方法执行之前之后执行。被注解为环绕增强的方法要有返回值,Object 类型。并且方法可以包含一个ProceedingJoinPoint 类型的参数。接口 ProceedingJoinPoint 其有一个proceed()方法,用于执行目标方法。若目标方法有返回值,则该方法的返回值就是目标方法的返回值。最后,环绕增强方法将其返回值返回。该增强方法实际是拦截了目标方法的执行。接口增加方法:

 

    
    @Around(value = "execution(* *..SomeServiceImpl.doFirst(..))")
    public Object myAround(ProceedingJoinPoint pjp) throws Throwable {

        String name = "";
        //获取第一个参数值
        Object args [] = pjp.getArgs();
        if( args!= null && args.length > 1){
              Object arg=  args[0];
              name =(String)arg;
        }

        //实现环绕通知
        Object result = null;
        System.out.println("环绕通知:在目标方法之前,输出时间:"+ new Date());
        //1.目标方法调用
        if( "zhangsan".equals(name)){
            //符合条件,调用目标方法
            result = pjp.proceed(); //method.invoke(); Object result = doFirst();

        }

        System.out.println("环绕通知:在目标方法之后,提交事务");
        //2.在目标方法的前或者后加入功能

        //修改目标方法的执行结果, 影响方法最后的调用结果
        if( result != null){
              result = "Hello AspectJ AOP";
        }

        //返回目标方法的执行结果
        return result;
    }

(5) [了解]@AfterThrowing 异常通知-注解中有 throwing 属性

在目标方法抛出异常后执行。该注解的 throwing 属性用于指定所发生的异常类对象。当然,被注解为异常通知的方法可以包含一个参数 Throwable,参数名称为 throwing 指定的名称,表示发生的异常对象。

 

    
    @AfterThrowing(value = "execution(* *..SomeServiceImpl.doSecond(..))",
            throwing = "ex")
    public void myAfterThrowing(Exception ex) {
        System.out.println("异常通知:方法发生异常时,执行:"+ex.getMessage());
        //发送邮件,短信,通知开发人员
    }

(6) [了解]@After 最终通知

无论目标方法是否抛出异常,该增强均会被执行。

 

    
    @After(value = "execution(* *..SomeServiceImpl.doThird(..))")
    public  void  myAfter(){
        System.out.println("执行最终通知,总是会被执行的代码");
        //一般做资源清除工作的。
     }

(7) @Pointcut 定义切入点
当较多的通知增强方法使用相同的 execution 切入点表达式时,编写、维护均较为麻烦。AspectJ 提供了@Pointcut 注解,用于定义 execution 切入点表达式。其用法是,将@Pointcut 注解在一个方法之上,以后所有的 execution 的 value 属性值均可使用该方法名作为切入点。代表的就是@Pointcut 定义的切入点。这个使用@Pointcut 注解的方法一般使用 private 的标识方法,即没有实际作用的方法。

    @After(value = "mypt()")
    public  void  myAfter(){
        System.out.println("执行最终通知,总是会被执行的代码");
        //一般做资源清除工作的。
     }

    @Before(value = "mypt()")
    public  void  myBefore(){
        System.out.println("前置通知,在目标方法之前先执行的");
    }

    
    @Pointcut(value = "execution(* *..SomeServiceImpl.doThird(..))" )
    private void mypt(){
        //无需代码,
    }
第4章Spring 集成 MyBatis

将 MyBatis 与 Spring 进行整合,主要解决的问题就是将SqlSessionFactory 对象交由 Spring来管理。所以只需要将SqlSessionFactory 的对象生成器 SqlSessionFactoryBean 注册在 Spring 容器中,再将其注入给 Dao 的实现类即可完成整合。实现 Spring 与 MyBatis 的整合常用的方式:扫描的 Mapper 动态代理,Spring 像插线板一样,mybatis 框架是插头,可以容易的组合到一起。插线板 spring 插上 mybatis,两个框架就是一个整体。

步骤:
1.新建maven项目
2.加入maven的依赖
  1)spring依赖
  2)mybatis依赖
  3)mysql驱动
  4)spring的事务的依赖
  5)mybatis和spring集成的依赖: mybatis官方体用的,用来在spring项目中创建mybatis
     的SqlSesissonFactory,dao对象的
3.创建实体类
4.创建dao接口和mapper文件
5.创建mybatis主配置文件
6.创建Service接口和实现类,属性是dao。
7.创建spring的配置文件:声明mybatis的对象交给spring创建
 1)数据源DataSource
 2)SqlSessionFactory
 3) Dao对象
 4)声明自定义的service

8.创建测试类,获取Service对象,通过service调用dao完成数据库的访问

定义实体类 Student

public class Student {
    //属性名和列名一样。
    private Integer id;
    private String name;
    private Integer age;

定义 StudentDao 接口

public interface StudentDao {
    int insertStudent(Student student);
    List selectStudents();
}

定义映射文件 mapper



    
        insert into student values(#{id},#{name},#{age})
    
    
        select * from goods where id=#{goodsId}
    



    
        insert into sale(gid,nums) values (#{gid},#{nums})
    

Step5:定义异常类
定义 service 层可能会抛出的异常类 NotEnoughException

public class NotEnoughException extends RuntimeException{
    public NotEnoughException(){
        super();
    }
    public NotEnoughException(String msg)
    {
        super(msg);
    }

}

Step6:定义 Service 接口

public interface BuyGoodsService {
    public void buy(Integer goodsId,Integer amount);

}

Step7:定义 service 的实现类

public class BuyGoodsServiceImpl implements BuyGoodsService{
    private GoodsDao goodsDao;
    private SaleDao saleDao;

    public void setGoodsDao(GoodsDao goodsDao) {
        this.goodsDao = goodsDao;
    }

    public void setSaleDao(SaleDao saleDao) {
        this.saleDao = saleDao;
    }

    @Override
    public void buy(Integer goodsId, Integer amount) {
        Sale sale=new Sale();
        sale.setGid(goodsId);
        sale.setNums(amount);
        saleDao.insertSale(sale);
        Goods goods = goodsDao.selectGoods(goodsId);
        if(goods==null)
        {
            throw new NullPointerException("无此商品");
        }
        if(goods.getAmount() 

Step8:修改 Spring 配置文件内容

    
    
    
        
        
        
        
        
        
    

    
    
        
        
        
        
    

    
    
        
        
        
        
    

    
    
        
        
    


Step9:定义测试类

    @Test
    public void test01()
    {
        ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        BuyGoodsService buyGoodsService = context.getBean("buyGoodsService", BuyGoodsService.class);
        buyGoodsService.buy(10010,200);
    }
5.4使用 Spring 的事务注解管理事务(掌握)

通过@Transactional 注解方式,可将事务织入到相应 public 方法中,实现事务管理。

@Transactional 的所有可选属性如下所示:
propagation:用于设置事务传播属性。该属性类型为 Propagation 枚举,默认值为Propagation.REQUIRED。
isolation:用于设置事务的隔离级别。该属性类型为 Isolation 枚举,默认值为Isolation.DEFAULT。
readOnly:用于设置该方法对数据库的操作是否是只读的。该属性为 boolean,默认值为 false。
timeout:用于设置本操作与数据库连接的超时时限。单位为秒,类型为 int,默认值为-1,即没有时限。
rollbackFor:指定需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
rollbackForClassName:指定需要回滚的异常类类名。类型为 String[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
noRollbackFor:指定不需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
noRollbackForClassName:指定不需要回滚的异常类类名。类型为 String[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。

需要注意的是,@Transactional 若用在方法上,只能用于 public 方法上。对于其他非 public方法,如果加上了注解@Transactional,虽然 Spring 不会报错,但不会将指定事务织入到该方法中。因为 Spring 会忽略掉所有非 public 方法上的@Transaction 注解。
若@Transaction 注解在类上,则表示该类上所有的方法均将在执行时织入事务。

实现注解的事务步骤:

  1. 声明事务管理器
    
    
    
        
        
    

  1. 开启注解驱动
    
    
  1. 业务层 public 方法加入事务属性
    @Transactional(
            propagation = Propagation.REQUIRED,
            rollbackFor = { NotEnoughException.class,NullPointerException.class }
    )
    @Override
    public void buy(Integer goodsId, Integer amount) {
        Sale sale=new Sale();
        sale.setGid(goodsId);
        sale.setNums(amount);
        saleDao.insertSale(sale);
        Goods goods = goodsDao.selectGoods(goodsId);
        if(goods==null)
        {
            throw new NullPointerException("无此商品");
        }
        if(goods.getAmount() 
5.5使用 AspectJ 的 AOP 配置管理事务(掌握) 

使用 XML 配置事务代理的方式的不足是,每个目标类都需要配置事务代理。当目标类较多,配置文件会变得非常臃肿。
使用 XML 配置顾问方式可以自动为每个符合切入点表达式的类生成事务代理。其用法很简单,只需将前面代码中关于事务代理的配置删除,再替换为如下内容即可。

Step1:创建项目
构建domain实体类层——对应数据库字段
构建dao层——操作数据库的增删改查
构建service层——业务层 控制事务
配置mybatis-config.xml文件
配置ApplicationContext.xml文件
Step1:maven 依赖 pom.xml

新加入 aspectj 的依赖坐标

org.springframework
spring-aspects
5.2.5.RELEASE

Step3:在容器中添加事务管理器

    
    
    
        
    

Step4:配置事务通知
为事务通知设置相关属性。用于指定要将事务以什么方式织入给哪些方法。
例如,应用到 buy 方法上的事务要求是必须的,且当 buy 方法发生异常后要回滚业务。

 
    
        
        
            
            

            
            
            
            
            
            
            
            
        
    

Step5:配置增强器
指定将配置好的事务通知,织入给谁。


    
    

    
    

spring的事务处理

回答问题
1.什么是事务
mysql的时候,提出了事务。 事务是指一组sql语句的集合, 集合中有多条sql语句可能是insert , update ,select ,delete, 我们希望这些多个sql语句都能成功,或者都失败, 这些sql语句的执行是一致的,作为一个整体执行。

2.在什么时候想到使用事务
当我的操作,涉及得到多个表,或者是多个sql语句的insert,update,delete。需要保证这些语句都是成功才能完成我的功能,或者都失败,保证操作是符合要求的。

在java代码中写程序,控制事务,此时事务应该放在那里呢?
service类的业务方法上,因为业务方法会调用多个dao方法,执行多个sql语句

3.通常使用JDBC访问数据库, 还是mybatis访问数据库怎么处理事务
jdbc访问数据库,处理事务 Connection conn ; conn.commit(); conn.rollback();
mybatis访问数据库,处理事务, SqlSession.commit(); SqlSession.rollback();
hibernate访问数据库,处理事务, Session.commit(); Session.rollback();

4.3问题中事务的处理方式,有什么不足
1)不同的数据库访问技术,处理事务的对象,方法不同,
需要了解不同数据库访问技术使用事务的原理
2)掌握多种数据库中事务的处理逻辑。什么时候提交事务,什么时候回顾事务
3)处理事务的多种方法。

总结: 就是多种数据库的访问技术,有不同的事务处理的机制,对象,方法。

5.怎么解决不足
spring提供一种处理事务的统一模型, 能使用统一步骤,方式完成多种不同数据库访问技术的事务处理。

使用spring的事务处理机制,可以完成mybatis访问数据库的事务处理
使用spring的事务处理机制,可以完成hibernate访问数据库的事务处理。

6.处理事务,需要怎么做,做什么
spring处理事务的模型,使用的步骤都是固定的。把事务使用的信息提供给spring就可以了

1)事务内部提交,回滚事务,使用的事务管理器对象,代替你完成commit,rollback
事务管理器是一个接口和他的众多实现类。
接口:PlatformTransactionManager ,定义了事务重要方法 commit ,rollback
实现类:spring把每一种数据库访问技术对应的事务处理类都创建好了。
mybatis访问数据库—spring创建好的是DataSourceTransactionManager
hibernate访问数据库----spring创建的是HibernateTransactionManager

 怎么使用:你需要告诉spring 你用是那种数据库的访问技术,怎么告诉spring呢?
  声明数据库访问技术对于的事务管理器实现类, 在spring的配置文件中使用声明就可以了
  例如,你要使用mybatis访问数据库,你应该在xml配置文件中
   

2)你的业务方法需要什么样的事务,说明需要事务的类型。
说明方法需要的事务:
1)事务的隔离级别:有4个值。
DEFAULT:采用 DB 默认的事务隔离级别。MySql 的默认为 REPEATABLE_READ; Oracle默认为 READ_COMMITTED。
➢ READ_UNCOMMITTED:读未提交。未解决任何并发问题。
➢ READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。
➢ REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读
➢ SERIALIZABLE:串行化。不存在并发问题。

  1. 事务的超时时间: 表示一个方法最长的执行时间,如果方法执行时超过了时间,事务就回滚。
    单位是秒, 整数值, 默认是 -1.

3)事务的传播行为 : 控制业务方法是不是有事务的, 是什么样的事务的。
7个传播行为,表示你的业务方法调用时,事务在方法之间是如果使用的。

		PROPAGATION_REQUIRED
		PROPAGATION_REQUIRES_NEW
		PROPAGATION_SUPPORTS
		以上三个需要掌握的

		PROPAGATION_MANDATORY
		PROPAGATION_NESTED
		PROPAGATION_NEVER
		PROPAGATION_NOT_SUPPORTED

3)事务提交事务,回滚事务的时机
1)当你的业务方法,执行成功,没有异常抛出,当方法执行完毕,spring在方法执行后提交事务。事务管理器commit

2)当你的业务方法抛出运行时异常或ERROR, spring执行回滚,调用事务管理器的rollback
运行时异常的定义: RuntimeException 和他的子类都是运行时异常, 例如NullPointException , NumberFormatException
3) 当你的业务方法抛出非运行时异常, 主要是受查异常时,提交事务
受查异常:在你写代码中,必须处理的异常。例如IOException, SQLException

总结spring的事务
1.管理事务的是 事务管理和他的实现类
2.spring的事务是一个统一模型
1)指定要使用的事务管理器实现类,使用
2)指定哪些类,哪些方法需要加入事务的功能
3)指定方法需要的隔离级别,传播行为,超时时间

你需要告诉spring,你的项目中类信息,方法的名称,方法的事务传播行为。

spring框架中提供的事务处理方案
1.适合中小项目使用的, 注解方案。
spring框架自己用aop实现给业务方法增加事务的功能, 使用@Transactional注解增加事务。
@Transactional注解是spring框架自己注解,放在public方法的上面,表示当前方法具有事务。
可以给注解的属性赋值,表示具体的隔离级别,传播行为,异常信息等等

使用@Transactional的步骤:
1.需要声明事务管理器对象

   

2.开启事务注解驱动, 告诉spring框架,我要使用注解的方式管理事务。
spring使用aop机制,创建@Transactional所在的类代理对象,给方法加入事务的功能。
spring给业务方法加入事务:
在你的业务方法执行之前,先开启事务,在业务方法之后提交或回滚事务,使用aop的环绕通知

	 @Around("你要增加的事务功能的业务方法名称")
	 Object myAround(){
       开启事务,spring给你开启
		  try{
		     buy(1001,10);
			  spring的事务管理器.commit();
		  }catch(Exception e){
         spring的事务管理器.rollback();
		  }
		 
	 }

3.在你的方法的上面加入@Trancational

2.适合大型项目,有很多的类,方法,需要大量的配置事务,使用aspectj框架功能,在spring配置文件中声明类,方法需要的事务。这种方式业务方法和事务配置完全分离。

实现步骤: 都是在xml配置文件中实现。
1)要使用的是aspectj框架,需要加入依赖


	org.springframework
	spring-aspects
	5.2.5.RELEASE

2)声明事务管理器对象


  1. 声明方法需要的事务类型(配置方法的事务属性【隔离级别,传播行为,超时】)
  2. 配置aop:指定哪些哪类要创建代理。
第6章Spring 与 Web

在 Web 项目中使用 Spring 框架,首先要解决在 web 层(这里指 Servlet)中获取到 Spring容器的问题。只要在 web 层获取到了 Spring 容器,便可从容器中获取到 Service 对象。

6.1Web 项目使用 Spring 的问题(了解)

Step1:新建一个 Maven Project
类型 maven-archetype-webapp

Step2: 代码,配置文件,jar

(1)Service 层、Dao 层全部代码
(2)配置文件 applicationContext.xml 及 jdbc.properties,mybatis.xml
(3)pom.xml
(4)加入 servlet ,jsp 依赖

Step3:定义 index 页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    Title


     

注册学生

id
姓名:
email:
年龄:

Step4:定义 RegisterServlet(重点代码)

public class RegisterServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        String strId = request.getParameter("id");
        String strName = request.getParameter("name");
        String strEmail = request.getParameter("email");
        String strAge = request.getParameter("age");

        //创建spring的容器对象
        //String config= "spring.xml";
        //ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        WebApplicationContext ctx = null;
        //获取ServletContext中的容器对象,创建好的容器对象,拿来就用
        

        //使用框架中的方法,获取容器对象
        ServletContext sc = getServletContext();
        ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
        System.out.println("容器对象的信息========"+ctx);

        //获取service
        StudentService service  = (StudentService) ctx.getBean("studentService");
        Student student  = new Student();
        student.setId(Integer.parseInt(strId));
        student.setName(strName);
        student.setEmail(strEmail);
        student.setAge(Integer.valueOf(strAge));
        service.addStudent(student);

        //给一个页面
        request.getRequestDispatcher("/result.jsp").forward(request,response);

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

Step5:定义 result页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    Title


result.jsp 注册成功



Step6:web.xml 注册 Servlet

    
        RegisterServlet
        com.bjpowernode.controller.RegisterServlet
    
    
        RegisterServlet
        /reg
    

Step7:运行结果分析
当表单提交,跳转到 result.jsp 后,多刷新几次页面,查看后台输出,发现每刷新一次页面,就 new 出一个新的 Spring 容器。即,每提交一次请求,就会创建一个新的 Spring 容器。对于一个应用来说,只需要一个 Spring 容器即可。所以,将 Spring 容器的创建语句放在 Servlet 的 doGet()或 doPost()方法中是有问题的。

此时,可以考虑,将 Spring 容器的创建放在 Servlet 进行初始化时进行,即执行 init()方法时执行。并且,Servlet 还是单例多线程的,即一个业务只有一个 Servlet 实例,所有执行该业务的用户执行的都是这一个 Servlet 实例。这样,Spring 容器就具有了唯一性了。
但是,Servlet 是一个业务一个 Servlet 实例,即 LoginServlet 只有一个,但还会有StudentServlet、TeacherServlet 等。每个业务都会有一个 Servlet,都会执行自己的 init()方法,也就都会创建一个 Spring 容器了。这样一来,Spring 容器就又不唯一了。

6.2使用 Spring 的监听器 ContextLoaderListener(掌握)

对于 Web 应用来说,ServletContext 对象是唯一的,一个 Web 应用,只有一个ServletContext 对象,该对象是在 Web 应用装载时初始化的。若将 Spring 容器的创建时机,放在 ServletContext 初始化时,就可以保证 Spring 容器的创建只会执行一次,也就保证了Spring 容器在整个应用中的唯一性。
当 Spring 容器创建好后,在整个应用的生命周期过程中,Spring 容器应该是随时可以被访问的。即,Spring 容器应具有全局性。而放入 ServletContext 对象的属性,就具有应用的全局性。所以,将创建好的 Spring 容器,以属性的形式放入到 ServletContext 的空间中,就保证了 Spring 容器的全局性。

Step1:maven 依赖 pom.xml


org.springframework
spring-web
5.2.5.RELEASE

Step2:注册监听器 ContextLoaderListener
若要在 ServletContext 初 始 化 时 创 建 Spring 容 器 , 就 需 要 使 用 监 听 器 接 口ServletContextListener 对 ServletContext 进行监听。在 web.xml 中注册该监听器。

    
        org.springframework.web.context.ContextLoaderListener
    

Spring 为该监听器接口定义了一个实现类 ContextLoaderListener,完成了两个很重要的工作:创建容器对象,并将容器对象放入到了 ServletContext 的空间中。

打开 ContextLoaderListener 的源码。看到一共四个方法,两个是构造方法,一个初始化方法,一个销毁方法。


所以,在这四个方法中较重要的方法应该就是 contextInitialized(),context 初始化方法。

跟踪 initWebApplicationContext()方法,可以看到,在其中创建了容器对象。


并且,将创建好的容器对象放入到了 ServletContext 的空间中,key 为一个常量:WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE。

Step3:指定 Spring 配置文件的位置
ContextLoaderListener 在对 Spring 容器进行创建时,需要加载 Spring 配置文件。其默认的 Spring 配置文件位置与名称为:WEB-INF/applicationContext.xml。但,一般会将该配置文件放置于项目的 classpath 下,即 src 下,所以需要在 web.xml 中对 Spring 配置文件的位置及名称进行指定。

    
        
        contextConfigLocation
        
        classpath:spring.xml
    

Step4:获取 Spring 容器对象

在 Servlet 中获取容器对象的常用方式有两种:

(1) 直接从 ServletContext 中获取
从对监听器 ContextLoaderListener 的源码分析可知,容器对象在 ServletContext 的中存放的 key 为WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE。所以,可以直接通过 ServletContext 的 getAttribute()方法,按照指定的 key 将容器对象获取到。

        //创建spring的容器对象
        //String config= "spring.xml";
        //ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        WebApplicationContext ctx = null;
        //获取ServletContext中的容器对象,创建好的容器对象,拿来就用
        

(2) 通过 WebApplicationContextUtils 获取

 //使用框架中的方法,获取容器对象
        ServletContext sc = getServletContext();
        ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
        System.out.println("容器对象的信息========"+ctx);

查其源码,看其调用关系,就可看到其是从 ServletContext 中读取的属性值,即 Spring容器。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/434172.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号