Java代理

突然想了解下java动态修改类执行行为的知识。


1. 首先看了下AspectJ,它可以通过静态植入在不改变原java代码点情况下修改代码的执行逻辑。启中cflow功能更强大更灵活。cflow一般指的是一个方法里的所有代码的片段切换。 


一、Java Instrumentation。 详细讲解可参考http://blog.csdn.net/productshop/article/details/50623626 

premain方式(>=JDK1.5)。应用启动时需要通过-javaagent:xxx.jar指定代理类,且jar包含META-INF/MANIFEST.MF 

Manifest-Version: 1.0 

Premain-Class:com.pre.PreMain

Can-Redefine-Classes: true 


在使用的时候遇到几个问题 

问题1:使用redefineClasses方式时,Caused by: java.lang.UnsupportedOperationException: redefineClasses is not supported in this environment 

通过在MANIFEST.MF增加配置Can-Redefine-Classes: true解决 


问题2:使用addTransformer 方式时,报java.lang.NoClassDefFoundError: com/TransClass (wrong name: com/TransClass2) 

这个主要是由于要替换的class为TransClass2,跟被替换的TransClass不匹配导致,后修一致改为TransClass解决 


agentmain方式(>=JDK1.6),应用正常启动,代理类通过attach方式追加到应用系统

Manifest-Version: 1.0 

Can-Retransform-Classes: true

Agent-Class: com.agent.AgentMain 


问题1:Caused by: java.lang.UnsupportedOperationException: adding retransformable transformers is not supported in this environment 

通过在MANIFEST.MF增加配置Can-Retransform-Classes: true解决


二、java代理

Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

自动生成实现interfaces的java代理类,然后通过loader加载该代理类。实现没个接口的方法,调用h的

public Object invoke(Object proxy, Method method, Object[] args)throws Throwable方法。主要的业务逻辑在InvocationHandler的invoke方法中实现。自我感觉proxy这个参数意义不大,至少还没有发现应用场景

代理是基于接口的,代理生成的对象也是接口。开发时首先定义需要代理的接口,然后在InvocationHandler定义代理的业务逻辑。业务逻辑可以实现代理接口,也可以与代理接口无关

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");是的动态代理的class文件保存在磁盘


三、ASM

下载地址http://forge.ow2.org/project/showfiles.php?group_id=23&release_id=5902

public class Account {
	public void foo(){
		operation();
	}
	public void operation(){
		System.out.println("operation...");
	}
	
	public static void main(String[] args) throws Exception{
		Checker.check();
	}
}

public class AddCheckClassAdapter extends ClassAdapter {

	public AddCheckClassAdapter(ClassVisitor arg0) {
		super(arg0);
	}

	 public MethodVisitor visitMethod(final int access,  final String name, final String desc,final String signature, final String[] exceptions){
		MethodVisitor mv = super.visitMethod(access, name, desc, signature,exceptions);
		MethodVisitor wrappedMv = mv; 
	   	if (mv != null) { 
	            // 对于 "operation" 方法
	   		if (name.equals("operation")) { 
	         	// 使用自定义 MethodVisitor,实际改写方法内容
	         	wrappedMv = new AddCheckMethodAdapter(mv); 
	   		} 
	   	} 
	 	return wrappedMv;
	 }

}

public class AddCheckMethodAdapter extends MethodAdapter {

	public AddCheckMethodAdapter(MethodVisitor mv) {
		super(mv);
		// TODO Auto-generated constructor stub
	}
	
	public void visitCode() {
		visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Checker.class), "check", "()V"); 
	 } 
	
}

public class Generator {
	public static void main(String[] args) throws Exception { 
		 ClassReader cr = new ClassReader(Account.class.getName()); 
		 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); 
		 ClassAdapter classAdapter = new AddCheckClassAdapter(cw); 
		 cr.accept(classAdapter, ClassReader.SKIP_DEBUG); 
		 byte[] data = cw.toByteArray(); 
		 File file = new File("com/asm/Account.class"); 
		 FileOutputStream fout = new FileOutputStream(file); 
		 fout.write(data); 
		 fout.close(); 
	 }
}

