本文介绍Spring体系中Spring framework的主要内容, 包括Spring的两个核心概念, 即控制反转(Inversion of Control, IoC)和面向切面编程(Aspect Oriented Programming, AOP). 这些内容是Spring的基础知识, 被广泛的应用在其他的模块之中.
IoC
创建Bean
Spring中有两种基于注解的方式创建Bean, 具体使用功能的注解效果如下所示
注解 | 注解对象 | 效果 |
---|---|---|
@Component | 类 | 表示此类需要装配到IoC容器之中 |
@ComponentScan | 类 | 自动扫描包含@Component的类并且将其装配到IoC容器之中 |
@Configuration | 类 | 表示此类是一个配置类, 配合@Bean来创建Bean |
@Bean | 方法 | 配置类中使用, 表示此方法的返回值需要装配到IoC容器之中 |
@ComponentScan不带参数时扫描当前包和其子包, 可以额外指定其扫描的表的位置. 通过includeFilters和excludeFilters可以指定需要扫描的类的条件或者需要排除的类的条件.
被@Configuration注解的类本身也会声明为一个Bean.
以下代码演示使用上述两种方式创建Bean.
1 |
|
使用@Component生成的Bean的名称是首字母小写的类名, 使用@Bean生成的类的名称是对应的方法的名称. 因此上述代码中分别创建了名为basicDataSource和user的Bean. 此外也可以给上述两个注解提供一个字符串来指定为需要的名称.
注入Bean
Spring中可以使用@Autowired进行注入, 此注解按照类型寻找合适的Bean, 因此当前的系统中有且只能有一个类与需要注入的类符合. 例如以下的代码通过此标签注入了之前声明的DataSource.
1 | public class WebSecurityConfig extends WebSecurityConfigurerAdapter { |
@Autowired可以注解在属性, 构造方法和setter方法上, 但是推荐注解在构造方法或setter方法上.
消除歧义
由于@Autowired按照类型注入, 因此如果需要注入的类型在系统中存在多个实现, 则注入时有多个选择, 从而无法自动注入. 因此Spring提供了@Primary和@Quelifier两个注解, 用于消除歧义
注解 | 效果 |
---|---|
@Primary | 如果多个可选类中只有一个类有此注解, 则使用此注解对应的类 |
@Quelifier | 与@Autowired一同使用, 通过类型和名称共同确定一个Bean |
如果有多个可选的了类都标记了@Primary, 则依然无法自动注入.
注入常量
Spring中可以使用@Value注入一个常量, 常量值通常来自配置文件, 但也可以通过Spring EL表达式获得计算结果或者系统属性.
1 | // 如果出现编码问题, PropertySource可以指定编码方式 |
其中${...}
对应一个配置文件中的属性. @PropertySource将指定的配置文件加入到Spring的环境之中.
Spring EL表达式大致有如下的集中用法
示例 | 效果 |
---|---|
#{'Some String'} |
获得一个字符串 |
#{3.1415} |
注入一个数字 |
#{T(System).currentTimeMillis()} |
调用函数获得返回值 |
#{user.name} |
引用bean的属性 |
#{23+321} |
数值计算 |
#{user.name eq 'admin'} |
字符串比较 |
如果调用java.lang.*中的方法, 可以直接写类名, 调用其他方法需要写全限定名.
更多关于Spring EL表达式的内容, 可以阅读官方文档的Language Reference
Bean作用域
Spring中的Bean有如下几种作用域
类型 | 使用范围 | 说明 |
---|---|---|
singleton | 所有Spring应用 | 默认值, 单例模式 |
prototype | 所有Spring应用 | 每次都会创建新的Bean |
session | Spring Web应用 | 一次HTTP会话保存唯一 |
application | Spring Web应用 | 整个Web工程的生命周期保持唯一 |
通常, application与singleton效果相同, 因此使用singleton来代替.
如果希望改变Bean的作用域, 可以使用@Scope注解. 以下代码演示将一个Bean设置为prototype作用域.
1 |
|
如果使用Spring MVC, 则可以使用 WebApplicationContext 的有关属性.
AOP
AOP的作用是在指定的一个切面前后加入一部分代码, 从而在不干扰原有业务逻辑的基础上, 提供额外的功能. 例如在于数据库的操作过程中, 业务逻辑可以只负责数据的查询, 如何开启数据库连接以及如何关闭连接由框架通过AOP技术完成.
使用AOP功能需要引入aspectjrt(提供基本功能)和aspectjweaver(提供Advice相关功能)
AOP基本概念
AOP涉及如下的一些基础概念, 具体如下
名称 | 英文名称 | 含义 |
---|---|---|
连接点 | join point | 被拦截的对象, Spring只支持方法 |
切点 | point cut | 通过规则定义的一组连接点 |
通知 | advice | 在拦截对象之前,之后等条件下执行的方法 |
创建切面
使用@Aspect来表明一个类是切面的配置类. 由于切面的配置类需要成为一个Bean才能使用, 因此通常还会加上@Configuration来将其声明为一个配置类. 此外还可以使用@EnableAspectJAutoProxy使得多个切面时, 能使用自动代理使得这些切面都能执行.
在切面配置类中可以使用@Before, @After, @AfterReturning, @AfterThrowing等注解来添加功能, 各注解功能如下
注解 | 功能 |
---|---|
@Before | 此注解修饰的方法在注解指定的方法调用之前执行 |
@After | 此注解修饰的方法在注解指定的方法调用之后执行 |
@AfterReturning | 此注解修饰的方法在注解指定的方法正常返回后执行 |
@AfterThrowing | 此注解修饰的方法在注解指定的方法抛出异常后执行 |
以下代码演示如何使用上述注解创建切面
1 |
|
其中的execution
表示在方法执行的时候拦截, *
表示任意返回值, top.lizec.web.UserWeb
是拦截对象的全限定名, listUser
是拦截方法的名称, (..)
表示任意参数.
创建切点
直接使用@Before等注解创建切面时, 可能有很多注解都需要拦截同一个连接点, 从而导致同一的条件写多次. 此时可以使用切点绑定一个条件, 之后的注解都引用这个切点.
切点通过@Pointcut注解创建, 注解在方法上. 注解的方法不需要有实际内容, 仅仅起到提供名称的作用, 其他的注解直接引用这个方法的名称来表示引用此切点. 以下代码演示如何通过切点进行拦截
1 |
|
环绕执行
除了上述提到的在拦截方法前后执行的注解以外, Spring还提供了一种环绕执行模式. 在该模式下使用新的方法替代原有的方法, 并且提供了一个调用原来方法的机制. 以下代码演示环绕执行
1 |
|
切面执行顺序
当有多个切面时, 其执行顺序是随机的, 可以使用@Order来指定切面的执行顺序. @Order需要一个数字参数, 数字越小, 优先级越高.
各个切面嵌套执行, 即执行before时, 数字越小越先执行, 执行after时, 数字越大越先执行
AOP 表达式
以下是一些常见的AOP 表达式
表达式 | 含义 |
---|---|
excution(...) |
表示一个方法执行是拦截, 可以使用*匹配任意字符, 使用(..)表示任意参数 |
target(...) |
指定一个接口, 拦截所有实现此接口的类的全部方法 |
@target(...) |
指定一个注解, 拦截所有被指定注解修饰的方法 |
@within(...) |
指定一个注解, 拦截被指定注解修饰的类的全部方法 |
关于AOP 表达式的详细规则可以查阅Spring文档中的Aspect Oriented Programming with Spring章节
三级缓存结构
如果对象A和对象B循环依赖,且都有代理的话,那创建的顺序就是
- A半成品加入第三级缓存
- A填充属性注入B -> 创建B对象 -> B半成品加入第三级缓存
- B填充属性注入A -> 创建A代理对象,从第三级缓存移除A对象,A代理对象加入第二级缓存(此时A还是半成品,B注入的是A代理对象)
- 创建B代理对象(此时B是完成品) -> 从第三级缓存移除B对象,B代理对象加入第一级缓存
- A半成品注入B代理对象
- 从第二级缓存移除A代理对象,A代理对象加入第一级缓存
参考资料
MVC架构
通常情况下, MVC项目按照如下的结构放置代码
包名 | 注解 | 内容 |
---|---|---|
controller | @Controller | MVC的控制器 |
service | @Service | 业务逻辑有关的代码 |
mapper | @Repository | 数据映射相关的代码 |
entity | N/A | 业务实体类 |
conf | @Configuration | 配置类 |
控制器接受到请求后, 首先完成请求参数的处理, 然后调用service层的方法实现业务逻辑. service层通过mapper层提供的数据库方法完成需要的业务逻辑. mapper层通常由框架实现具体的操作, 因此仅仅需要提供方法声明, 然后使用配置文件配置具体的行为.
关于Spring Web的基础知识 , 还可以参考以下的内容
最后更新: 2023年08月30日 14:44
版权声明:本文为原创文章,转载请注明出处
原始链接: https://lizec.top/2021/09/01/Spring%E7%AC%94%E8%AE%B0%E4%B9%8B%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/