什么是Spring

Spring 是一款开源的轻量级 Java 开发框架,旨在提高开发人员的开发效率以及系统的可维护性。
Spring的核心思想是不重新造轮子,开箱即用,省去开发中的大量重复工作,提高开发效率。

IOC

IOC(Inversion of Control:控制反转)是一种设计思想,而不是一个具体的技术实现。
所谓控制,就是创建对象的权力,而反转即为把控制权交给外部环境(Spring框架,IOC容器)。
就以我的Redis项目来说,在没有使用IOC思想的情况下,Service层想要使用Dao层的具体实现的话,需要通过newUserServiceImpl中手动创建IUserDao的具体实现类UserDaoImpl,如图所示:
手动创建
但一个项目难免有很多需求,针对这些需求,我需要在IUserDao接口中开发满足这些需求的具体实现类。因为Service层依赖了IUserDao的具体实现,所以我就要修改UserServiceImplnew的对象。如果只有一个类引用了IUserDao的具体实现,那还好说,但如果很多地方都引用了,牵一发而动全身,修改起来会极其麻烦。

如果使用IOC的思想,我们将对象的控制权交由IOC容器去管理,要用到的时候直接问IOC容器要即可,如图所示:
IOC

在Spring中,IOC容器是Spring用来实现IOC的载体,本质就是一个Map(key, value),Map中存放的是各种对象。

Bean

Bean代指的就是那些被IOC容器所管理的对象,我们需要告诉IOC容器哪些对象需要管理,常用的注解有以下4种:
@Comonent:如果一个Bean不知道属于哪个层,可以使用;
@Repository:持久化层(Dao),用于数据库相关操作;
@Service:服务层,实现主要的业务逻辑,需要用到Dao层;
@Controller:Spring MVC控制层,用于接受用户请求并调用Service层返回数据给前端页面。

注入Bean的注解

我在项目中主要使用@Autowired@Resource,两者的主要区别如下:

  • @Autowired 是 Spring 提供的注解,@Resource 是 JDK 提供的注解。
  • @Autowired 默认的注入方式为byType(根据类型进行匹配),@Resource默认注入方式为 byName(根据名称进行匹配)。
  • 当一个接口存在多个实现类的情况下,@Autowired@Resource都需要通过名称才能正确匹配到对应的 Bean。@Autowired 可以通过 @Qualifier 注解来显式指定名称,@Resource可以通过 name 属性来显式指定名称.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Autowired
private IUserService userServiceImpl;

@Autowired
@Qualifier(value = "userServiceImpl")
private IUserService userService;

System.out.println("--------------");

@Resource
private IUserService userServiceImpl1;

@Resource(name = "userServiceImpl1")
private IUserService userService;

AOP

在网上看到了一个很好的例子,我自己试着画了下图解:
以我自己的Redis项目来说,很多操作都需要检查活动有效性和是否需要用户登录,比如下单、点赞、关注等,按照正常逻辑可以这样做:
1.0
这样做会导致有多少接口,就要把检查这部分代码copy多少次,导致代码冗余。我可以把这部分方法抽取出来,做成一个公共方法,谁需要谁就来调用:
2.0
但这样同样有个问题,虽然我不用每次都copy代码了,但每个接口总要调用这个方法。于是就有了切面的概念,我直接将方法注入到接口调用的某个某个地方(切点)。
3.0
红框处,就是面向切面变成。

OOP/AOP

在我学习AOP时,有这样一个疑问:OOP和AOP看似都是将可复用的代码抽取出来,二者到底有什么区别和联系呢?
先举一个OOP的例子:
现有三个类:Horse,Pig,Dog,这三个类都有eat()run()两个方法。
通过OOP思想中的继承,我们可以提取出一个名为Animal的父类,将eat()run()方法放入父类中,Horse,Pig,Dog通过继承Animal类即可自动获得eat()run()方法,从而减少代码冗余。

OOP思想可以解决大部分代码重复的问题。但有些问题是处理不了的。比如在父类 Animal 中的多个方法的相同位置出现了重复的代码,OOP 就解决不了。

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
/**
* 动物父类
*/
public class Animal {

/** 身高 */
private String height;

/** 体重 */
private double weight;

public void eat() {
// 性能监控代码
long start = System.currentTimeMillis();

// 业务逻辑代码
System.out.println("I can eat...");

// 性能监控代码
System.out.println("执行时长:" + (System.currentTimeMillis() - start)/1000f + "s");
}

public void run() {
// 性能监控代码
long start = System.currentTimeMillis();

// 业务逻辑代码
System.out.println("I can run...");

// 性能监控代码
System.out.println("执行时长:" + (System.currentTimeMillis() - start)/1000f + "s");
}
}

这部分重复代码,一般称为横切逻辑代码。AOP就是用来解决这类问题的。
利用AOP的思想,我们可以在不改变原有业务逻辑的情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复。

总结

OOP是面向对象编程,核心思想是将客观存在的不同事物抽象成相互独立的类,然后把与事物相关的属性和行为封装到类里,并通过继承和多态来定义类彼此间的关系,最后通过操作类的实例来完成实际业务逻辑的功能需求。

AOP是面向切面编程,核心思想是将业务逻辑中与类不相关的通用功能切面式的提取分离出来,让多个类共享一个行为,一旦这个行为发生改变,不必修改类,而只需要修改这个行为即可。

二者的区别:

  1. 面向目标不同:OOP面向名词领域,AOP面向动词领域。
  2. 思想结构不同:OOP是纵向结构,AOP是横向结构。
  3. 注重方面不同:OOP注重业务逻辑单元的划分,AOP偏重业务处理过程的某个步骤或阶段。

二者的联系:
两者之间是一个相互补充和完善的关系,AOP是OOP的一种延续。