二、Spring

1. 概念

(1)Spring是一个开源的轻量级的应用开发框架,其目的是用于简化企业级应用程序开发,降低开发者的开发难度;
(2)Spring提供的IoC和AOP应用,可以将组件的耦合度降至最低(即解耦),便于系统日后的维护和升级;
(3)Spring为系统提供了一个整体的解决方案,开发者可以利用它本身提供的功能外,也可以与第三方框架和技术整合应用,可以自由选择采用哪种技术进行开发。比如Spring整合SpringMVC、Spring整合MyBatis、Spring整合Struts2、Spring整合Hibernate、Spring整合Quartz(定时任务处理)。

2. Spring的优点

(1)方便解耦,简化开发:通过Spring提供的IoC容器,可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。
(2)AOP编程的支持:通过Spring提供的AOP功能,方便进行面向切面的编程,如性能监测、事务管理、日志记录等。
(3)声明式事务的支持
(4)方便集成各种优秀框架
(5)降低Java EE API的使用难度:例如JDBC、JavaMail和远程调用等提供了简便封装。

3. Spring架构

Spring 最初的目标就是要整合一切优秀资源,然后对外提供一个统一的服务。
Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式,如下图所示:
在这里插入图片描述

核心容器 Spring Core核心容器,提供Spring框架的基本功能。核心容器的主要组件是BeanFactory,它是工厂模式的实现。BeanFactory 使用**控制反转(IOC)模式 **,将应用程序的配置和依赖性规范与实际的应用程序代码分开。
Spring ContextSpring上下文,是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
Spring AOP通过配置管理特性,Spring AOP 模块直接将面向切面编程的功能集成到了 Spring 框架中。可以很容易地使 Spring框架管理的任何对象支持AOP。Spring AOP模块为基于Spring 的应用程序中的对象提供了事务管理服务。通过使用Spring AOP,就可以将声明性事务管理集成到应用程序中
Spring DAOJDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
Spring ORMSpring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括JDO、Hibernate和iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
Spring WebWeb上下文模块建立在应用程序上下文模块之上,为基于Web 的应用程序提供了上下文。所以Spring 框架支持与Jakarta Struts的集成。Web模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
Spring MVC框架MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。

## 4. 控制反转(IOC) **IOC—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。**

控制反转,就是指将对象的创建,对象的存储(map),对象的管理(依赖查找,依赖注入)交给了Spring容器。
提示:Spring容器是spring中的一个核心模块,用于管理对象。

只需要将类提前配置在spring配置文件中,就可以将对象的创建交给spring容器,当需要对象时,不需要自己创建,而是直接通过spring获取即可。
这样就省去了实例化对象的步骤,可以降低代码之间的耦合性。

这时候就会有个问题:IOC如何实例化对象?
答案如下:

<bean id="user" class="com.tdeu.pojo.User">

Spring容器执行过程:
当tomcat服务器启动时会加载Spring容器的配置文件,当程序解析到Bean标签时,通过反射机制实例化对象,对象最终保存到了Spring容器自身维护的Map<Id,Object对象>。

知识点:反射机制调用对象的无参构造实例化对象。

控制反转,顾名思义就是控制(由IOC容器来控制对象的创建)反转(由容器来帮忙创建及注入依赖对象)
那问题来了:谁控制谁?控制什么?为何是反转?哪些方面反转了?
谁控制谁:当然是IOC容器控制了对象;
控制什么:主要控制了外部资源获取,不只是对象,也包括文件等;
为何是反转:因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;
哪些方面反转了:依赖对象的获取被反转了。

用图例说明一下,传统程序设计如下图所示,都是主动去创建相关对象然后再组合起来:
在这里插入图片描述
当有了IoC/DI的容器后,在客户端类中不再主动去创建这些对象了。
在这里插入图片描述

4.1 IOC能做什么

IOC是一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。

传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难以测试;有了IOC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。

其实IOC对编程带来的最大改变不是从代码上,而是从思想上发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IOC/DI思想中,应用程序就需要被动的等待IOC容器来创建并注入它所需要的资源。

IOC很好的体现了面向对象设计法则之一 —— 好莱坞法则:“别找我们,我们找你”。即由IOC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。

4.2 IOC和DI

DI—Dependency Injection,即“依赖注入”:是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中

依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

理解DI的关键是:“谁依赖谁? 为什么需要依赖? 谁注入谁? 注入了什么?”,那就让我来深入分析一下:
(1)谁依赖于谁:当然是应用程序依赖于IoC容器;
(2)为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
(3)谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
(4)注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。

理解了以上的内容,接下来就要理解IOC和DI存在什么样的关系?
其实它们是同一个概念的不同角度描述。由于控制反转概念比较含糊,可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系,所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IOC 而言,“依赖注入”明确描述了“被注入对象依赖IOC容器配置依赖对象”

5. 底层原理

对于对象中的属性来说,注入属性的方式:在配置文件中使用property标签
代码示例如下:

<property name="对象属性名称" >
   <value>需要注入的内容</value>
</property>

