概述
Spring4是一套JAVA的MVC框架,经过一系列的自动化改良,如今变得非常简单易用。Spring4框架的功能很多,有非常多详细的介绍,这里仅仅提纲式的陈列要点,以帮助初学者快速入门。
如在本笔记中发现错误,欢迎指正。
参考资料
《Spring实战》,第四版,人民邮电出版社。
Bean装配
创建应用对象之间协作关系的行为通常称为装配,这也是依赖注入的本质。装配就是将一个符合定义的bean赋予给一个定义的变量。
Spring提供了三种装配方式,这三种方式也可以混合使用:
- 隐式bean发现和自动装配,这是最方便的方式,尽量使用这种方式。
- Java中配置,适用于自动化无法完成的场景。
- XML中配置,适用于Java配置无法完成的场景,一般情况下不会用到。
此外,Spring还提供了两种条件化装配方式:
- 根据环境装配。
- 根据指定条件装配。
隐式bean发现和自动装配
两个步骤完成自动化装配:
- 组件扫描:自动发现bean。
- 自动装配:自动满足bean之间的依赖。
组件扫描
- 在拥有@Configuration注解的配置类中添加@ComponentScan注解,默认会在当前的包和子包中进行扫描。
- 通过传入参数basePackages可以指定需要扫描的基础包,这个参数名可以省略,格式如下:
- "a"
- {"a", "b“}
- basePackages="a"
- basePackages={"a", "b“}
- 为了类型安全,也可以通过传入参数basePackageClasses来指定类,这些类所在的包会作为基础包。为此建议为每个基础包创建一个用来扫描的空标记接口,以避免代码重构时被删除。
bean定义
- 为定义bean的类添加@Component注解,bean的ID默认是类名将第一个字母变成小写,也可以通过给注解的默认参数name传入字符串值来指定ID。
- @Named注解和@Component注解有一些细微的差距,但是大多数时候是可以替换使用的。
自动装配
- 使用@Autowired注解,该注解可以在如下位置使用:
- 成员变量,对该变量进行自动装配。
- 构造器,对构造器的参数进行自动装配。
- 任意的类方法上,对方法的参数进行自动装配。
- 如果没有匹配的bean则会抛出异常,可以通过给注解的参数required传入false值来允许未装配。
- @Inject注解和@Autowired注解有一些细微的差距,但是大多数时候是可以替换使用的。
Java代码装配
在JavaConfig类中可以进行显式的配置,创建JavaConfig类的关键在于要添加@Configuration注解以表明这是一个配置类。
通过@Import注解可以在配置类中导入其他的配置类。
- 为配置类中返回bean的方法添加@Bean注解进行配置,bean的ID即为方法名。
- 通过给注解的name参数传入一个字符串值可以设置一个不同的ID。
XML装配
某些情况下不得不使用xml来进行装配,但是这种场景很少见,这里就略过了。
根据环境装配
通过使用注解@Profile可以指定bean属于哪个环境,没有使用该注解的bean在任何环境下都会被创建。
@Profile注解可以放在如下位置:
- 配置类上,对该类中的所有bean生效。
- 配置类的方法上,对该方法的bean生效。
- Bean类上,对该bean生效。
根据条件装配
- 通过使用@Conditional注解,可以根据指定条件的计算结果来决定是否装配。
- 注解的传入参数为一个实现了Condition接口的类,并且通过接口中的matches方法来进行判断。
- 注解可以放置的位置同@Profile注解。
- @Profile注解本身也使用了@Conditional注解,并且引用了ProfileCondition作为Condition的实现。
处理装配歧义
当有多个满足条件的bean都能进行装配时,Spring会因为不知道该装配哪一个而报错,Spring提供了如下方式来处理歧义:
- 通过在申明bean的时候加上@Primary注解来表明某个bean是首选的,但是有多个加了该注解的bean都满足条件时同样会报错。
- 通过在使用@Autowire注解的时候加上@Qualifier注解来指定希望注入的bean,传入的参数是一个限定符,默认每个bean都会根据其ID创建一个同名限定符。注解可以重复使用。
- 通过在声明bean的时候加上@Qualifier注解来指定该bean的限定符。注解可以重复使用。
- 通过@Qualifier注解来创建自定义的注解,并使用自定义的注解。
Bean作用域
Spring为bean定义了多种作用域,通过在声明bean的同时使用@Scope注解可以指定bean的作用域:
- 单例(Singleton):整个应用中只创建bean的一个实例,这是默认作用域。
- 原型(Prototype):每次注入都会创建一个实例。
- 会话(Session):为web应用中为每个会话创建一个实例。
- 请求(Request):为web应用中每个请求创建一个实例。
环境配置
Spring允许为不同的环境设置不同的环境变量,resource目录下的application.properties文件或者application.yml文件就是用来进行环境和环境变量的配置的。
- 两种方式可以混合使用。
- yml是有序的,properties是无序的。
变量设置
- properties文件采用每行一个key=value的形式来设置变量,用.符号分割key的字段。
- yml文件采用key:value的方式来设置变量,用换行缩进的方式来分割key的字段。
环境配置
- 通过为properties文件加上不同的环境名称来创建不同的环境配置文件,格式为application-{env}.properties。
- 在yml文件中通过---符号来区分不同的环境配置变量,并在相应的代码块中使用spring.profiles变量来设置代码环境名称。
环境激活
- spring.profiles.active属性设置当前激活的环境,可以在配置文件中设置。
- spring.profiles.default属性设置默认激活的环境,在没有设置。spring.profiles.active时生效。这个属性需要在DispatcherServlet中设置。
- 可以同时激活多个环境。
环境变量获取
- 通过定义并自动装配Enviroment类型的bean来获得环境变量。
- 通过@Value注解并传入${a.b}格式的字符串参数来进行装配,这种格式叫做属性占位符。
- 通过@Value注解并传入#{...}格式的字符串参数来进行装配,这种格式叫做SpEL表达式。有关表达式的语法这里先略过。
面向切面编程
在软件开发中,散布于应用中多处的功能被称为横切关注点。把这些横切关注点与业务逻辑分离正是面向切面编程所要解决的问题。
术语理解
- 通知(Advice):通知定义了切面是什么以及何时应用。Spring有五种类型的通知。
- 前置通知(Before):在目标方法被调用之前调用通知功能。
- 后置通知(After):在目标方法完成之后调用通知。
- 返回通知(After-returning):在目标方法成功执行之后调用通知。
- 异常通知(After-throwing):在目标方法抛出异常后调用通知。
- 环绕通知(Around):通知包裹了被通知的方法,自定义方法被调用之前和之后的通知行为。
- 连接点:应用执行过程中能够插入切面的一个点,可以是调用方法时、抛出异常时甚至修改一个字段时。
- 切点:定义会匹配通知所要织入的一个或者多个连接点。
- 切面:通知和切点共同定义了切面的全部内容,是什么、在何时和何处完成其功能。
- 引入:允许我们向现有的类添加新的方法或属性。
- 织入:把切面应用到目标对象并创建新的代理对象的过程,在目标对象的生命周期里有多个点可以进行织入:
- 编译期:需要特殊的编译器,比如AspectJ。
- 类加载期:需要特殊的类加载器。
- 运行期:比如Spring AOP。
Spring对AOP的支持
Spring提供了4中类型的AOP支持,前三种都是Spring AOP实现的变体:
- 基于代理的经典Spring AOP,非常笨重和复杂,可以被注解驱动替代。
- 纯POJO切面,需要XML配置,可以被注解驱动替代。
- @AspectJ注解驱动的切面,Spring只支持方法级别的连接点。
- 注入式AspectJ切面。
注解创建切面
定义切面的语法
image.png- 常见的操作符有&&、||和!。
- 常见的指示器有within、bean和args。
创建切面
- 在JavaConfig类中通过@EnableAspectJAutoProxy注解来启动自动代理功能。
- 通过为类添加@Aspect注解来定义切面类。
- 通过使用通知方法的注解(比如@After),并将定义切面的语法作为参数传入来定义切面方法。
- 通过使用注解@Pointcut来定义可以被重复使用的切点,其他切点就可以用该注解的方法来作为参数。
- 通过@DeclareParents注解可以创建切面代理,允许我们动态的更换切面接口。
- value属性制定了哪种类型的bean要引入接口。
- defaultImpl属性指定了为引入功能实现提供的默认类。
注入AspectJ切面
Spring AOP和AspectJ相比是一个比较弱的解决方案。但对于大部分功能来讲,AspectJ切面与Spring是相互独立的,在应用AspectJ切面时几乎不会涉及到Spring。
具体介绍这里略过。
Spring REST
Spring REST用于开发web应用,是Spring MVC框架实现的功能。
由于现在通用的Web架构已经将前端View和后端Controller分离,而后端普遍采用Restful的方式提供API接口,因此完整的MVC架构已经很少使用了,这里也就不多介绍。
REST基本概念
REST含义
Representational State Transfer,表述性状态转移:
- 表述性:REST资源可以用各种形式来进行表述,包括JSON、XML和HTML。
- 状态:当使用REST的时候,更关注资源的状态而不是对资源采取的行为。
- 转移:REST涉及到转移数据资源,它以某种表述性形式从一个应用转移到另一个应用。
REST动作
REST是通过HTTP方法来定义的,CRUD动作和HTTP方法的匹配关系如下:
- Create:POST。
- Read:GET。
- Update:PUT或PATCH。
- Delete:DELETE。
REST创建
控制器类
- 为控制器类添加@RestController注解来申明这是一个控制器类,该注解基于@Component注解,需要放在能被扫描到的地方。
- 为控制器类添加@RequestMapping注解来为该类中所有控制器方法添加匹配路径的前缀,参数如下:
- value:默认传入的参数,用字符串或者字符串数组表示匹配的路径。
控制器方法
- 为控制器方法添加@RequestMapping注解来创建控制器,参数如下:
- value:默认传入的参数,用字符串或者字符串数组表示匹配的路径,路径中可以使用{}来创建路径参数占位符。
- method:HTTP方法,默认为GET。
- 为控制器方法的参数添加@RequestParam注解表明这是一个查询参数,参数如下:
- value:默认传入的参数,用字符串表示查询参数的名称。
- required:表示该参数是否必须存在,默认为true。
- defaultValue:没有传入查询参数时,该参数的默认值。
- 为参数添加@PathVariable注解表明这是一个路径参数,需要将路径占位符名称作为参数传入注解。
- 为参数添加@RequestBody注解表明这是一个需要进行消息转换的参数,Spring会根据请求头选择合适的消息转换器。
- 控制器方法默认返回的状态码是200,可以参考异常处理的方法来修改这个值,但是不建议在这里进行修改而应该采用异常处理的方式。
异常处理
异常类
默认情况下,Spring会将自身的一些异常自动转换为合适的状态码。
通过为自定义异常类添加@ResponseStatus注解,可以为其添加状态码和返回消息,参数如下:
- value:HTTP状态码。
- reason:返回的字符串消息。
异常处理方法
- 在控制器类中可以为该类的控制器编写异常处理方法,通过添加@ExceptionHandler注解并传入异常类作为参数来声明该方法处理的异常类型。
- 通过将方法的返回类型指定为ResponseEntity<T>,可以对Response的详细内容进行配置,比如状态吗、内容、头信息。
异常处理类
- 通过为类添加@ControllerAdvice注解来表明该类是一个全局的异常处理类。
- 通过为异常处理类的方法添加@ExceptionHandler注解并传入异常类作为参数来声明该方法处理的异常类型。
校验器
当控制器方法的传入参数是一个Model类型时,可以为该参数添加注解进行校验:
- @Valid:按照规则对传入模型进行校验,如果不满足则会生成一个Error类型的实例赋予名为error的传入参数,该参数需要在方法的传入参数中声明。
- @RequestBody:按照规则对传入模型进行校验,如果不满足则不会匹配该方法。
校验规则在Model类中进行定义,方法是为属性添加注解,列举几个常用注解:
- @Null:必须为空。
- @Max:必须是数字,并且它的值要小于等于给定值。
- @Future:必须是一个将来的日期。
- @AssertTrue:必须是boolean型,并且值为true。
REST客户端
Spring提供了一个RestTemplate作为REST客户端模板,使用它可以方便的发送restful的请求。
Spring Security
Spring security是一个RBAC认证框架。不过默认角色和权限不是一对多的关系而是两个独立的概念,如果需要实现通常理解的模型,需要定制化开发。
配置Security
启用
- 必须配置在一个实现了WebSecurityConfigurer的bean中,或者扩展WebSecurityConfigurerAdapter。
- 在JavaConfig类上添加@EnableWebSecurity注解来启用功能。
配置
WebSecurityConfigurerAdapter有三个configure方法可以重载用来配置安全性:
- configure(WebSecurity):配置Filter链。
- configure(HttpSecurity):配置如何通过拦截器保护请求。
- antMatchers()定义拦截路径。
- access()等方法来定义拦截规则。
- and()方法连接配置。
- requiresChannel()方法强制HTTPS。
- configure(AuthenticationManagerBuilder):配置user-detail服务。
- 基于内存用户认证。
- 基于数据库表认证。
- 基于LDAP认证。
- 重写UserDetailsService实现自定义认证。
方法保护
Security还可以用来保护方法,通过在JavaConfig类上添加@EnableGlobalMethodSecurity注解来启用该功能。
Security提供了三种不同的安全注解:
- Secutiry自带的@Secured注解,可以基于权限进行保护。
- JSR-250的@RolesAllowed注解,需要向@EnableGlobalMethodSecurity注解传入参数jsr250Enabled=true来启动,使用上和@Secured注解类似。
- 表达式驱动的注解,包括@PreAuthorize、@PostAuthorize、@PreFilter和@PostFilter,需要向@EnableGlobalMethodSecurity注解传入参数prePostEnabled=true参数来启用,这种注解可以传入SpEL表达式来对参数进行验证,以及对输入输出进行过滤。
关系型数据库
Spring访问关系型数据库的便利:
- 提供了用于数据访问的模板,可以将注意力放在执行语句上。
- 提供了多样而详细的异常定义,能够更加精确地表述错误。
配置数据源
- JDBC驱动程序定义数据源。
- DriverManagerDataSource:在每个连接请求时都会返回一个新建的连接。
- SimpleDriverDataSource:与DriverManagerDataSource工作方式类似,但是它直接使用JDBC驱动,来解决特定环境下的类加载问题。
- SingleConnectionDataSource:在每个连接请求时都会返回同一个连接。
- JNDI查找的数据源,好处是数据源可以在应用程序之外进行管理。
- 连接池数据源,Spring并没有提供实现,需要依赖开源实现,配置方式与JDBC驱动的配置相似。
- Apache Commons DBCP。
- c3p0。
- BoneCP。
- 嵌入式数据源,比如H2数据库。
JDBC模板
- JdbcTemplate:最基本的JDBC模板,这个模板支持简单的JDBC数据库访问功能以及基于索引参数的查询。该模板通过使用自定义的RowMapper来映射数据库字段到Java模型类中。
- NamedParameterJdbcTemplate:使用该模板类执行查询时可以将值以命名参数的形式绑定到SQL中,而不是使用简单的索引参数。
- SimpleJdbcTemplate:该模板类利用Java5的一些特性如自动装箱、泛型以及可变参数列表来简化JDBC模板的使用。
缓存
Spring提供了多种缓存管理器的实现,具体选择哪一种取决于想要使用的底层缓存供应商。
通过在JavaConfig类上添加@EnableCaching注解来启用缓存,然后在类中配置缓存管理器、缓存管理器工厂和模板。
使用缓存
如下注解可以放在方法或者类上:
- @Cacheable:如果有缓存则返回缓存,否则执行并且添加缓存。参数如下:
- value:缓存名称。
- condition:SpEL表达式,执行结果为true才应用缓存。
- key:SpEL表达式,计算缓存的key,可以根据传入或者返回参数来自定义。
- unless:SpEL表达式,执行结果为true则不存缓存。
- @CachePut:方法使用都会被调用,并且添加缓存。参数类似@Cacheable注解。
- @CacheEvict:清除缓存。
- @Caching:分组注解,能够同时应用多个其他注解。
一些省略的内容
- 视图渲染:因为现在通用的架构已经实现了前后端分离,所以前端使用流行的框架是更合理的选择,而无需在后端使用视图了。
- NoSql数据库:不同的NoSql数据库的设计和操作都不尽相同,但总归就是Spring提供一个模板,然后操作即可。
- RPC和WebSocket:现在已经不流行这种方式了,可以尝试一下Spring Cloud微服务框架。
- 消息机制:一般的后端开发较少使用,微服务框架中会比较常用。
网友评论