最近项目中发现一个好玩的事情:本地调试时,返回的date是日期格式yyyy-MM-dd HH:mm:ss,但发布到服务器后就变为Long型的时间戳了
最后查到的原因很简单,因为发布服务器启动时的脚本加下如下的参数,而本地没有加
java -Djava.security.egd=file:/dev/urandom -Dspring.jackson.serialization.write-dates-as-timestamps=true -jar -Xms2048m -Xmx2048m xxx.jar
就是因为加了这个spring参数,才会使date类型都转变为Long型(除了代码中加如下注解的Date型)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
public Date getCreateDate() {
return createDate;
}
当然,本地如果也想返回Long型时间戳,可以在yml中加上如下配置
spring.jackson.serialization.write-dates-as-timestamps: true
但在调查过程中顺便把Spring的一些地方给了解了一遍,总结如下几点
1. 有关注解
比如@RestController
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Controller @ResponseBody public @interface RestController { /** * The value may indicate a suggestion for a logical component name, * to be turned into a Spring bean in case of an autodetected component. * @return the suggested component name, if any * @since 4.0.1 */ String value() default ""; }
回顾下这些常用的注解
1)元注解
@Document:是否编入Javadoc
@Target:使用范围(TYPE、FIELD、METHOD、PARAMETER、CONSTRUCTOR、PACKAGE等)
@Retention:保留范围(SOURCE、CLASS、RUNTIME)
@Inherited:子类继承父类的注解
2)常用注解
1. @Autowired和@Qualifier:后者可以根据名称寻找依赖(常用于对同class的多个bean)
2. 别称:类似@Controller、@Service实质都是Component
3. @RestController = Controller + ResponseBody
4. @Transactional(事务控制的注解,很重要)
于是因为@Transactional想顺便研究下Spring的事务机制...
2. Spring事务
比如@Transactional,Spring Framework 默认使用 AOP 代理,在代码运行时生成一个代理对象,根据@Transactional 的属性配置信息,这个代理对象决定该声明@Transactional 的目标方法是否由拦截器 TransactionInterceptor 来使用拦截,在 TransactionInterceptor 拦截时,会在在目标方法开始执行之前创建并加入事务,并执行目标方法的逻辑, 最后根据执行情况是否出现异常,利用抽象事务管理器(图 2 有相关介绍)AbstractPlatformTransactionManager 操作数据源 DataSource 提交或回滚事务, 如下图所示。
这里涉及到的Spring抽象事务管理器AbstractPlatformTransactionManager,实现的是如下接口
public interface PlatformTransactionManager { TransactionStatus getTransaction( TransactionDefinition definition) throws TransactionException; void commit(TransactionStatus status) throws TransactionException; void rollback(TransactionStatus status) throws TransactionException; }
具体底层处理依赖于AbstractPlatformTransactionManager的实现,比如DataSourceTransactionManager管理JDBC的Connection,这些类的关系如下
@Transactional的使用
- 属性propagation的使用(通常被称为事务的传播,我认为叫做嵌套事务管理更好) 需要注意下面三种 propagation 可以不启动事务。本来期望目标方法进行事务管理,但若是错误的配置这三种 propagation,事务将不会发生回滚。
TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
其他选项:
TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。 - 属性rollbackFor
默认情况出现RuntimeException或者Error才回滚,如果需要定制,则使用rollbackFor(这种情况,如果是自定义异常的父类也会回滚) - 属性timeout:如果超过时间未提交成功,则回滚
- 属性readonly:只读事务(读取期间不会看到中途中其它事务的提交-可重复读)
- 只有在public方法才有用
- 自调用问题可以用AspectJ配置解决
当然,因为这么好用的注解实现又依赖于Spring的AOP,继续...
3. Spring 的AOP
简介如下
AOP(Aspect Orient Programming),作为面向对象编程的一种补充,广泛应用于处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、对象池管理等。
AOP 实现的关键就在于 AOP 框架自动创建的 AOP 代理,AOP 代理则可分为静态代理和动态代理两大类,
静态代理
静态代理是指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强
实现方法:AspectJ框架
示例
动态代理
动态代理则在运行时借助于 JDK 动态代理、CGLIB 等在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强。
运行时生成目标类的代理类,该代理类要么与目标类实现相同的接口,要么是目标类的子类
方法
1. AspectJ Annotation,但底层依然是Spring AOP,比如@Aspect
2. 使用CGLIB生成代理类 Enhancer
3. JDK的动态代理(InvocationHandler)
Spring AOP 代理有 CglibAopProxy 和 JdkDynamicAopProxy 两种
参考:
https://www.jianshu.com/p/e9329c8a59c2
Spring事务:
https://docs.spring.io/spring-framework/docs/4.2.x/spring-framework-reference/html/transaction.html
https://www.ibm.com/developerworks/cn/java/j-master-spring-transactional-use/index.html
Spring的AOP
https://www.ibm.com/developerworks/cn/java/j-lo-springaopcglib/index.html
JDK的动态代理