问:那么如果在类中有对另一个对象的引用的情况,要如何注入呢?
答:其实类似于注入一个属性,只是需要使用ref标签进行注入,具体步骤如下:
首先我们需要通过bean配置两个类,我们分别给他们的id命名为:类1、类2;
这时假如我们的需求是类1中包含有对类2的引用,也就是说 类2 是 类1 的一个属性。
这时我们就需要将类2的对象注入到类1对象中。
配置文件中的写法如下:

<bean id= "类1" class="类1的全路径">
       <!--这里的注入方法类似于上面的value注入,可以写在一行也可以多行-->
        <property name="类2在类1中的属性名" ref="类2"></bean>
<!--这里的类2是对象名称,再注入的时候ref中的值必须和bean 后面的Id完全一致-->
<bean id="类2" class="类2的全路径"></bean>

以上的做法就是Spring管理各个对象和维护各个对象之间的关系
其中具体的执行原理是:
1.当ClassPathXmlApplicationContext(“applicationContext.xml”)这句话执行的时候,spring容器对象就被创建;
2.创建的对象会读取applicationContext.xml中配置的bean就会被创建,并且放入内存。具体是怎么创建的,就要依赖于反射机制。
就拿以上代码来说:首先,类1被创建;创建后,假设这个对象在内存中的地址为:123对象中对于对象二的引用为(???)。此时的内存如下:
在这里插入图片描述然后 类2 被创建,这时候 类2 在对象中的内存地址会赋值给 类1 中相应字段的引用。
具体来说:假设 类2 的地址为:456,这时123对象中的那个对于对象2的引用由(???)改为(456)完成类的依赖注入。
此时内存如下:
在这里插入图片描述
其实,这些对象都存在与applicationContext对象的一个类似于HashMap的引用中。
这也是我们为什么要将applicationContext对象设置为单例的原因。

那Spring是如何实现刚才的那些的调用?
首先,通过dom4j将我们的配置文件读取,这时我们就可以解析到所有相关的类的全路径了;
然后,它再利用反射机制通过如下代码完成类的实例化:类1=Class.forName(“类1的全路径”);
这时,我们就得到了类1。
这也是为啥当我们的类的全路径写错了会导致出现classNotfind的错误。

当我们得到了 类1 以后,通过调用 类1 的set方法,将属性给对象进行注入。而且,需要遵循首字母大写的set规范。例如:我们的类中有个字段的属性为name那么set方法必须写成setName
注意:Name 的首字母要大写,否则会报一个属性找不到的错误。
代码示例如下:

 public void setName(String name){ 
     this.name= name; 
 }

对象创建后,将对象id和对象物理地址一起存入类似于HashMap的容器中;
然后,通过getBean的方法,通过对象Id获得对象的物理地址,得到对象;
最后,调用对象的方法,完成对方法的调用。

6. 关于AOP的那些事儿

6.1 什么是AOP?

AOP(面向切面编程)可以说是OOP(面向对象编程)的补充和完善。

OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。

OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能,日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系,对于其他类型的代码也是如此,如安全性、异常处理和透明的持续性。

这种散布在各处的无关的代码被称为横切代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

而AOP则利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。

简单的说,所谓“方面”,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。

AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。

AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。
面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。

在这里插入图片描述

6.2、AOP的基本概念

(1)Aspect(切面):通常是一个类,里面可以定义切入点和通知
(2)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用
(3)Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around
(4)Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式
(5)AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类。

通知方法:
1.前置通知:在我们执行目标方法之前运行(@Before)
2.后置通知:在我们目标方法运行结束之后 ,不管有没有异常(@After)
3.返回通知:在我们的目标方法正常返回值后运行(@AfterReturning)
4.异常通知:在我们的目标方法出现异常后运行(@AfterThrowing)
5.环绕通知:动态代理, 需要手动执行joinPoint.procced()(其实就是执行我们的目标方法执行之前相当于前置通知, 执行之后就相当于我们后置通知(@Around)

6.3 AOP 实现分类

AOP 要达到的效果是,保证开发者不修改源代码的前提下,去为系统中的业务组件添加某种通用功能。

AOP 的本质:由 AOP 框架修改业务组件的多个方法的源代码,看到这其实应该明白了,AOP 其实就是前面一篇文章讲的代理模式的典型应用。

按照 AOP 框架修改源代码的时机,可以将其分为两类:
第一类:静态 AOP 实现: AOP 框架在编译阶段对程序源代码进行修改,生成了静态的 AOP 代理类,例如:AspectJ… …
注意:AOP 框架在编译阶段对程序源代码进行修改时所生成的 .class 文件已经被改掉了,需要使用特定的编译器。
第二类:动态 AOP 实现: AOP 框架在运行阶段对动态生成代理对象,例如:SpringAOP… …
注意:框架在运行阶段对动态生成代理对象是指:在内存中以 JDK 动态代理 或 CGlib 动态地生成 AOP 代理类。
下面给出常用 AOP 实现比较:
在这里插入图片描述

6.4 Spring AOP

Spring中的AOP代理还是离不开Spring的IOC容器,代理的生成,管理及其依赖关系都是由IOC容器负责,Spring默认使用JDK动态代理,在需要代理类而不是代理接口的时候,Spring会自动切换为使用CGLIB代理,不过现在的项目都是面向接口编程,所以JDK动态代理相对来说用的还是多一些。

6.5 基于注解的AOP配置方式

切面类:
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