浅谈java动态代理
浅谈java动态代理
文章目录
- 一、前言
- 二、什么是代理模式
- 2.1 什么是代理模式
- 三、java中常用代理模式
- 3.1 静态代理
- 3.2 动态代理
- 3.2.1 jdk动态代理
- 3.2.1.1 jdk动态代理源码分析
- 3.2.1.2 jdk动态代理小结
- 3.2.2 CGLib动态代理
- 三、区别
- 3.1静态代理和动态代理的区别
- 3.2jdk动态代理和CGLib动态代理
一、前言
最近学习一些框架中,发现很多框架用到了动态代理技术,这里总结一下,以便自己忘记。
二、什么是代理模式
2.1 什么是代理模式
代理模式是一种常用的设计模式,在spring框架、mybatis、Habernate中经常看见它,是一个重要的知识点。
代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用
三、java中常用代理模式
根据代理类的创建时机和创建方式的不同,可以将其分为静态代理和动态代理两种形式:在程序运行前就已经存在的编译好的代理类是为静态代理,在程序运行期间根据需要动态创建代理类及其实例来完成具体的功能是为动态代理。代理模式的目的就是为真实业务对象提供一个代理对象以控制对真实业务对象的访问,代理对象的作用有:
- 代理对象存在的价值主要用于拦截对真实业务对象的访问;
- 代理对象具有和目标对象(真实业务对象)实现共同的接口或继承于同一个类;
- 代理对象是对目标对象的增强,以便对消息进行预处理和后处理。
代理模式主要包含三个角色,即抽象主题角色(Subject)、委托类角色(被代理角色,Proxied) 以及 代理类角色(Proxy):
- 抽象主题角色:可以是接口,也可以是抽象类;
- 委托类角色:真实主题角色,业务逻辑的具体执行者;
- 代理类角色:内部含有对真实对象RealSubject的引用,负责对真实主题角色的调用,并在真实主题角色处理前后做预处理和后处理。
3.1 静态代理
所谓静态代理是指,在程序运行前,由程序员创建或特定工具自动生成源代码并对其编译生成.class文件。静态代理的实现只需要三步:首先,定义业务接口;其次,实现业务接口;然后,定义代理类并实现业务接口;最后便可通过客户端进行调用。例如,
抽象主题角色:UserService 接口
public interface UserService {
void sayHello();
}
委托类角色: UserServiceImpl类
public class UserServiceImpl implements UserService {
@Override
public void sayHello() {
System.out.println("hello");
}
}
代理类角色: UserProxy类
public class UserProxy implements UserService{
UserService mService;
public UserProxy(UserService mService) {
this.mService = mService;
}
@Override
public void sayHello() {
mService.sayHello();
}
}
测试
public class TestProxy {
public static void main(String[] args) {
System.out.println("测试静态代理");
testStaticProxy();
}
public static void testStaticProxy() {
UserProxy mProxy= new UserProxy(new UserServiceImpl());
mProxy.sayHello();
}
}
输出结果
测试静态代理
hello
从中可以看到,它们之间的关系为一个代理类的对象与一个委托类的对象关联,代理类对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法来提供特定的服务。
3.2 动态代理
对代理模式而言,一般来说,具体主题类与其代理类是一一对应的,这也是静态代理的特点。但是,也存在这样的情况:有N个主题类,但是代理类中的“预处理、后处理”都是相同的,仅仅是调用主题不同。那么,若采用静态代理,那么必然需要手动创建N个代理类,这显然让人相当不爽。
动态代理则可以简单地为各个主题类分别生成代理类,共享“预处理,后处理”功能,这样可以大大减小程序规模,这也是动态代理的一大亮点。在动态代理中,代理类是在运行时期生成的。因此,相比静态代理,动态代理可以很方便地对委托类的相关方法进行统一增强处理,如添加方法调用次数、添加日志功能等等。动态代理主要分为JDK动态代理和cglib动态代理两大类
3.2.1 jdk动态代理
jdk动态代理主要使用了java.lang.reflect.Proxy类和InvocationHandler接口。如何使用,例如,
- 创建被代理的接口和类
接口为上文中的UserService,实现类为UserServiceImpl - 实现InvocationHandler接口,对目标接口中声明的所有方法进行统一处理
调用Proxy的静态方法,创建代理类并生成相应的代理对象
//实现了InvocationHandler接口
public class JDKProxy implements InvocationHandler {private Object target;
public JDKProxy(Object target) {
this.target = target;
}
public <T> T getTarget() {
//通过Proxy生成代理对象
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("进入代理方法前");
//相当于调用了sayHello方法
Object obj=method.invoke(target,args);
System.out.println("执行完代理方法后");
return obj;
}
}
使用代理
public class TestJdkProxy {
public static void main(String[] args) {
System.out.println("测试动态代理");
testJDKProxy();
}
public static void testJDKProxy() {
JDKProxy mProxy= new JDKProxy(new UserServiceImpl());
UserService mInterface= mProxy.getTarget();
mInterface.sayHello();
}
}
输出结果
测试动态代理
进入代理方法前
hello
执行完代理方法后
3.2.1.1 jdk动态代理源码分析
我们可以从生成代理对象源码看起,Proxy的newProxyInstance方法:
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
//这里生成了代理类class
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//获取class的构造函数
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//调用Constructor的newInstance方法生成对象
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
看到这里,我们知道了是用的反射生成的对象,代理类class又是怎么生成的呢?我们看一下Class<?> cl = getProxyClass0(loader, intfs)的getProxyClass0方法
/**
* Generate a proxy class. Must call the checkProxyAccess method
* to perform permission checks before calling this.
*/
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
//方法数大于65535抛异常
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
//如果存在实现给定接口的给定加载器定义的代理类,则只返回缓存副本; 否则,它将通过ProxyClassFactory创建代理类
return proxyClassCache.get(loader, interfaces);
}
proxyClassCache是一个缓存对象,缓存中有,就返回缓存副本,没有就使用ProxyClassFactory创建,proxyClassCache的声明如下;
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
接下来我们看一下ProxyClassFactory是如何生成的
/**
* A factory function that generates, defines and returns the proxy class given
* the ClassLoader and array of interfaces.
*/
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
//验证类加载器是否将此接口的名称解析为同一Class对象。
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
//验证Class对象实际上是否表示接口。
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
//验证此接口不是重复的。
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
/*
*记录非公共代理接口的包,以便在同一个包中定义代理类。 验证所有非公共代理接口是否在同一个包中。
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* Choose a name for the proxy class to generate.
* 选择要生成的代理类的名称。从这里我们可以看到,生成的代理class的名称是com.sun.proxy.$Proxy0
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Generate the specified proxy class.
* 生成代理类字节码
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
//调用native方法生成class对象
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
}
到这里,我们就可以看出ProxyClassFactory生成class字节码,然后调用native方法生成相应的class类。
这里我们可以在IDEA中配置JVM参数-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
来输出生成的代理类文件,看看。配置如下:
生成后的目录
生成的代码如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.sun.proxy;
import com.fm.service.UserService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements UserService {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void sayHello() throws {
try {
//从这里可以看到,调用了super.h的invoke方法,也就是我们自定义的JDKProxy的invoke方法
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.fm.service.UserService").getMethod("sayHello");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
从生成的文件可以看出:
JDK动态代理生成的代理类继承了Proxy类,这正是JDK动态代理只能实现接口代理而不能实现类代理的原因,即Java不允许多继承,而动态代理生成的代理类本身就已经继承了Proxy类
3.2.1.2 jdk动态代理小结
1、jdk动态代理使用了反射技术;
2、jdk动态代理类必须实现InvocationHandler接口;
3、代理对象是对目标对象的增强,以便对消息进行预处理和后处理;
4、JDK动态代理只能实现接口代理而不能实现类代理;
3.2.2 CGLib动态代理
CGLIB是一个强大的高性能的代码生成包。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。
CGLIB主要使用了Enhancer类和MethodInterceptor接口。
1、创建被代理的接口和类
接口为上文中的UserService,实现类为UserServiceImpl
2、实现MethodInterceptor接口,实现intercept方法
public class CglibProxy implements MethodInterceptor {
public Object getInstance(Class cls){
Enhancer en=new Enhancer();
en.setSuperclass(cls);
en.setCallback(this);
return en.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("进入代理方法前");
//相当于调用了sayHello方法
Object obj=methodProxy.invokeSuper(o,objects);
System.out.println("执行完代理方法后");
return obj;
}
}
3、 调用Proxy的静态方法,创建代理类并生成相应的代理对象
public class TestJdkProxy {
public static void main(String[] args) {
// System.out.println("测试静态代理");
// testStaticProxy();
// System.out.println("测试动态代理");
// testJDKProxy();
System.out.println("测试Cglib动态代理");
testCglibProxy();
// System.out.println("测试反射");
// testConstructor();
}
public static void testJDKProxy() {
JDKProxy mProxy= new JDKProxy(new UserServiceImpl());
UserService mInterface= mProxy.getTarget();
mInterface.sayHello();
}
public static void testCglibProxy() {
CglibProxy mProxy= new CglibProxy();
UserService mUserService= (UserService) mProxy.getInstance(UserServiceImpl.class);
mUserService.sayHello();
}
}
如何查看CGLib生成的class文件,可以在Enhancer en=new Enhancer();前加上
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\test")
运行后,接可以在D盘的test文件夹下查看到3个文件夹
三、区别
3.1静态代理和动态代理的区别
1、静态代理,针对需要代理的类,都要创建创建一个代理类,来代理需要代理的类。动态代理,只需要一个通用的代理类,可以代理所有需要代理的类,并且通过InvocationHandler.invoke来处理所有的代理方法。
3.2jdk动态代理和CGLib动态代理
1、jdk动态代理需要实现InvocationHandler接口,CGLib需要实现MethodInterceptor接口。
2、Java动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的extends 了Proxy,Java类继承机制不允许多重继承)。
CGLIB既能代理普通的class,也能够代理接口,它主要针对类实现代理,是对指定的类生成一个子类,覆盖其中的方法,并覆盖其中方法实现增强,但是因为采用的是继承,所以该类或方法最好不要声明成final,对于final类或方法,是无法继承的。
3、jdk动态代理是使用java反射实现的,而CGLib是采用ASM字节码生成框架,使用字节码技术生成代理类。可想而知,CGLib效率更高。
还没有评论,来说两句吧...