java实现aop,不影响原来任何代码

1 篇文章 0 订阅
1 篇文章 0 订阅

本文实现java aop,用到了asm4.0,agentmain,eclipse bycode插件


  之前自己写了一个orm框架,但是无奈事务只能靠手动代码控制,曾经想过集成到spring mvc的事务注解里面去,无奈技术太浅。如果使用需要配置拦截器,各种包名或者方法名的方式来做到的话,感觉很麻烦,所以打算自己来实现aop,实现注解控制事务。。


初步的想法是,扫描配置包下面所有的class文件,并读取带有事务注解的方法。然后使用asm4.0修改方法执行之前和返回之前插入一段事务控制的代码,并重新生成byte [] 加载到虚拟机里面去。

先完成第一步,扫描出带事务注解的方法和类

<span style="white-space:pre">	</span>Set<Class<?>> classes = ScanUtil.getClasses("com.wddiaosi.orm.main.db.impl");
		String [] scanPackages=scanPackage.split(",");
		for(int i=0;i<scanPackages.length;i++){
			classes.addAll(ScanUtil.getClasses(scanPackages[i]));
		}
com.wddiaosi.orm.main.db.impl 是我orm框架的默认主要事务注解的类

把扫描出来的class放在set中

ScanUtil.getClasses(scanPackages[i])
是在网上找到别人的代码



读取刚才包含有事务注解的类,并保持事务注解参数

for (Class<?> clazz : classes) {
			ReadClassTransaction.readClass(clazz);
		}



asm修改类方法,具体使用自行搜索,需要注意的是,在解析事务注解的参数时,需要生成方法的签名,以确定当前方法的唯一性

用TransactionVisitor来实现,在方法运行前,返回前插入自己的事务控制代码

asm 尽量使用调用方法的方式来做,不适合用asm生成大量的逻辑代码

AopManager。这个类是在agen项目里面的

for (Entry<Class<?>,Set<VisitorAction>> entry:ReadClassTransaction.map.entrySet()) {
			try {
				ClassReader cr = new ClassReader(entry.getKey().getName());
				 ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
			     ClassVisitor cv = new TransactionVisitor(cw,entry.getValue());
		        cr.accept(cv, Opcodes.ASM4);
				byte[] code = cw.toByteArray();
				AopManager.aopMap.put(entry.getKey(), code);
			} catch (IOException e) {
				e.printStackTrace();
			}

		}


自己写了一个方法签名生成的方法,也可以使用javap -s xxx.class

	private static final Map<String,String> typeMap=new HashMap<String,String>();
	static{
		typeMap.put("void", "V");
		typeMap.put("boolean", "Z");
		typeMap.put("byte", "B");
		typeMap.put("char", "C");
		typeMap.put("short", "S");
		typeMap.put("int", "I");
		typeMap.put("long", "J");
		typeMap.put("float", "F");
		typeMap.put("double", "D");
		typeMap.put("[]", "[");
	}
方法类型对应,如果不是基本类型返回值或者参数前面加大写 L代表引用类型

(参数)返回值

比如 public int test (int a,String b);

方法前面为 (ILjava.lang.String)I




使用agent需要新建一个项目写一个类,类似于main方法,这个类会在jvm加载类进去之后执行

public static void agentmain(String agentArgs, Instrumentation inst) throws IOException, ClassNotFoundException, UnmodifiableClassException {
		Class<?>[] clazzs = inst.getAllLoadedClasses();
		for (int i = 0; i < clazzs.length; i++) {
			byte[] bytes = AopManager.aopMap.get(clazzs[i]);
			if (bytes != null) {
				 ClassDefinition cd= new ClassDefinition(clazzs[i], bytes);
				try {
					inst.redefineClasses(cd);
				} catch (ClassNotFoundException | UnmodifiableClassException e) {
					e.printStackTrace();
				}
			}
		}
		AopManager.aopMap=null;
	}


在其他项目中添加需要修改的类,和新的byte[] 到aopManager.aopMap中,在jvm加载之前替换

把agen打包成jar

被其他项目引用

注意,在打包jar这个过程中。

遇到了很多坑。。

打包后文件目录

代码包

META-INF\MANIFEST.MF

MANIFEST.MF文件内

Agent-Class: Agent
Can-Retransform-Classes: true  
Can-Redefine-Classes: true
Can-Set-Native-Method-Prefix: true   

Agent-Class:( 必须存在空格)Agent


在引用agen.jar的项目中。初始化的时候写入下面这段代码,动态添加代理

String name = ManagementFactory.getRuntimeMXBean().getName();
		String pid = name.split("@")[0];
		try {
			VirtualMachine vm = VirtualMachine.attach(pid);
			String agentJarPath=AopManager.class.getProtectionDomain().getCodeSource().getLocation().getPath();
			vm.loadAgent(agentJarPath.substring(1, agentJarPath.length()));
		} catch (Exception e) {
			e.printStackTrace();
		}


注意 VirtualMachine 这个对象是在jdk环境下的  tools.jar 直接引入进来就可以了



可以测试一下自己写的注解事务的效果了








这样已经实现了事务的传播




ps:我只记录下了,我遇到的几个坑点的问题,并没有详细去记录整个项目实现。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值