public class Main {
	public static void main(String[] args) throws Exception{
		Account account = new Account(); 
		account.foo();
	}
}

首先执行Generator,再执行Main会发现代码已经被修改,使用反编译工具也会看到Account的operation已经被插入。

问题1:Exception in thread "main" java.lang.NoClassDefFoundError: com.Checker
        at com.asm.Account.operation(Unknown Source)
        at com.asm.Account.foo(Unknown Source)

        at Main.main(Main.java:6)

主要是没有使用/格式的类名字,而是使用了“.”,使用Type.getInternalName(Checker.class)转换后解决


asm还可以实现不生成class文件进行动态代理


4:cglib

cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。

package com.cglib;

public interface Test {
	public void test();
}

package com.cglib;

public class TestImpl implements Test {
	public void test(){
		System.out.println("test");
	}
}

package com.cglib;

public class TestMain {
	public static void main(String[] args) {  
		TestProxy proxy = new TestProxy();
		Test test = (Test)proxy.getInstance(new TestImpl());
		test.test();
	}
}

package com.cglib;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;  
import net.sf.cglib.proxy.MethodInterceptor;  
import net.sf.cglib.proxy.MethodProxy;  

public class TestProxy implements MethodInterceptor{

	private Object target;  

	public Object getInstance(Object target) {  
        this.target = target;  
        Enhancer enhancer = new Enhancer();  
        enhancer.setSuperclass(this.target.getClass());  
        // 回调方法  
        enhancer.setCallback(this);  
        // 创建代理对象  
        return enhancer.create();  
    }  
	
	public Object intercept(Object obj, Method method, Object[] args,  
            MethodProxy proxy) throws Throwable {
		System.out.println("cglib 开始");  
        proxy.invokeSuper(obj, args);  
        System.out.println("cglib 结束");
		return null;
	}

}


5:spring cglib(同cglib)

package com.spring;

public interface Test {
	public String test();
}

package com.spring;

public class TestImpl implements Test {
	public String test(){
		System.out.println("test");
		return "test result";
	}
}

package com.spring;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;


public class TestProxy implements MethodInterceptor{

	private Object target;  

	public Object getInstance(Object target) {  
        this.target = target;  
        Enhancer enhancer = new Enhancer();  
        enhancer.setSuperclass(this.target.getClass());  
        // 回调方法  
        enhancer.setCallback(this);  
        // 创建代理对象  
        return enhancer.create();  
    }  
	
	public Object intercept(Object obj, Method method, Object[] args,  
            MethodProxy proxy) throws Throwable {
		System.out.println("cglib 开始");  
        Object result = proxy.invokeSuper(obj, args);  
        System.out.println("cglib 结束");
		return result;
	}

}

package com.spring;

public class TestMain {
	public static void main(String[] args) {  
		TestProxy proxy = new TestProxy();
		Test test = (Test)proxy.getInstance(new TestImpl());
		System.out.println(test.test());
	}
}


6:javassist

比asm更加高级语言化的修改class。

package com.javassist;

public interface TestJavassistInt {
	public void test();
}

package com.javassist;

public class TestJavassist1 implements TestJavassistInt{
	public void test(){
		System.out.println("javassist test");
	}
	
	public static void main(String[] args)throws Exception{
		TestJavassist app = new TestJavassist();
		app.test();
	}
}

package com.javassist;

public class TestJavassist implements TestJavassistInt{
	public void test(){
		System.out.println("javassist test");
	}
	
	public static void main(String[] args)throws Exception{
		TestJavassist app = new TestJavassist();
		app.test();
	}
}

package com.javassist;

import javassist.ClassPool;
import javassist.CtClass;

import com.asm.ByteClassLoader;

