Spring的工程搭建
创建项目
使用IDEA搭建maven工程
打开开发工具IDEA,点击创建新项目。
核实项目所使用的JDK是否是已经安装好的JDK。
选择Maven工程 点击下一步。
输入项目名spring-demo可以看到存储位置有自动追加spring-demo
将存储位置改为任意盘下。
groupID一般是公司域名,我们可以使用com.自己的姓名全拼作为练习。
版本默认即可 点击完成。

IDEA配置Maven
File-Setting打开设置页

搜索maven

修改maven home directory为maven安装路径。
勾选“Override”将setting文件位置及仓库位置改为自配置。
修改setting文件位置为 maven安装路径\conf\setting.xml
修改仓库位置为 maven安装路径\repository

搭建配置Spring
引入依赖
将maven仓库查询网址:MavenRepository

将maven里需要的基础包的类到处并输入至下方代码中
spring基础包:

spring-core:Core模块主要包含Spring框架基本的核心工具类,Spring的其他组件要都要使用到这个包里的类,Core模块是其他组件的基本核心
spring-beans:包含访问配置文件、创建和管理bean以及进行IOC/DI操作相关的所有类
spring-context:Spring的上下文即IOC容器,通过上下文可以获得容器中的Bean
spring-expression:EL表达式语言用于在运行时查询和操纵对象

springCouldAlibaba搭建 spring工程搭建_代理类


刷新maven等待自动下载

libraries中有了所有导入的包表示依赖引入完成

刷新出下图中的文件即代表成功引入

springCouldAlibaba搭建 spring工程搭建_AOP_02


核心配置文件

在搭建好框架后,里面包含了许多功能,这里就需要创建配置文件与spring框架通信,文件路径为\src\main\resources文件名为applicationContext.xml

官方给出的配置文件内容如下:

springCouldAlibaba搭建 spring工程搭建_AOP_03


复制到我们的配置文件后左上角会提示“Application context not configured for this file”,点击“Configure application context”,点击OK即

springCouldAlibaba搭建 spring工程搭建_AOP_04


可在这里插入图片描述编写代码测试接口类

新建接口类

springCouldAlibaba搭建 spring工程搭建_代理类_05


编写接口类:

package services;
public interface UserService {
 public void saveUser();
 }

实现类:
新建Java文件
实现接口并使用快捷键添加接口方法实现

package services.impl;
import services.UserService;
public class UserServiceImpl implements UserService {
 public void saveUser() {
 System.out.println(“service的save方法执行了”);
 }
 }

补充配置文件

将我们自定义的实现类交给Spring的容器管理

测试类

新建测试类
新建Java文件
编写测试类main方法:

public class DemoTest {
 public static void main(String[] args) {
 ApplicationContext context = new ClassPathXmlApplicationContext(“classpath:applicationContext.xml”);
 UserService service = (UserService) context.getBean(“userService”);
 service.saveUser();
 }
 }


Alt+Enter键导包
控制台打印输出,证明容器中获取到了实例

执行过程分析

BeanFactory

BeanFactory是基础类型的IOC容器,是管理bean容器的根接口,并提供了完整的IOC服务支持

简单来说BeanFactory就是一个管理Bean的工厂,它主要负责初始化各种Bean、调用生命周期等方法

ApplicationContext

ApplicationContext被称为应用上下文,是BeanFactory接口的子接口,在其基础上提供了其他的附加功能,扩展了BeanFactory接口

ClassPathXmlApplicationContext

ClassPathXmlApplicationContext是ApplicationContext的实现类,也在其基础上加了许多附加功能

该类从类路径ClassPath中寻找指定的XML配置文件,找到并完成对象实例化工作

refresh方法的作用:

准备容器刷新
准备bean工厂对象
加载配置文件中的所有bean标签
完成bean工厂实例化
完成容器刷新
context.getBean()

context.getBean()方法是通过配置文件中声明的bean标签id属性获取容器内的实例。

常用后端代码结构
项目常用后端代码结构也就是Controller+Service+Dao

Controller层(流程控制层)主要负责具体的业务模块流程的控制
Service层(业务逻辑层)主要负责业务模块的逻辑应用设计
DAO层(数据操作层)主要负责与数据库进行联络的一些任务

Controller层是为宾馆住宿后打扫人员,Service层负责顾客入住管理人员,DAO层是负责为宾馆提供一次性用具人员。

为了方便后端与前端、后端与数据库的数据传输引入了一些定义类,如entity、vo、dto、po、jo等,常用的就是entity和vo

vo为视图类,传递和接收前端的数据,与代码前端需要所对应。
entity为实体类,与数据库表一一对应。

Dao层去数据库查询基础数据,查到的基础数据用entity实体类存储
Service层调用Dao层方法拿取基础数据加工处理,加工好的数据用vo视图类存储
Controller层调用Service层方法拿取数据给前端

if判断和三目运算符
if条件语句是代码中常用的判断条件的代码,相对于其他的条件判断语句更为简单,

如果判别式结果为真,会执行代码块1,若为假执行代码块2

