记录一下 Spring 如何扫描注解的 Bean 与资源

Spring 相关代码分析

本文通过对 Spring 的源代码来理解它是如何扫描 Bean 与资源的,因为自己有一个类似的需求,想把一堆的配置文件丢到 resources 下某个目录中,在程序启动的时候能加载它们。因为文件名是不一定的,所以不能直接指定文件名来加载,通过对 Spring 扫描资源的理解后,可以在自己的代码中手工扫描那些配置文件,以后有任何新的配置文件只需要扔到相应的配置目录即可。

下面以一个最简单的 Spring Boot 项目为例,调试并观察源代码

还是直奔主题吧,不一步一步的去探寻到底是哪个实现类去扫描资源的,用 Google 找到的是 ClassPathScanningCandidateComponentProvider, 因此直接在这个类的敏感位置上打上断点,比如它的构造函数 阅读全文 >>

类别: Spring. 标签: , . 阅读(109). 评论(0) »

Spring 下基于自定义注解拦截方法调用

其实很多年前就做过如此的实验,一翻开自己的日志有关于 aspectj site:unmi.cc, 可以找到  2008 年写的日志。真是流光容易把人抛,红了樱桃,绿了巴蕉。只是那时候 Spring 刚步入 2.0, 才翻开强大 AOP 的篇章,还记得彼时只要是直接使用 AspectJ 就要写  *.aj 文件。而如今 Spring 都到 5.0 了,也就是一年前才重拾起 Spring, 这期间 AspectJ 早就可以不用 *.aj 文件,只需普通 Java 文件,加上 @Aspect 和 @Pointcut 之类的注解就行。

本文内容与几年前写过的日志大体相差不大,再缀上一篇纯粹是个人笔记。这里不以 Spring 5.0 为例,仍然是最新的 4.3.11.RELEASE, 并且直接用 Spring, 而非选择  Spring Boot, 因为用了 Spring Boot 常常搞不清楚哪些是自动配置了的。原生的 Spring 可以使自己掌握一个 Spring AOP 的基本要素。

需求:@LogStartTime 注解的方法,在每次进入该方法时把当前时间写入 ThreadLocal 中去,被 @LogStartTime 注解的方法中随时可以获得进入方法的时间 阅读全文 >>

类别: Spring. 标签: . 阅读(198). 评论(0) »

理解 Spring 定时任务的 fixedRate 和 fixedDelay 的区别

用过  Spring 的 @EnableScheduling 的都知道,我们用三种形式来部署计划任务,即 @Scheduled 注解的 fixedRate(fixedRateString), fixedDelay(fixedDelayString), 以及 cron. cron 不在这里讨论的范畴。我们着重在如何理解 fixedRate 和 fixedDelay 的区别。

在 Spring 的  Scheduled 注解的 JavaDoc 对此的解释很简单

public abstract long fixedRate
Execute the annotated method with a fixed period in milliseconds between invocations.

public abstract long fixedDelay
Execute the annotated method with a fixed period in milliseconds between the end of the last invocation and the start of the next.

只是说是 fixedRate 任务两次执行时间间隔是任务的开始点,而 fixedDelay 的间隔是前次任务的结束与下次任务的开始。

大致用示意字符串来表示如下(每个 T1, 或 T2 代表任务执行秒数(每次任务执行时间不定),假定 fixedRate 或  fixedDelay 的值是 5 秒,用 W 表示等待的数)

fixedRate:    T1.T1WWWT2.T2.T2WW.T3.T3.T3.T3.T3.T4.T4.T4.T4.T4.T4.T4T5T5WWWT6.T6........

fixedDelay:  T1.T1.WWWWW.T2.T2.T2WWWWW.T3.T3.T3.T3.T3.WWWWW.T4.T4.T4.T4.T4.T4.T4.WWWWWT6.T6......

一般来说能理解到上面两个场景已经差不多了,相比而言 fixedDelay 简单些,盯着上一次任务的屁股就行。 阅读全文 >>

类别: Spring. 标签: , . 阅读(2,423). 评论(0) »

Spring 定时任务(Schedule) 和线程

Spring 定时任务实例

Spring 中使用定时任务很简单,只需要  @EnableScheudling 注解启用即可,并不要求是一个 Spring Mvc 的项目。对于一个 Spring Boot 项目,使用定时任务的简单方式如下:

pom.xml 中

Application.java 阅读全文 >>

类别: Spring. 标签: , . 阅读(4,122). 评论(2) »

Spring 项目中把属性或 SQL 语句写在 .xml 文件中

Spring 项目中把大量的 SQL 分散在 Java 代码中,无 Here Doc 的情况下用加号来连接写着实在是不爽,于是之前思考这个 Spring 项目中把 SQL 语句写在 .sql 文件中 --  把它们写在 *.sql 文件中,但是这个 *.sql 需要特定的格式来标识属性 Key

--!select.user
select id, firstname, lastname, address

--!update.user
update ........

而且还需要一个额外的类 SqlPropertySourceFactory 来解析上面的 *.sql 文件, 识别出 select.user 是 Key, 紧接着后面的块是相应的属性值,用注解引用它时还有点额外的 factory 属性来配置,如

@PropertySource(value = "classpath:sql/queries.sql", factory = SqlPropertySourceFactory.class)

所以一直在思考是否能够再简单些,是否能用一个自定义的注解,如

@SqlPropertySource("classpath:sql/queries.sql")

捉摸了很久,似乎有点难度,不过再不断发掘的过程中找到了这个类 org.springframework.core.io.support.PropertiesLoaderUtils, 有下面的代码片断 阅读全文 >>