public class Generator {
//	public static void main(String[] args) throws Exception {  
//        ClassPool pool = ClassPool.getDefault();  
//        //创建Programmer类       
//        CtClass cc= pool.makeClass("Programmer");  
//        //定义code方法  
//        CtMethod method = CtNewMethod.make("public static void main(String[] args)throws Exception{}", cc);  
//        //插入方法代码  
//        method.insertBefore("System.out.println(\"I'm a Programmer,Just Coding.....\");");  
//        cc.addMethod(method);  
//        //保存生成的字节码  
//        cc.writeFile("/Users/wangql/source/spring_core/ssh/ssh");
//    }
	
	public static void main(String[] args) throws Exception {  
        ClassPool pool = ClassPool.getDefault();
        //创建Programmer类   
        CtClass cc = pool.get(TestJavassist.class.getName());
        cc.getMethod("test", "()V").insertBefore("System.out.println(\"Insert Javassist Test\");");
        
        
//     	cc.writeFile("/Users/wangql/source/spring_core/ssh/ssh/");
        
//      ByteClassLoader loader = new ByteClassLoader();
//      Object obj = loader.defineClassFromClassFile("com.javassist.TestJavassist1",cc.toBytecode()).newInstance();
//      ((TestJavassist1)obj).test();
        
        ByteClassLoader loader = new ByteClassLoader();
        Class clazz = loader.defineClassFromClassFile("com.javassist.TestJavassist",cc.toBytecode());
        Object o= clazz.newInstance();  
        try {
           //调用Programmer的code方法  
            clazz.getMethod("test", null).invoke(o, null);  
        } catch (Exception e) {  
             e.printStackTrace();  
        }  
    }
}

package com.javassist;

public class Main {
	public static void main(String[] args)throws Exception{
		TestJavassist app = new TestJavassist();
		app.test();
	}
}


本来想通过ctclass的toBytecode方法在程序中直接转换class,但是失败,总提示classcast错误,想了想应该是由于两个类的classloader不属于同一个造成的,但是转化为interface的化是可以的,因为interface是用的同一个classloader。可以通过反射来执行


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java代理模式是一种结构型设计模式,其目的是为其他对象提供一种代理以控制对这个对象的访问。代理对象可以在客户端和目标对象之间充当中介,以便于客户端访问目标对象时,可以在不改变目标对象的情况下添加一些额外的功能,比如安全性、远程访问、缓存等。 在Java中,代理模式可以通过两种方式实现:静态代理和动态代理。静态代理需要手动编写代理类,而动态代理可以在运行时通过反射机制动态生成代理类,更加灵活。 举个例子,假设我们有一个接口`Subject`,其中定义了一些方法。我们希望在调用这些方法时,增加一些额外的日志记录功能。我们可以编写一个代理类`SubjectProxy`,在代理类中实现接口方法并调用目标对象的方法,同时在方法前后添加日志记录的代码。客户端则通过代理类访问目标对象。 静态代理示例代码如下: ```java public interface Subject { void doSomething(); } public class RealSubject implements Subject { @Override public void doSomething() { System.out.println("RealSubject do something."); } } public class SubjectProxy implements Subject { private Subject realSubject; public SubjectProxy(Subject realSubject) { this.realSubject = realSubject; } @Override public void doSomething() { System.out.println("Before do something."); realSubject.doSomething(); System.out.println("After do something."); } } public class Client { public static void main(String[] args) { Subject realSubject = new RealSubject(); Subject subjectProxy = new SubjectProxy(realSubject); subjectProxy.doSomething(); } } ``` 动态代理示例代码如下: ```java public class SubjectHandler implements InvocationHandler { private Object target; public SubjectHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before " + method.getName()); Object result = method.invoke(target, args); System.out.println("After " + method.getName()); return result; } } public class Client { public static void main(String[] args) { Subject realSubject = new RealSubject(); InvocationHandler handler = new SubjectHandler(realSubject); Subject subjectProxy = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler); subjectProxy.doSomething(); } } ``` 无论是静态代理还是动态代理代理模式都可以在不改变目标对象的情况下,为其添加额外的功能,提高代码的可复用性和灵活性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值