【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
    评论
In Elasticsearch 7.7, the recommended way to query from multiple indexes using the Transport Java API is to use the MultiSearchRequest API. Here's an example code snippet for querying from multiple indexes using the MultiSearchRequest API: ``` // Create a client object TransportClient client = new PreBuiltTransportClient(Settings.EMPTY) .addTransportAddress(new TransportAddress(InetAddress.getByName("localhost"), 9300)); // Create a MultiSearchRequest object MultiSearchRequest multiSearchRequest = new MultiSearchRequest(); // Add multiple search requests to the MultiSearchRequest object multiSearchRequest.add(SearchRequestBuilders.searchRequest("index1").source(query)); multiSearchRequest.add(SearchRequestBuilders.searchRequest("index2").source(query)); // Execute the MultiSearchRequest MultiSearchResponse multiSearchResponse = client.multiSearch(multiSearchRequest).actionGet(); // Process the response for (MultiSearchResponse.Item item : multiSearchResponse.getResponses()) { SearchResponse searchResponse = item.getResponse(); // Process the search response for each index } // Close the client object client.close(); ``` In this code snippet, we first create a TransportClient object and a MultiSearchRequest object. We then add multiple SearchRequest objects to the MultiSearchRequest object, with each SearchRequest targeting a different index. We then execute the MultiSearchRequest using the client object, and process the response for each index. Note that the above code snippet uses the deprecated TransportClient API. It is recommended to use the Java High Level REST Client or the Java Low Level REST Client instead.

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值