以取两数中最大值为例

springCouldAlibaba搭建 spring工程搭建_java_06


三目运算符

还有一种比if条件语句更为简单的语句就是三目运算符

但只有if语句中代码块只有一行语句时我们会使用三目运算符来代替if语句

其代码结构如下:
1:判别式 ? 代码块1 : 代码块2;

如果判别式结果为真,会执行代码块1,若为假执行代码块2

以取两数中最大值为例:

springCouldAlibaba搭建 spring工程搭建_AOP_07


f判断和三目运算符对比

三目运算符是我们经常在代码中使用的,a= (b==null?0:1); 这样一行代码可以代替一个 if-else,可以使代码变得清爽易读。但是,三目运算符也是有一定的语言规范的。在运用不恰当的时候会导致意想不到的问题。

但如果判断后要执行的代码块偏复杂还是用if判断比较好

Spring IOC & DI
IOC(Inversion of Control 控制反转)是面向对象编程中的一种设计模式

其最常见的方式叫做DI(Dependency Injection 依赖注入)

通过控制反转,将实例化对象的控制权,由手动的new变成了Spring框架通过反射机制实例化

需要使用的时候,依赖通过配置文件以及注解的方式注入到对象中

我们新建Maven项目名为“spring-ioc“,设置好Maven版本、配置文件以及Maven仓库

以查询User数据为例对比IOC的引入前后程序耦合性

引入IOC之前

表示Dao层数据已经一层层传到Controller层并展示了出来
缺点分析

1.代码耦合性太强 不利于程序的测试
2. 代码也不利于扩展

解决方式:

Spring的IOC完美的解决了这一点

对象的实例化由Spring框架加载实现,放到Spring容器中管理,避免了我们手动new对象

有需要用到对象实例依赖,直接向Spring容器要,让他注入即可

而一旦涉及到对象的实例修改,那么只需更改Spring加载实例化对象的地方,程序代码无需改动

从而降低耦合,提升扩展性
引入IOC(XML)

代码实现

要想使用SpringIOC首先需要导入Spring框架基础包并且添加Spring核心配置文件

将依赖交给Spring的beanFactory管理

User模块测试类:UserTest.java
读取配置文件刷新Spring容器
Controller由手动实例化改为从Spring容器拿取
把ApplicationContext传到Controller层继续使用

public class UserTest {
 public static void main(String[] args) {
 // 读取配置文件刷新Spring容器
 ApplicationContext context = new ClassPathXmlApplicationContext(“classpath:applicationContext.xml”);
 // 从Spring容器拿Controller
 UserController userController = (UserController) context.getBean(“userController”);
 // 执行Controller层方法,因为之后还需要用到context对象,故下传
 UserVo userVo = userController.getVo(1, context);
 System.out.println(userVo);
 }
 }


User模块Controller层:UserController.java

Service由手动实例化改为从Spring容器拿取
把ApplicationContext传到Service层继续使用

