【Way to Java】Spring事务注解实现的原理

静态代理

在这里插入图片描述

  • 定义真实对象和代理对象的公共接口
  • 定义真实对象,实现真实业务行为
  • 定义代理对象,包含真实对象,以及真实业务行为的前置后置增强
  • 面向接口编程

但是如果增加代理的功能,代理类需要添加新接口、需要包含新的真实业务类,违反开闭原则

设计模式的几个原则
1、单一职责原则:一个类或者一个接口只负责唯一职责,尽量设计出功能单一的接口;
2、依赖倒转原则:高层模块不应该依赖底层模块的具体实现,解耦高层和底层。既是面向接口编程,当实现发生变化时,只需要提供新的实现类,不需要修改原本高层代码;
3、开放封闭原则:;程序对外扩展开放,对修改关闭,当需求发生变化时,我们可以通过添加新模块来满足新需求,而不是通过修改原本代码来满足新需求;
4、迪米特原则:一个对象应该对其他对象保持最少的了解,尽量降低类与类之间的耦合度;
5、里氏代换原则:所有引用父类的地方必须能透明的使用其子类的对象;
6、接口隔离原则:客户端不应该依赖它不需要的接口,一个类对另一个类的依赖应该建立在最小接口上;

动态代理

在这里插入图片描述

核心类Proxy和InvocationHandler

Proxy来自于java.lang.reflect反射包,提供了静态方法newProxyInstance,创建动态代理的类和实例,同时也是所有代理类的父类

	public static Object new ProxyInstance(ClassLoader loader, 
											Class<?>[] interfaces, 
											InvocationHandler h) 
		throws IllegalArgumentException {
	// 内容省略
	}

InvocationHandler是一个接口(只有invoke方法)

	public interface InvocationHandler{
		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
	}

Proxy在Jvm内部帮我生成一个实例,InvocationHandler通过反射进行业务增强

Demo代码

	public class RyanProxy implements InvocationHandler {
		// 被代理的对象
		private Object factory;
		// 省略get/set方法
		...
		// 通过Proxy获取动态代理的对象
		public Object getProxyInstance() {
			return Proxy.newProxyInstance(factory.getClass().getClassLoader(),
											factory.getClass().getInterfaces(),
											this);
		}
		// 通过动态代理对方法进行增强
		@Override
		public Object invoke(Object proxy, Method method, Object[]args) throws Throwable {
			doSomethingBefore();
			Object ret = method.invoke(factory, args);
			doSomethingAfter();
		}
		// 前置增强
		private void doSomethingBefore() {};
		// 后置增强
		private void doSomethingAfter() {};
	}

假设上述为代理公司,假设有业务工厂CarFactory实现业务接口SellCar,有业务公司WatchFactory实现业务需求接口SellWatch

	// 有代理公司和多个业务公司
	RyanProxy ryan = new RyanProxy();
	CarFactory carFactory = new CarFactory();
	WatchFactory watchFactory = new WatchFactory();
	// 代理业务公司的业务, 获取业务部门进行业务操作
	comp.setFactory(carFactory);
	CarFactory carDept = (CarFactory)ryan.getProxyInstance();
	carDept.sellCar("Audi");

	comp.setFactory(watchFactory);
	WatchFactory watchDept = (WatchFactory)ryan.getProxyInstance();
	watchDept.sellWatch("Rolex");

这样再新增业务的时候,不需要修改原有代码,提供新的业务实现类即可;
一个业务流程出错也不会影响到其他业务流程;

动态代理源码

Debug走到CarFactory carDept = (CarFactory)comp.getProxyInstance();的时候发现carDept的类名为$Proxy0,watchDept的类名为$Proxy1,这两个类是哪里来的,为什么会有?

类的完整的生命周期

在这里插入图片描述

  1. IDE中编写Java源文件(.java)
  2. javac命令把Java源文件编译成Java字节码(.class),字节码还可以从网络(例如热加载)、jvm生成
  3. jvm classloader加载字节码,在jvm内部生成Class对象(Class在jvm的方法区)
  4. 进一步实例化对象
  5. 无引用卸载

上述getProxyInstance即为jvm生成的.class文件,那么具体是如何生成的?

字节码class在内存中如何生成

在这里插入图片描述
进入getProxyClass方法,因为方法区限定64M,所以限定了类实现接口数量小于65535,防止OOM内存溢出
在这里插入图片描述
进入get方法
在这里插入图片描述
最后进入apply方法,红框里是校验,继续往下看
在这里插入图片描述
这里便是要生成的class文件的名字和具体内容,proxyClassNamePrefix = “$Proxy”
在这里插入图片描述
defineClass0的方法如下,native本地方法,用c c++甚至是汇编语言编写的操作系统级别的类库

	private static native Class<?> defineClass0(ClassLoader loader, String name,
                                                byte[] b, int off, int len);

Spring事务注解实现的原理

  1. Spring在调用到service时候发现实际service是一个动态代理类,$Proxy数字的格式,有一个属性为JdkDynamicAopProxy的类,该类为Spring aop的核心类,实现了InvocationHandler接口
    在这里插入图片描述
  2. invoke方法只是定义了通用校验,不和具体业务绑定。Spring启动时扫描那些类的那些方法上添加了aop相关的注解,并且丢到缓存里去。
    在这里插入图片描述
  3. Key为方法,Value为方法上有多少拦截器的注解。如果添加了@Transactional注解,具体拦截器为TransactionInterceptor
    在这里插入图片描述
  4. 其中具体的业务封装在了TransactionAspecSupport的方法里
    在这里插入图片描述
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值