Spring 是 Spring 系列全家桶的核心与基础,熟练掌握 Spring 基础对后续学习 SpringBoot、SpringCloud、Spring Data 等大有裨益。
IoC 和 AOP 是 Spring 两大核心能力,玩转 Spring 高级特性,必须熟练掌握 AOP,理解 AOP 首先要理解动态代理。基于 JDK 的动态代理相对容易理解,但应用受限;CGlib 动态代理比较灵活,因此应用较为广泛。
隔离性(Isolation)和传播性(Propagation)是事务的两大基础特性。
隔离级别 | 脏读 | 可重复读 | 幻读 |
READ_UNCOMMITTED | 是 | 是 | 是 |
READ_COMMITTED | 否 | 是 | 是 |
REPEATABLE_READ | 否 | 否 | 是 |
SERIALIZABLE | 否 | 否 | 否 |
读未提交:两个并发未提交的事务,能够互相读取到对方未提交的数据。
读已提交:两个并发的事务,一个未提交的事务读取到另一个已提交事务的数据。
可重复读:两个并发的事务,一个未提交的事务重复读取数据是相同的,尽管在此期间另一个事务已经并发提交完事务。
序列化:串型执行。
幻读:两个并发事务,一个批量修改数据,另一个增加数据,批量修改数据的事务(特殊情况下)存在未能修改数据的记录行。脏读是指两个及以上并发事务修改同一条记录;幻读是指一个事务批量修改,另一个事务增加记录。
Mysql 数据库 InnoDB 引擎,使用注解@Transactional()或者@Transactional(isolation = Isolation.DEFAULT)标注的默认隔离等级为REPEATABLE_READ。
事务传播行为类型 | 说明 |
REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。 |
SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 |
REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
NESTED | 如果当前存在事务,则在嵌套事务内执行。 |
Spring 中注解@Transactional()和@Transactional(propagation = Propagation.REQUIRED)是默认传播行为,表示方法需要在事务环境下运行,调用方法有事务的话被调用方法就加入,没有就新开事务。
注解@Transactional()和@Transactional(rollbackFor = RuntimeException.class)表示程序在运行异常时回滚。
注意:在事务中不要捕获异常,否则会出现发生运行异常事务不回滚的情况。
表锁虽然开销小,锁表快,但高并发下性能低。行锁虽然开销大,锁表慢,但高并发下相比之下性能更高。事务和行锁都是在确保数据准确的基础上提高并发的处理能力。
多事务操作同一行数据时,后来的事务处于阻塞等待状态,避免了脏读等数据一致性的问题。后来的事务仍然可以操作其他行数据,改善了表锁高并发场景下性能低的问题。
InnoDB 的行锁是针对索引加的锁,不是针对记录加的锁。并且该索引不能失效,否则都会从行锁升级为表锁。
对于UPDATE、DELETE和INSERT语句,InnoDB 会自动给涉及数据加排他锁;对于普通 SELECT 语句,InnoDB 不会加任何锁,显示加锁除外。
JDK 动态代理:利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用 InvokeHandler 来处理。
CGlib 动态代理:利用 ASM 开源包,将代理对象类的 class 文件加载进来,通过修改其字节码生成子类来处理。
区别:JDK 代理只能对实现接口的类生成代理;CGlib 是针对类实现代理,对指定的类生成一个子类,并覆盖其中的方法,这种通过继承类的实现方式,不能代理 final 修饰的类。
静态代理类图如下所示:真实类和代理类实现相同的接口,真实类以组合的方式成为代理类的成员变量,通过构造器或者 set 方法将真实类注入代理类。代理类在实现共同接口时,可以在真实类方法执行前、后加上额外的修饰。
// 实例化代理对象
Subject subject = new ProxySubject(new RealSubject());
// 调用实现接口方法(真实类和代理类服用方法,客户端无感,透明实现代理)
subject.request();
若想使用 JDK 动态代理,被代理类必须实现接口!JDK 动态代理与静态代理比较类似,增加了 JDK 级别的通用类支持。
(1)共同接口
public interface Subject {
// 无参方法调用
void request();
// 有参方法调用
String request(String name);
}
(2)真实类
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("啦啦啦,无参数方法调用,无返回值,用动态代理调用了就好");
}
@Override
public String request(String name) {
Map<String, Object> map = new HashMap<>();
map.put("key", name);
map.put("value", System.currentTimeMillis());
System.out.println(map);
return map.toString();
}
}
(3)InvocationHandler 实现类
public class InvocationHandlerImpl implements InvocationHandler {
private Subject subject;
public InvocationHandlerImpl(Subject subject) {
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("【模拟AOP注解缓存】:在调用之前,先检查是否有缓存,有就直接返回,没有就继续执行方法。");
Object returnValue = method.invoke(subject, args);
System.out.println("【模拟AOP注解缓存】:在调用之后,将方法执行结果存入缓存。");
return returnValue;
}
}
(4)客户端
public class Client {
@Test
void test02() {
RealSubject realSubject = new RealSubject();
InvocationHandler handler = new InvocationHandlerImpl(realSubject);
ClassLoader loader = realSubject.getClass().getClassLoader();
Class[] interfaces = realSubject.getClass().getInterfaces();
/**
* 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
*/
Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);
String returnValue = subject.request("john");
System.out.println("返回值:" + returnValue);
}
}
JDK 实现动态代理的核心是
Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);
CGLIB 相比于 JDK 动态代理更加强大,JDK 动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么 Java 动态代理就没法使用。
(1)添加依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
(2)被代理实体类
public class RealSubject {
public void request() {
System.out.println("啦啦啦,无参数方法调用,无返回值,用动态代理调用了就好");
}
public String request(String name) {
System.out.println("name = " + name);
return name;
}
}
(3)客户端调用
public class Client {
@Test
public void test01(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealSubject.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
System.out.println("before method run...");
Object result = proxy.invokeSuper(obj, args);
System.out.println("after method run...");
return result;
});
RealSubject realSubject = (RealSubject) enhancer.create();
realSubject.request("john");
}
}
看完如果有帮助,希望可以给个 三连 ,你的鼓励就是我不断前进的动力。谢谢
关注我:私信获取Java高级架构资料、大厂面试试题、视频