Spring 作为一个基础的框架,是在 Java EE 开发历史中,是成千上万公司选择。单独使用 Spring 的非常少了,很多都是用 Spring-Boot/Spring-Cloud 来开发,但是 Spring 基础依然是我们使用的基石。我们将一起来聊一聊 Spring 的基本使用。
首先我们一起来了解一下 Spring 框架整体架构图如下:
核心功能:控制反转(IOC) 、AOP
非核心功能:事件驱动、国际化、资源管理,数据绑定、类型转换 、SpEL、单元测试等。
PS:核心功能,在本文会有使用实践。
控制反转(IOC)是 Spring 框架的核心功能之一,其本质的就是将用户创建 Bean 的过程赋予给 IOC 容器去完成,实现 Bean 创建权利的反转为容器来创建 Bean 和依赖 Bean 。
Spring 容器创建 Bean 只需要三个步骤:
举一个例子:
public class TestMain {public static void main(String[] args) {ApplicationContext applicationContext =new AnnotationConfigApplicationContext(AppConfig.class);Student student = applicationContext.getBean(Student.class);student.study();student.sleep();student.study();}
}@Configuration
@Import(Student.class)
class AppConfig {}@Component
class Student {private String name;private Integer source;public void study() {System.out.println("学习中...");}public void sleep() {System.out.println("休息中...");}// setter getter
}
复制代码
运行上面的代码我们可以得到一下结果:
学习中...
休息中...
学习中...
复制代码
上面的代码执行什么呢?其实我们可以将 ApplicationContext
理解为 Spring 容器对象,然后我们在 AppConfig 配置类中去定义 Spring 容器去帮助我们加载那些 Bean ,最后我们通过 getBean 方法获取我们注册的 Bean 对象。如下图:
在这个过程中使用到那些关键的接口/类呢?
ApplicationContext
的一个父接口。Spring IOC 容器主要是解决了 Bean 的创建和依赖管理的问题。我们常见的有两种依赖注入方式:
通过成员属性的方式实现 Bean 的自动注入
@Component
class Student {@Autowiredprivate Address address;// ...
}@Component
class Address {// ...
}
复制代码
@Component
可以将 Student 、Address 类标记为一个 bean 对象@Autowired
可以将依赖 Bean 自动注入进来。通过构造方法实现 Bean 的自动注入
@Component
class Student {public Student(Address address) {this.address = address;}
}@Component
class Address {// ...
}
复制代码
Spring 的 IOC 解决了什么问题?
AOP(Aspect Oriented Programming)是面向切面的意思。
Java 是一个面向对象(OOP)的编程语言,但是它有一个弊端就是需要为多个不具有继承关系的对象引入一个公共行为时,例如:日志记录、权限验证、事务管理、访问统计等公共行为,这样不便于维护,而且有大量重复代码,AOP 可以实现和 AOP 的互补。
举个例子:
我们有两个逻辑登录业务、订单业务,需要在他们调用前后进行:权限验证、日志记录等公共逻辑。
通过 OOP 的方式实现我们需要做一个逻辑模板:权限验证,具体逻辑(登录、订单),日志记录。
通过 AOP 的方式实现我们只需针对具体逻辑(登录、订单)前后做一个自定义切点,进行权限验证、日志记录。
如下图:
经过 AOP 方式处理过后,我们可以减少公共对象的引用、通过非继承的方式来处理切入逻辑的拦截,实现公共逻辑和业务的逻辑的松耦合关系。
Spring 通过代理的方式去实现 AOP,Java 代理的两种模式:静态代理、动态代理。
Spring 的两种代理模式:
两种代理的选择:
如果 Bean 实现了接口就采用 JDK 代理, 如果没有实现就采用 GCLIB 代理。
假设已经有一个 UserService 类提供了登录业务,我们需要对该业务做一个【权限验证】、【日志记录】这两个公共逻辑,在不修改 UserService 类代码的前提下就可以通过 AOP 来解决。
示例如下:
// 1. 测试类
public class AopTest {public static void main(String[] args) {ApplicationContext applicationContext =new AnnotationConfigApplicationContext(AopConfig.class);UserService userService = applicationContext.getBean(UserService.class);userService.login("admin", "123456");}
}// 2. 配置类
@EnableAspectJAutoProxy
@Configuration
@Import({UserService.class, ValidateAspect.class})
class AopConfig {}// 业务类
@Component
class UserService {public String login(String username, String password) {System.out.println("username:" + username + ",password:" + password);return "ok";}
}// Aspect
@Aspect
@Component
class ValidateAspect {@Pointcut("execution(public * io.zhengsh.simu.spring.UserService.*(..))")public void servicePoint() {// Do nothing}@Around("servicePoint()")public Object doAroundService(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("validate param invoke !!!");return joinPoint.proceed();}
}
复制代码
org.springframework spring-core ${spring.version} org.springframework spring-beans ${spring.version} org.springframework spring-context ${spring.version} org.springframework spring-aop ${spring.version} org.aspectj aspectjweaver 1.9.9.1