package controller;
public class UserController {
 private UserService userService;

代理模式
什么是代理模式
代理对象在客户端和目标对象之间起到中介作用,代理模式属于结构性设计模式。使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象。

代理模式的意义

中间隔离作用:在一些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起媒介作用,其特征是代理类和委托类实现相同的接口
增加功能:给代理类增加额外的功能可以用来扩展委托类的功能,这样做只需要修改代理类而不需要再修改委托类
委托类和代理类的功能

委托类实现真正的业务功能
代理类
负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等
代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务
例如将项目加入缓存、日志这些功能就可以使用代理类来完成,没必要打开已经封装好的委托类
静态代理 & 动态代理

代理模式可以分为静态代理和动态代理:

静态代理

静态代理是由程序员创建或特定工具自动生成源代码,在程序运行之前,代理类就已经编译生成了.class文件。

静态代理的优点是可以在符合开闭原则的情况下对目标对象进行功能扩展,缺点则是开发人员需要为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改

动态代理

动态代理是在程序运行时通过反射机制动态创建的,随用随加载。动态代理常用的有基于接口和基于子类两种方式

基于接口的动态代理指的是由JDK官方提供的Proxy类,要求被代理类最少实现一个接口,这种方式大大减少了开发人员的开发任务,减少了对业务接口的依赖,降低了耦合度,缺点就是注定有一个共同的父类叫Proxy,Java的继承机制注定了这些动态代理类们无法实现对class的动态代理,原因是多继承在Java中本质上就行不通

基于子类的动态代理指的是由第三方提供的CGLib,CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以要求被代理类不能用final修饰,即不能是最终类。
动态代理和静态代理基本思路是一致的,只不过动态代理功能更强大,随着业务的扩展适应性更强。

SpringAOP
Spring的核心特性就是IOC和AOP,即:面向切面编程

面向切面编程是指通过预编译和运行期动态代理的方式实现在不修改源代码的情况下给程序动态统一添加功能的一种技术

如果目标对象有接口,优先使用JDK 动态代理,如果目标对象没有接口,则使用CGLib动态代理

AOP(Aspect-OrientedProgramming,面向切面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。
AOP编程可不是Spring独有的,Spring只是支持AOP编程的框架之一,这一点非常重要,切勿搞反了关系。
AOP分两类,一类可以对方法的参数进行拦截,一类是对方法进行拦截,SpringAOP属于后者,所以Spring的AOP是属于方法级的 。

引入AOP(XML)

相关概念

使用Spring的AOP替代代理类。先回顾下AOP的概念

AOP是一种编程设计模式,是一种编程技术,使用AOP后通过修改配置即可实现增加或者去除某些附加功能

学习AOP中的常用术语:

Join point(连接点)
所谓连接点是指那些可以被拦截到的点

在Spring中这些点指的是方法,可以看作正在访问的,或者等待访问的那些需要被增强功能的方法

Spring只支持方法类型的连接点

Pointcut(切入点)
切入点是一个规则,定义了我们要对哪些Joinpoint进行拦截

因为在一个程序中会存在很多的类,每个类又存在很多的方法,Pointcut来标记哪些方法会应用AOP对该方法做功能增强

Advice(通知)
所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。也就是对方法做的增强功能。通知分为如下几类:

前置通知:在连接点之前运行的通知类型,它不会阻止流程进行到连接点,只是在到达连接点之前运行该通知内的行为

后置通知:在连接点正常完成后要运行的通知,正常的连接点逻辑执行完,会运行该通知

最终通知:无论连接点执行后的结果如何,正常还是异常,都会执行的通知

异常通知:如果连接点执行因抛出异常而退出,则执行此通知

环绕通知:环绕通知可以在方法调用之前和之后执行自定义行为

Target(目标)
Target指的是代理的目标对象,更通俗的解释就是:AOP对连接点方法做增强,底层是代理模式生成连接点所在类的代理对象,那么连接点所在的类,就是被代理的类称呼为Target

Aspect(切面)
切面本质是一个类,只不过是个功能类,作为整合AOP的切入点和通知。

一般来讲,需要在Spring的配置文件中配置,或者通过注解来配置

Weaving(织入)
织入是一种动作的描述,在程序运行时将增强的功能代码也就是通知,根据通知的类型(前缀后缀等…)放到对应的位置,生成代理对象

Proxy(代理)
一个类被AOP织入增强后,产生的结果就是代理类

代码实现

在执行原始业务类前对方法增强也就是SpringAOP中所谓的前置通知,对原始业务类中的方法执行之后的增强行为就是后置通知

而一旦出现异常,那么所做的动作就是异常通知。本案例使用几种通知,来实现事务的控制。

删除事务代理工具类:TransactionProxyUtils.java

导入aspectjweaver包
配置文件中添加 AOP 的相关配置
修改测试类代码
执行结果:
控制台打印结果:
在转账前后由开启、提交事务,最后有释放连接

表示SpringAOP已经在不改变源代码的基础上对其做了增强

修改前数据库中值
修改后数据库中值
两个账号的数据已经发生了改变,证明转账的动作确实已经完成

再次在出账账户金额修改之后,入账账户金额修改之前添加异常代码
运行中发现在捕捉到异常后进行了事务的回滚

说明SpringAOP配置的事务管理同样也解决了事务问题,保证了数据的原子性和一致性
通过在xml文件中配置SpringAOP相关配置,就可以实现对我们业务类中的方法实现了增强,无需自定义对业务类做代理实现

XML改注解(AOP)

使用注解介绍

@Aspect
此注解用于表明某个类为切面类,而切面类的作用我们之前也解释过,用于整合切入点和通知

@Pointcut
此注解用于声明一个切入点,表明哪些类的哪些方法需要被增强

@Before 前置通知
在连接点之前运行的通知类型,它不会阻止流程进行到连接点,只是在到达连接点之前运行该通知内的行为

@AfterReturning 后置通知
在连接点正常完成后要运行的通知,正常的连接点逻辑执行完,会运行该通知

@After 最终通知
无论连接点执行后的结果如何,正常还是异常,都会执行的通知

@AfterThrowing 异常通知
如果连接点执行因抛出异常而退出,则执行此通知

代码实现

删除XML中的AOPXML配置并注解代理模式

package transaction;
@Component
 @Aspect
 public class TransactionManager {
 // 数据库连接工具类
 @Autowired
 private ConnectionUtils connectionUtils;

总结

SpringAOP的作用就是把程序中重复的代码抽取出来,在需要执行的时候,使用动态代理技术,在不修改源码的基础上,对已有方法进行增强

优势就是减少了重复代码,提高代码复用性,提高开发效率,使得代码的维护更加方便

在开发时通常将日志记录,数据库连接池的管理,系统统一的认证、权限管理等用面向切面的方式开发。

spring用代理类包裹切面,把他们织入到Spring管理的bean中。也就是说代理类伪装成目标类,它会截取对目标类中方法的调用,让调用者对目标类的调用都先变成调用伪装类,伪装类中就先执行了切面,再把调用转发给真正的目标bean。