java动态代理概念
Java静态代理中代理类的.class文件在程序运行期间已存在,并且目标类和代理类都需要实现同一个接口,每个目标类的增强都需要有一个对应的代理类,代码的复用性低。
Java动态代理需要解决的就是这个痛点,通过反射的方式动态创建目标类的代理类来实现功能的增强。动态代理将目标类和增强类分开,实现了解耦,提高了代码复用性,也不需要创建目标类对应的代理类,而是根据不同的实现方式动态地生成其代理类。
Java动态代理有两种:jdk动态代理和cglib动态代理。
篇幅有限,下文先介绍jdk动态代理。
jdk动态代理
jdk动态代理必须实现接口。
需要动态代理的接口
package com.teasir.spring.proxyjdk;
/*
* 目标接口
* */
public interface PersonService {
public void save();
}
需要代理的目标实现类
package com.teasir.spring.proxyjdk;
public class PersonServiceImpl implements PersonService {
@Override
public void save() {
System.out.println("save some information");
}
}
增强类
package com.teasir.spring.proxyjdk;
/**
* 增强类:为方法开启事务
* */
public class MyTransaction {
public void begin() {
System.out.println("开启事务");
}
public void commit() {
System.out.println("提交事务");
}
}
创建一个实现InvocationHandler接口的类,实现invoke方法,调用增强类方法实现对目标类方法的增强。
每次生成动态代理实现类都需要实现一个处理器接口。
package com.teasir.spring.proxyjdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class PersonServiceInterceptor implements InvocationHandler {
//目标类
private Object target;
//增强类
private MyTransaction myTransaction;
//构造器注入目标类和增强类
public PersonServiceInterceptor(Object target, MyTransaction myTransaction) {
this.target = target;
this.myTransaction = myTransaction;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
this.myTransaction.begin();
Object retObj = method.invoke(this.target, args);
this.myTransaction.commit();
return retObj;
}
}
通过Proxy的静态方法newProxyInstance创建一个动态代理实例。
package com.teasir.spring.proxyjdk;
import java.lang.reflect.Proxy;
public class ProxyTest {
public static void main(String[] args) {
//目标类
Object object=new PersonServiceImpl();
//增强类
MyTransaction myTransaction=new MyTransaction();
//生成动态代理对象
PersonServiceInterceptor interceptor=new PersonServiceInterceptor(object,myTransaction);
PersonService personService= (PersonService) Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),interceptor);
personService.save();
}
}
测试结果
开启事务
save some information
提交事务
jdk动态代理的实现过程
为什么调用动态代理类personService的save()最后会调用PersonServiceInterceptor的invoke()方法呢?
查看源码可以看到起关键作用的位置是动态生成的字节码文件:
//生成字节码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
生成字节码代码改造如下:
package com.teasir.spring.proxyjdk;
import sun.misc.ProxyGenerator;
import javax.security.auth.Subject;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Proxy;
public class ProxyTest {
public static void main(String[] args) {
//目标类
Object object=new PersonServiceImpl();
//增强类
MyTransaction myTransaction=new MyTransaction();
//生成动态代理对象
PersonServiceInterceptor interceptor=new PersonServiceInterceptor(object,myTransaction);
PersonService personService= (PersonService) Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),interceptor);
personService.save();
// 将生成的字节码保存到本地,
createProxyClassFile();
}
private static void createProxyClassFile() {
String name = "ProxySubject";
byte[] data = ProxyGenerator.generateProxyClass(name, new Class[]{PersonService.class});
FileOutputStream out = null;
try {
out = new FileOutputStream(name + ".class");
System.out.println((new File("hello")).getAbsolutePath());
out.write(data);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != out) try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
定义方法生成字节码文件并通过反编译查看动态代理生成的字节码文件,可以看出最后生成的动态代理实例继承Proxy并且实现了PersonService
接口,当该实例调用save方法时,最后会调用PersonServiceInterceptor的invoke()方法。
反编译代码
import com.teasir.spring.proxyjdk.PersonService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class ProxySubject extends Proxy
implements PersonService
{
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public ProxySubject(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
throws
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void save()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
throws
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("com.teasir.spring.proxyjdk.PersonService").getMethod("save", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
jdk动态代理实现过程总结
1、定义目标类接口、定义增强类(比如事务类);
2、定义目标类实现目标类接口,定义一个自定义处理器类实现InvocationHandler接口并实现invoke方法;
3、通过Proxy.newProxyInstance(类加载器,类的接口,自定义处理器)来创建动态代理实例;
创建动态代理实例原理:通过反射生成对应的.class字节码文件来创建动态代理实例。
4、动态代理实例调用目标类的方法,从生成的字节码反编译可以看出,每次调用目标类的方法时同时会调用InvocationHandler接口并实现invoke方法从而完成方法功能的增强。
反编译工具下载:
链接: https://pan.baidu.com/s/1y2syXFmn44u9axKvOiQx1w
提取码: r2m9
相关文章
Java静态代理详解
Java反射机制详解
spring动态代理之cglib动态代理
参考文章
https://caoju.blog.csdn.net/article/details/90714295
https://blog.csdn.net/litianxiang_kaola/article/details/85335700
https://blog.csdn.net/jiankunking/article/details/52143504
https://blog.csdn.net/laijunfeng816/article/details/79241925