Spring
给容器IOC中注入组件
控制层
@Controller
业务层
@Service
数据访问层
@Repository
通用
@Component:泛指各种组件,@Controller、@Service、@Repository都可以称为@Component,如果组件没有明确分层,可用@Component。
给属性注入IOC容器中的bean
@Autowired:由bean提供,@Autowired可以作用在变量、setter方法、构造函数上;@Autowired有个属性为required,可以配置为false。
@Resource:由JSR-250提供。
@Inject:由JSR-330提供,@Inject用法和@Autowired一样。
@Primary:让spring进行自动装配的时候,默认使用首选的bean,和@Qualifier一个效果。
@Autowired、@Inject是默认按照类型匹配的,@Resource是按照名称匹配的,@Autowired如果需要按照名称匹配需要和@Qualifier一起使用,@Inject和@Name一起使用。
用于JSON处理的注解
@JsonIgnore:(1)作用:在json序列化时将java bean中的一些属性忽略掉,序列化和反序列化都受影响。(2)使用方法:一般标记在属性或者方法上(最好是在属性上),返回的json数据即不包含该属性。(3)注解失效:如果注解失效,可能是因为你使用的是fastJson,尝试使用对应的注解来忽略字段,注解为:@JSONField(serialize = false),使用方法一样。
//生成json 时不生成age 属性
public class user {
private String name;
@JsonIgnore
private int age;
}
@JsonIgnoreProperties:此注解是类注解,作用是json序列化时将java bean中的一些属性忽略掉,序列化和反序列化都受影响。写法将此标签加在user类的类名上 ,可以多个属性也可以单个属性。
//生成json时将name和age属性过滤
@JsonIgnoreProperties({"name"},{"age"})
public class user {
private String name;
private int age;
}
@JsonFormat:此注解用于属性或者方法上(最好是属性上),可以方便的把Date类型直接转化为我们想要的模式,比如:
public class User{
@JsonFormat(pattern = “yyyy-MM-dd HH-mm-ss”)
private Date date;
}
@JsonSerialize:此注解用于属性或者getter方法上,用于在序列化时嵌入我们自定义的代码,比如序列化一个double时在其后面限制两位小数点。
@JsonDeserialize:此注解用于属性或者setter方法上,用于在反序列化时可以嵌入我们自定义的代码,类似于上面的@JsonSerialize
@Transient:如果一个属性并非数据库表的字段映射,就务必将其标示为@Transient,否则ORM框架默认其注解为@Basic;
@JsonIgnoreType:标注在类上,当其他类有该类作为属性时,该属性将被忽略。
@JsonProperty:@JsonProperty 可以指定某个属性和json映射的名称。例如我们有个json字符串为{“user_name”:”aaa”},
而java中命名要遵循驼峰规则,则为userName,这时通过@JsonProperty 注解来指定两者的映射规则即可。这个注解也比较常用。
public class SomeEntity {
@JsonProperty("user_name")
private String userName;
}
只在序列化情况下生效的注解
@JsonPropertyOrder:在将 java pojo 对象序列化成为 json 字符串时,使用 @JsonPropertyOrder 可以指定属性在 json 字符串中的顺序。
@JsonInclude:在将 java pojo 对象序列化成为 json 字符串时,使用 @JsonInclude 注解可以控制在哪些情况下才将被注解的属性转换成 json,例如只有属性不为 null 时。
@JsonInclude(JsonInclude.Include.NON_NULL):这个注解放在类头上,返给前端的json里就没有null类型的字段,即实体类与json互转的时候 属性值为null的不参与序列化。
另外还有很多其它的范围,例如 NON_EMPTY、NON_DEFAULT等
在反序列化情况下生效的注解
@JsonSetter:@JsonSetter 标注于 setter 方法上,类似 @JsonProperty ,也可以解决 json 键名称和 java pojo 字段名称不匹配的问题。
配置类相关注解
@Configuration:声明当前类为配置类。
@Bean:注解在方法上,声明当前方法的返回值为一个bean,替代xml中的方式。
@ComponentScan:用于对Component进行扫描。
@EnableWebMvc:在配置类中开启Web MVC的配置支持。
切面(AOP)相关注解
Spring支持AspectJ的注解式切面编程。
@Aspect:声明一个切面。
@After:在方法执行之后执行(方法上)。
@Before:在方法执行之前执行(方法上)。
@Around:在方法执行之前与之后执行(方法上)。
@PointCu:声明切点。
在java配置类中使用@EnableAspectJAutoProxy注解开启Spring对AspectJ代理的支持
@Value注解
(1)支持如下方式的注入:
注入普通字符
注入操作系统属性
注入表达式结果
注入其它bean属性
注入文件资源
注入网站资源
注入配置文件
(2)@Value三种情况的用法。
${}
是去找外部配置的参数,将值赋过来
#{}
是SpEL表达式,去寻找对应变量的内容
#{}
直接写字符串就是将字符串的值注入进去
异步相关
@EnableAsync:配置类中通过此注解开启对异步任务的支持。
@Async:在实际执行的bean方法使用该注解来声明其是一个异步任务(方法上或类上所有的方法都将异步,需要@EnableAsync开启异步任务)。
定时任务相关
@EnableScheduling:在配置类上使用,开启计划任务的支持(类上)。
@Scheduled:来申明这是一个任务,包括cron,fixDelay,fixRate等类型(方法上,需先开启计划任务的支持)。
Contoller层接口相关注解
@RequestMapping:用于映射web请求,包括访问路径和参数。
@ResponseBody:支持将返回值放到response内,而不是一个页面,通常用户返回json数据。
@RequestBody:允许request的参数在request体中,而不是在直接连接的地址后面。(放在参数前)
@PathVariable:用于接收路径参数,比如@RequestMapping(“/hello/{name}”)声明的路径,将注解放在参数前,即可获取该值,通常作为Restful的接口实现方法。
@RestController:该注解为一个组合注解,相当于@Controller和@ResponseBody的组合,注解在类上,意味着,该Controller的所有方法都默认加上了@ResponseBody。
@ModelAttribute:(1)@ModelAttribute注释方法:如果把@ModelAttribute放在方法的注解上时,代表的是:该Controller的所有方法在调用前,先执行此@ModelAttribute方法。可以把这个@ModelAttribute特性,应用在BaseController当中,所有的Controller继承BaseController,即可实现在调用Controller时,先执行@ModelAttribute方法。比如权限的验证(也可以使用Interceptor)等。(2)@ModelAttribute注释一个方法的参数:当作为方法的参数使用,指示的参数应该从模型中检索。如果不存在,它应该首先实例化,然后添加到模型中,一旦出现在模型中,参数字段应该从具有匹配名称的所有请求参数中填充。hellorWord方法的userLogin参数的值来源于getUserLogin()方法中的model属性。
事务
事务是指逻辑上的一组操作在执行时,组成这组操作的各个单元,要不全部执行成功,要不全部都不执行成功。事务的基本要素为:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。其中,原子性(Atomicity): 事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位。一致性(Consistency): 事务开始前和结束后,数据库的完整性约束没有被破坏。比如A向B转账,不可能A扣了钱,B却没收到。隔离性(Isolation): 同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。持久性(Durability): 事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。
Spring事务属性对应TransactionDefinition类里面的各个方法。TransactionDefinition类方法如下所示:
public interface TransactionDefinition {
/**
* 返回事务传播行为
*/
int getPropagationBehavior();
/**
* 返回事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据
*/
int getIsolationLevel();
/**
* 事务超时时间,事务必须在多少秒之内完成
*/
int getTimeout();
/**
* 事务是否只读,事务管理器能够根据这个返回值进行优化,确保事务是只读的
*/
boolean isReadOnly();
/**
* 事务名字
*/
@Nullable
String getName();
}
事务属性可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上。事务属性包含了5个方面:传播行为、隔离规则、回滚规则、事务超时、是否只读。事务的产生需要依赖这些事务属性。包括我们下面要讲到的@Transactional注解的属性其实就是在设置这些值。
传播行为:当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。Spring定义了七种传播行为:
(1)TransactionDefinition.PROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务,则加入到这个事务中。这是最常见的选择。
(2)TransactionDefinition.PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。
(3)TransactionDefinition.PROPAGATION_MANDATORY 表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常.
(4)TransactionDefinition.PROPAGATION_REQUIRED_NEW 表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。
(5)TransactionDefinition.PROPAGATION_NOT_SUPPORTED 表示该方法不应该运行在事务中。如果当前存在事务,就把当前事务挂起。
(6)TransactionDefinition.PROPAGATION_NEVER 表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常。
(7)TransactionDefinition.PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
隔离规则:隔离级别定义了一个事务可能受其他并发事务影响的程度。在实际开发过程中,我们绝大部分的事务都是有并发情况。下多个事务并发运行,经常会操作相同的数据来完成各自的任务。在这种情况下可能会导致以下的问题:
(1)脏读(Dirty reads)—— 事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据。
(2)不可重复读(Nonrepeatable read)—— 事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致。
(3)幻读(Phantom read)—— 系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。
咱们已经知道了在并发状态下可能产生: 脏读、不可重复读、幻读的情况。因此我们需要将事务与事务之间隔离。根据隔离的方式来避免事务并发状态下脏读、不可重复读、幻读的产生。Spring中定义了五种隔离规则:
(1)TransactionDefinition.ISOLATION_DEFAULT 使用后端数据库默认的隔离级别。
(2)TransactionDefinition.ISOLATION_READ_UNCOMMITTED 允许读取尚未提交的数据变更(最低的隔离级别)。(脏读、不可重复读、幻读)
(3)TransactionDefinition.ISOLATION_READ_COMMITTED 允许读取并发事务已经提交的数据。(不可重复读、幻读)
(4)TransactionDefinition.ISOLATION_REPEATABLE_READ 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改。(幻读)
(5)TransactionDefinition.ISOLATION_SERIALIZABLE 最高的隔离级别,完全服从ACID的隔离级别,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的。(没有这三种问题)
ISOLATION_SERIALIZABLE 隔离规则类型在开发中很少用到。举个很简单的例子。咱们使用了ISOLATION_SERIALIZABLE规则。A,B两个事务操作同一个数据表并发过来了。A先执行。A事务这个时候会把表给锁住,B事务执行的时候直接报错。
补充:(1)事务隔离级别为ISOLATION_READ_UNCOMMITTED时,写数据只会锁住相应的行。(2)事务隔离级别为可ISOLATION_REPEATABLE_READ时,如果检索条件有索引(包括主键索引)的时候,默认加锁方式是next-key锁;如果检索条件没有索引,更新数据时会锁住整张表。一个间隙被事务加了锁,其他事务是不能在这个间隙插入记录的,这样可以防止幻读。(3)事务隔离级别为ISOLATION_SERIALIZABLE时,读写数据都会锁住整张表。(4)隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也就越大。
回滚规则:事务回滚规则定义了哪些异常会导致事务回滚而哪些不会。默认情况下,只有未检查异常(RuntimeException和Error类型的异常)会导致事务回滚。而在遇到检查型异常时不会回滚。 但是你可以声明事务在遇到特定的检查型异常时像遇到运行期异常那样回滚。同样,你还可以声明事务遇到特定的异常不回滚,即使这些异常是运行期异常。
事务超时:为了使应用程序很好地运行,事务不能运行太长的时间。因为事务可能涉及对后端数据库的锁定,也会占用数据库资源。事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待其结束。
是否只读:如果在一个事务中所有关于数据库的操作都是只读的,也就是说,这些操作只读取数据库中的数据,而并不更新数据, 这个时候我们应该给该事务设置只读属性,这样可以帮助数据库引擎优化事务。提升效率。
@Transactional使用:Spring 为事务管理提供了丰富的功能支持。Spring 事务管理分为编码式和声明式的两种方式:(1)编程式事务:允许用户在代码中精确定义事务的边界。编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。(2)声明式事务: 基于AOP,有助于用户将操作与事务规则进行解耦。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务管理也有两种常用的方式,一种是在配置文件(xml)中做相关的事务规则声明,另一种是基于@Transactional注解的方式。显然基于注解的方式更简单易用,更清爽。@Transactional注解的使用也是我们本文着重要理解的部分。显然声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。和编程式事务相比,声明式事务唯一不足地方是,后者的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。
@Transactional介绍:@Transactional注解 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。虽然@Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional注解应该只被应用到 public 方法上,这是由Spring AOP的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。
@Transactional注解属性:@Transactional注解里面的各个属性和咱们在上面讲的事务属性里面是一一对应的。用来设置事务的传播行为、隔离规则、回滚规则、事务超时、是否只读。
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
/**
* 当在配置文件中有多个 TransactionManager , 可以用该属性指定选择哪个事务管理器。
*/
@AliasFor("transactionManager")
String value() default "";
/**
* 同上。
*/
@AliasFor("value")
String transactionManager() default "";
/**
* 事务的传播行为,默认值为 REQUIRED。
*/
Propagation propagation() default Propagation.REQUIRED;
/**
* 事务的隔离规则,默认值采用 DEFAULT。
*/
Isolation isolation() default Isolation.DEFAULT;
/**
* 事务超时时间。
*/
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
/**
* 是否只读事务
*/
boolean readOnly() default false;
/**
* 用于指定能够触发事务回滚的异常类型。
*/
Class<? extends Throwable>[] rollbackFor() default {};
/**
* 同上,指定类名。
*/
String[] rollbackForClassName() default {};
/**
* 用于指定不会触发事务回滚的异常类型
*/
Class<? extends Throwable>[] noRollbackFor() default {};
/**
* 同上,指定类名
*/
String[] noRollbackForClassName() default {};
}
value、transactionManager属性:它们两个是一样的意思。当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器。大多数项目只需要一个事务管理器。然而,有些项目为了提高效率、或者有多个完全不同又不相干的数据源,从而使用了多个事务管理器。机智的Spring的Transactional管理已经考虑到了这一点,首先定义多个transactional manager,并为qualifier属性指定不同的值;然后在需要使用@Transactional注解的时候指定TransactionManager的qualifier属性值或者直接使用bean名称。配置和代码使用的例子:
<tx:annotation-driven/>
<bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource1"></property>
<qualifier value="datasource1Tx"/>
</bean>
<bean id="transactionManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource2"></property>
<qualifier value="datasource2Tx"/>
</bean>
public class TransactionalService {
@Transactional("datasource1Tx")
public void setSomethingInDatasource1() { ... }
@Transactional("datasource2Tx")
public void doSomethingInDatasource2() { ... }
}
propagation属性:propagation用于指定事务的传播行为,默认值为 REQUIRED。propagation有七种类型,就是我们在上文中讲到的事务属性传播行为的七种方式,如下所示:
REQUIRED TransactionDefinition.PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务,则加入到这个事务中。这是最常见的选择。
SUPPORTS TransactionDefinition.PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。
MANDATORY TransactionDefinition.PROPAGATION_MANDATORY 表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常。
REQUIRES_NEW TransactionDefinition.PROPAGATION_REQUIRES_NEW 表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。
NOT_SUPPORTED TransactionDefinition.PROPAGATION_NOT_SUPPORTED 表示该方法不应该运行在事务中。如果当前存在事务,就把当前事务挂起。
NEVER TransactionDefinition.PROPAGATION_NEVER 表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常。
NESTED TransactionDefinition.PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
isolation属性:isolation用于指定事务的隔离规则,默认值为DEFAULT。@Transactional的隔离规则和上文事务属性里面的隔离规则也是一一对应的。总共五种隔离规则,如下所示:
DEFAULT TransactionDefinition.ISOLATION_DEFAULT 使用后端数据库默认的隔离级别
READ_UNCOMMITTED TransactionDefinition.ISOLATION_READ_UNCOMMITTED 允许读取尚未提交的数据变更(最低的隔离级别) 。(脏读 不可重复读 幻读)
READ_COMMITTED TransactionDefinition.ISOLATION_READ_COMMITTED 允许读取并发事务已经提交的数据。(不可重复读 幻读)
REPEATABLE_READ TransactionDefinition.ISOLATION_REPEATABLE_READ 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改。(幻读)
SERIALIZABLE TransactionDefinition.ISOLATION_SERIALIZABLE 最高的隔离级别,完全服从ACID的隔离级别,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的。(没有这三个问题)
timeout属性:timeout用于设置事务的超时属性。
readOnly属性:readOnly用于设置事务是否只读属性。
rollbackFor、rollbackForClassName、noRollbackFor、noRollbackForClassName:rollbackFor、rollbackForClassName用于设置那些异常需要回滚;noRollbackFor、noRollbackForClassName用于设置那些异常不需要回滚。他们就是在设置事务的回滚规则。
@Transactional注解的使用:(1)@Transactional注解的使用关键点在理解@Transactional注解里面各个参数的含义。这个咱们在上面已经对@Transactional注解参数的各个含义做了一个简单的介绍。接下来,咱们着重讲一讲@Transactional注解使用过程中一些注意的点。(2) @Transactional注解内部实现依赖于Spring AOP编程。而AOP在默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为。
@Transactional 注解尽量直接加在方法上:为什么:因为@Transactional直接加在类或者接口上,@Transactional注解会对类或者接口里面所有的public方法都有效(相当于所有的public方法都加上了@Transactional注解,而且注解带的参数都是一样的)。第一影响性能,可能有些方法我不需要@Transactional注解,第二方法不同可能@Transactional注解需要配置的参数也不同,比如有一个方法只是做查询操作,那咱们可能需要配置Transactional注解的readOnly参数。所以强烈建议@Transactional注解直接添加的需要的方法上。
@Transactional 注解必须添加在public方法上,private、protected方法上是无效的:在使用@Transactional 的时候一定要记住,在private,protected方法上添加@Transactional 注解不会有任何效果。相当于没加一样。即使外部能调到protected的方法也无效。和没有添加@Transactional一样。
函数之间相互调用:关于有@Transactional的函数之间调用,会产生什么情况。这里咱们通过几个例子来说明。
https://www.jianshu.com/p/befc2d73e487