@wxf
2018-03-22T08:05:05.000000Z
字数 6201
阅读 827
面试系列
Spring 在以IOC和AOP为核心的基础上,提供了表现层Spring MVC,业务层事务管理以及持久层Spring JDBC等众多的企业级应用技术。
通过Spring提供的IOC容器,可以将对象之间的依赖关系交由Spring进行管理,避免硬编码所造成的过度依赖。
通过Spring提供的AOP功能,用户可以方便的进行面向切面编程。
IOC(控制反转):就是将原本在程序中手动创建对象的控制权,交给了Spring容器进行统一管理。它的实现原理是反射。
DI(依赖注入):在Spring框架负责创建Bean对象时,动态的将依赖对象注册到Bean组件。
共有三种方式分别是:类构造器实例化(常用)、静态工厂方法实例化及实例工厂方法实例化。
参考文档:Spring实例化Bean的三种方式及Bean的类型
参考资料:Spring Bean的生命周期(非常详细)
参考资料:Spring Bean生命周期
BeanFactory和ApplicationContext都是Spring提供得两种类型的IOC容器实现。
延伸阅读:Spring AOP 的实现机制
1、AOP即面向切面编程,其采用横向抽取机制,取代传统的纵向继承体系的重复性代码(性能监视、事务管理、安全检查、缓存)。它的底层使用动态代理方式实现。
2、AOP利用“横切”技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到 一个可重用模块中。这个模块叫做切面。所谓“切面”,简单来说,就是将那些与业务无关,却被业务模块所共同调用的逻辑封装起来,以减少系统中的重复代码,降低模块间的耦合度。
- 动态代理
在Spring中进行AOP操作,使用的就是Aspectj。Aspectj它不是Spring的一部分,只是和Spring一起使用进行AOP操作。
使用Aspectj实现AOP有两种方式
基于Aspectj的XML配置方式
注意:在实现环绕通知时需要通过ProceedingJoinPoint
对象来调用被增强的方法。代码如下:
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("方法之前......");
// 执行被增强的方法
proceedingJoinPoint.proceed();
System.out.println("方法之前......");
}
基于Aspectj的注解方式
在Spring配置文件中,开启AOP操作。
<!-- 开启AOP操作 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
在增强类中使用注解完成AOP操作
# Aspect注解
@Aspect 在类上使用
# 通知注解
@Before 前置通知,相当于BeforeAdvice
@AfterReturning 后置通知,相当于AfterReturningAdvice
@Around 环绕通知,相当于MethodInterceptor
@AfterThrowing 异常通知,相当于ThrowAdvice
@After最终通知,不管是否异常,该通知都会执行
代码实现
@Aspect
@Component
public class SellerAuthorizeAspect {
@Autowired
private StringRedisTemplate redisTemplate;
@Pointcut("execution(public * com.sell.controller.Seller*.*(..))" +
"&& !execution(public * com.sell.controller.SellerUserController.*(..))")
public void verify(){}
@Before("verify()")
public void doVerify(){
// ...
}
}
事务是指一个逻辑执行单元,在执行时要么全都成功要么全都失败。
不考虑隔离性会产生哪些读的问题
脏读:脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写的数据被回滚了,那么第一个事务获取的数据就是无效的。
不可重复读:不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新。
幻读(虚读):幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录。
如何解决读的问题
通过设置事务隔离级别
隔离级别:
隔离级别 | 含义 |
---|---|
DEFAULT(默认) | 使用后端数据库默认的隔离级别(这是Spring中的一个选项) |
READ_UNCOMMITED(读未提交) | 允许你读取还未提交的改变了的数据。可能导致脏、幻、不可重复读 |
READ_COMMITTED(读已提交) | 允许在并发事务已经提交后读取。可防止脏读,但幻读和 不可重复读仍可发生 |
REPEATABLE_READ(可重复读) | 对相同字段的多次读取是一致的,除非数据被事务本身改变。可防止脏、不可重复读,但幻读仍可能发生。 |
SERIALIZABLE(串行的) | 完全服从ACID的隔离级别,确保不发生脏、幻、不可重复读。这在所有的隔离级别中是最慢的,它是典型的通过完全锁定在事务中涉及的数据表来完成的。 |
MySQL默认采用REPEATABLE_READ(可重复读)隔离级别;Oracle默认采用READ_COMMITTED(读已提交)隔离级别。大多数的数据库系统的默认事务隔离级别都是:READ_COMMITTED。
事务的传播行为
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
常用的事务传播行为:
传播行为 | 说明 | 举例 |
---|---|---|
PROPAGATION_REQUIRED(默认) | 支持当前事务,如果不存在 就新建一个 | 如果A有事务,B使用A的事务,如果A没有事务,B就开启一个新的事务.(A,B是在一个事务中。) |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。 | |
PROPAGATION_MANDATORY | 支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。 | |
PROPAGATION_REQUIRES_NEW | 如果有事务存在,挂起当前事务,创建一个新的事务 | 如果A有事务,B将A的事务挂起,重新创建一个新的事务。(A,B不在一个事务中,事务互不影响。) |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 | |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 | |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |
第二种 声明式事务管理
基于XML配置文件实现(*)
1.配置事务管理器
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" /> <!-- 数据源 -->
</bean>
2.配置事务通知(增强)
<!-- 事务通知(增强) -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 做事务操作 -->
<tx:attributes>
<!-- 为进行事务操作的方法设置匹配规则 -->
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
<!-- ...... -->
</tx:attributes>
</tx:advice>
事务相关的五个属性包括:传播行为、隔离级别、回滚、只读、超时。其XML设置方式:
<tx:method name="方法名规则" propagation="传播行为" isolation="隔离级别" rollback-for="对哪些异常进行回滚" no-rollback-for="对哪些异常不进行回滚" read-only="只读事务" timeout="事务超时限制"/>
3.配置AOP切面产生事务代理
<!-- AOP切面配置 -->
<aop:config>
<!-- 切面(也就是将哪个事务通知应用到哪个切入点上) -->
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.geeker.mmall.manager.service.*.*(..))" />
</aop:config>
基于注解实现
1.配置事务管理器
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" /> <!-- 数据源 -->
</bean>
2.配置事务注解
<!-- 开启注解事务 -->
<tx:annotation-driven transaction-manager="transactionManager">
3.在要使用事务的方法所在类上添加注解
在业务层上添加注解@Transactional
事务相关的五个属性包括:传播行为、隔离级别、回滚、只读、超时。其注解设置方式:
@Transactional(propagation=传播行为, isolation=隔离级别, rollbackForClassName="异常", readOnly=事务只读, timeout=事务超时限制)
Spring事务管理的核心接口关系:
事务管理器PlatformTransactionManager
接口
Spring为不同的持久化框架提供了不同PlatformTransactionManager接口实现。
常用的事务管理器:
事务 | 说明 |
---|---|
org.springframework.jdbc.datasource.DataSourceTransactionManager | 使用Spring JDBC或MyBatis进行持久化数据时使用 |
org.springframework.orm.hibernate5.HibernateTransactionManager | 使用Hibernate5.0版本进行持久化数据时使用 |
org.springframework.orm.jpa.JpaTransactionManager | 使用JPA进行持久化时使用 |
事务属性TransactionDefinition
这个接口定义了一些基本的事务属性,事务属性包括:隔离级别、传播行为、事务超时、只读事务、回滚。
事务具体运行状态TransactionStatus
上面讲到的调用PlatformTransactionManager接口的getTransaction()的方法得到的是TransactionStatus接口的一个实现,这个接口的内容如下:
public interface TransactionStatus{
boolean isNewTransaction(); // 是否是新的事物
boolean hasSavepoint(); // 是否有恢复点
void setRollbackOnly(); // 设置为只回滚
boolean isRollbackOnly(); // 是否为只回滚
boolean isCompleted; // 是否已完成
}
可以发现这个接口描述的就是一些为处理事务提供简单的控制事务执行和查询事务状态的方法,在回滚或提交的时候需要应用对应的事务状态。
参考文档:Spring事务管理(详解+实例)
声明Bean的注解:
@Component : 组件,没有明确的角色
@Controller : 在展现层(MVC--SpringMVC)使用
@Service : 在业务逻辑层(service层)使用
@Repository : 在数据访问层(dao层)使用
注入Bean的常见注解:
@Aautowired : Spring提供的注解
@Resource : JSR-250提供的注解
SpringMVC 常用注解:
@Controller : 注解在类上; 声明这个类是Spring MVC中的Controller,并将其声明为一个Spring的Bean。
@RequestMapping : 可以注解在类上和方法上; 标识请求路径的映射
@ResponseBody :
@RequestBody :
@PathVariable : 用来接收路径参数使用; 注解放置在形参前