Spring使用手册

一、序言

Spring Spring 系列全家桶的核心与基础,熟练掌握 Spring 基础对后续学习 SpringBootSpringCloudSpring 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 的行锁是针对索引加的锁,不是针对记录加的锁。并且该索引不能失效,否则都会从行锁升级为表锁。

对于UPDATEDELETEINSERT语句,InnoDB 会自动给涉及数据加排他锁;对于普通 SELECT 语句,InnoDB 不会加任何锁,显示加锁除外。

三、动态代理

JDK 动态代理:利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用 InvokeHandler 来处理。

CGlib 动态代理:利用 ASM 开源包,将代理对象类的 class 文件加载进来,通过修改其字节码生成子类来处理。

区别:JDK 代理只能对实现接口的类生成代理;CGlib 是针对类实现代理,对指定的类生成一个子类,并覆盖其中的方法,这种通过继承类的实现方式,不能代理 final 修饰的类。

1、静态代理

静态代理类图如下所示:真实类和代理类实现相同的接口,真实类以组合的方式成为代理类的成员变量,通过构造器或者 set 方法将真实类注入代理类。代理类在实现共同接口时,可以在真实类方法执行前、后加上额外的修饰。

// 实例化代理对象

Subject subject = new ProxySubject(new RealSubject());

// 调用实现接口方法(真实类和代理类服用方法,客户端无感,透明实现代理)

subject.request();

(一)JDK 动态代理

若想使用 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();

    }

}

3InvocationHandler 实现类

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 动态代理

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高级架构资料、大厂面试试题、视频

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值