类别: Spring. 标签: . 阅读(370). 评论(0) »

JdbcTemplate 易被 Java 8 Lambda 带入的坑

Spring 的 JdbcTemplate 为我们操作数据库提供非常大的便利,不需要显式的管理资源和处理异常。在我们进入到了 Java 8 后,JdbcTemplate 方法中的回调函数可以用 Lambda 表达式进行简化,而本文要说的正是这种 Lambda 简化容易给我们带来的一个 Bug, 这是我在一个实际项目中写的单元测试发现的。

下面就是我们的一个样板代码,在我们的 UserRespository 中有一个方法 findAll() 用于获得所有用户:

阅读全文 >>

类别: Java8, Spring. 标签: . 阅读(539). 评论(1) »

Hibernate 映射枚举(Enum) 类型的属性

在数据库中我们一般用整数或字符串来表示枚举值(有些数据库(如 MySQL)本身带有枚举类型), 而在使用 Hibernate 时实体对象中也用 Integer 或 String 来表示枚举就不那么友好了。试想来我们这样定义实体对象的两个属性

@Entity
public class User {
  ....

  public Integer type;  //0: Individual 类型,1: Company 类型
  public String gender;  //可取值 Male 和 Female
}

这样的定义很不严谨,type 和 gender 理论上可取任何值,这会造成表中数据的混乱。其实 Hibernate 在 Java 实体对象中是可以直接用枚举类型与数据库中的整数或字符串映射,需用到 @Enumerated 注解,用法如下: 阅读全文 >>

类别: Hibernate. 标签: . 阅读(1,472). 评论(0) »

SQL Server 和 HSQLDB 中使用 merge into 完成 saveOrUpdate 操作

当我们调用 Hibernate 的  saveOrUpdate() 或 JPA 的 save() 方法的 Hibernate 实现时,都会做两步操作:1)按 ID 查询记录是否已存在,2)不存在插入新记录,存在则更新原记录。这种两步操作其实可以在 SQL Server 和 HSQLDB 中一条语句完成,这就是本文要介绍的 merge into 语句。感觉到用数据库自己的特性,并且一条语句会比 saveOrUpdate() 两步操作性能要好,还需实测。

之所以把 SQL Server 和 HSQLDB 扯到一块来讲,是因为我们在实际项目中的单元测试是基于 HSQLDB 内存数据库的。merge into 如其名所示,它应该是给予我们便利的去根据把一个表中符合条件的记录合并到另一个表中去。我们这里只利用它的这特性去实现类似 Hibernate 的 saveOrUpdate() 操作。

假设我们有一个简单的表

如果指 id 的记录已存在更新原来记录的 name 和  address, 不存在则插入新记录 阅读全文 >>

类别: Database, Hibernate. 标签: , . 阅读(90). 评论(0) »

Spring 如何初始化泛型类实例

在 Java 中对于泛型类型,比如这样简单的类定义

class Processor<T> {}

如果直接初始化时要指定具体类型的话,我们可以这么写

Processor<String> processor = new Processor<>();  //Java 7 及以上版本

Spring 对基本泛型的初始化

如果我们要用 Spring 容器来初始化这个类,比如给上面那个类加个 @Named 注解

@Named
class Processor<T> {
}

这时候我们通过 beanFactory.getBean(Processor.class) 得到的是一个什么样的实例呢?Spring 怎么知道要指定什么具体类型呢?很简单,任何不确定的情况都是 Object。所以通过容器得到的  Processor 实例相当于用下面代码构造出来的

Processor processor = new Processor();  //更准确来讲是 Processor<Object> processor = new Processor<>();

再进一步,对于有上限约束的泛型定义,Spring 才如何应对呢?像 阅读全文 >>

类别: Spring. 标签: , . 阅读(938). 评论(0) »

Spring 项目中把 SQL 语句写在 .sql 文件中

我们在使用 JDBC 时, 如果把所有的 SQL 语句全写在 Java 文件中, 由于 Java 不支持 Here Document, 多行字符串要么用加号, 要么用 Java 8 的 String.join() 方法来连接, 同时不能对 SQL 语句进行语法加亮, 所以这样的 SQL 字符串阅读性很差. 别说为何不用 Hibernate 之类的而不直接写原始的 SQL 语句, 在操作复杂的系统时还是会用到 JdbcTemplate 吧.

所以我们希望能把 SQL 语句写在单独的 *.sql 文件里, 这样很多编辑器就能语法高亮显示, 或在输入时还能得到智能提示. 有种办法是把 *.sql 用作为属性文件, 那么在其中定义多行的 SQL 语句时就得这样

select.user=select id, firstname, lastname, address \
  from users \
  where id=?

加载后就能用 getProperty("select.user") 来引用相应的语句了. 属性文件的换行与 Bash  一样, 也是用  \, 但如此, 则 *.sql 并非一个纯粹的 SQL 文件, 不能正确的进行语法加亮, 一旦写上 SQL 的注释 -- 就更是在添乱了.

所以我们的第二个方案是: 首先 *.sql 就该是一个真正的  SQL 文件, 而不是伪装的属性文件, 为了能在程序中引用每一条 SQL 语句, 我们该如何表示各自的 Key 呢? 这里的灵感仍然是来自于 Linux Shell, 在 Linux Shell 中指定执行环境的用了特殊的注释方式 #!, 如

#!/bin/bash
#!/usr/bin/env python

阅读全文 >>

类别: Spring. 标签: . 阅读(404). 评论(2) »
Page 1 of 1312345...10...Last »