Java动态代理简述
一、代理简介
代理一词含义十分宽泛,例如金融领域的股票发行代理、营销领域的销售代理、以及计算机领域中的代理设计模式等。尽管代理一词被使用的领域如此广泛,但是代理一词的大致的抽象含义是相似的或者说是相同的。代理是一个被委托人委托其执行如下活动:参加活动、行驶权力、执行任务等。这样理解的话,计算机中某个对象或组件的代理就非常好理解了。
计算机领域中代理的概念是一个十分重要的概念,常见的有代理服务器、代理设计模式等。在软件开发发展成熟的今天,每个工程的代码量也越来越庞大,带来的一个问题就是一次小小的需求修改就会引起很大的变化,从而可能引进新的BUG.
因此程序员对需求修改都深恶痛绝,而代理设计模式在某种程度上可以缓解这种问题。代理设计模式可以实现在不破坏原有代码的情况下,对原有代码添加额外的功能,从而实现以低的侵入完成原有系统功能的扩展,这种设计也符合里氏替换原则(对修改关闭,对扩展开放)。
二、Java语言的代理
编程语言中的代理分为静态代理和动态代理
- 静态代理:在编译时期就已经确定了静态代理的类型或者说是是在编译时期的时候生成代理的类(class)
- 动态代理:在运行时期确定代理的类型或者是说在运行时期生成代理的类(class)
像大多数其他语言一样,Java可以轻松的实现静态代理。具体来讲有两种形式:
静态代理
- 匿名内部类的形式
abstract class AbstractProxy {
AbstractProxy real;
public AbstractProxy() {
// TODO Auto-generated constructor stub
}
public AbstractProxy(AbstractProxy real) {
this.real = real;
};
//被代理的方法
public abstract void foolbar(String str);
}
//某个被代理类
class RealClass extends AbstractProxy {
public RealClass() {
super();
}
public RealClass(AbstractProxy real) {
super(real);
// TODO Auto-generated constructor stub
}
@Override
public void foolbar(String str) {
// TODO Auto-generated method stub
System.out.println("out>>" + str);
}
}
final AbstractProxy realObj = new RealClass();
AbstractProxy proxy = new AbstractProxy() {
@Override
public void foolbar(String str) {
// TODO Auto-generated method stub
System.out.println("you are proxied by me!");
realObj.foolbar(str);
}
};
该形式可以实现一个代理,看似是在运行时生成的一个匿名内部类,但是通过测试发现匿名内部类是在编译时期生成的类,这个可以通过匿名内部类类文件来观察,因此其属于静态代理。这种形式的代理看起来不太正常,而且一个代理类只能代理一个接口或者一个抽象类,如果代理多个就必须新增加多个匿名内部类。
- 继承被代理类或者实现被代理接口
这种形式的代理设计类似于设计模式中的装饰器模式或者适配器模式,具体看代码:
//被代理的接口
interface IReal {
public void doSomeThing(String str);
}
//某个被代理的类
class RealClass implements IReal {
@Override
public void doSomeThing(String str) {
// TODO Auto-generated method stub
System.out.println("doSomeThing " + str);
}
}
class ProxyClass implements IReal {
IReal realObj;
public ProxyClass(IReal realObj) {
this.realObj = realObj;
}
@Override
public void doSomeThing(String str) {
// TODO Auto-generated method stub
System.out.println("you are proxied by me!");
realObj.doSomething();
}
}
RealClass realObj = new RealClass();
RealClass proxy = new ProxyClass(realObj);
这种形式的代理类型需要在编译时期确定,因此属于静态类型。从某种程度上来看,这种形式和装饰器模式、适配器模式的设计思路相似。缺点同第一种形式一样,如果多个需要代理多个接口就需要重写代理类,让其实现多个被代理的接口;同时在类型转换的时候也会很麻烦。
动态代理
java
的动态代理是运行时动态的根据需要被代理的接口列表interfaces生成一个代理类,该代理类实现了接口列表interfaces
中的所有方法,然后在方法的内部实际是讲该方法的调用转发给了实现了InvocationHandler
接口的对象,顾名思义,该对象让包含代理时被代理方法的代理逻辑。
用法举例:编写一个代理类实现拦截某个被代理方法
具体的使用代码:
interface IProxied1 {
public void proxiedMethod1(String str);
}
interface IProxied2 {
public void proxiedMethod2(String str);
}
class Proxied implements IProxied1, IProxied2 {
@Override
public void proxiedMethod2(String str) {
// TODO Auto-generated method stub
System.out.println("proxiedMethod2 " + str);
}
@Override
public void proxiedMethod1(String str) {
// TODO Auto-generated method stub
System.out.println("proxiedMethod1 " + str);
}
}
class Interceptor implements InvocationHandler {
Object proxied;
public Interceptor() {
// TODO Auto-generated constructor stub
}
public Interceptor(Object proxied) {
// TODO Auto-generated constructor stub
this.proxied = proxied;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
System.out.println("I am watching...");
//判断是否拦截
if(method.getName().equals("proxiedMethod1")) {
System.out.println("you are intercepted!!!!!");
return null;
}
return method.invoke(proxied, args);
}
}
执行代码
public class DynamicProxyDemo {
public static void main(String[] str) {
IProxied1 proxiedObj = new Proxied();
Object proxyObj = Proxy.newProxyInstance(IProxied1.class.getClassLoader(), new Class<?>[]{IProxied1.class, IProxied2.class}, new Interceptor(proxiedObj));
((IProxied1)proxyObj).proxiedMethod1("Hello, World!");
System.out.println("-------");
((IProxied2)proxyObj).proxiedMethod2("Hello, World!");
}
}
输出如下:
I am watching...
you are intercepted!!!!!
-------
I am watching...
proxiedMethod2 Hello, World!
比较静态代理和动态代理
一般来讲,静态代理是硬编码去实现一个代理类,如果需要被代理的接口有变动,则需要重新编码代理类;静态绑定的过程将代理类的代理逻辑和代理类的生成绑定到一起了,
所以修改起来不是很方便(解耦不彻底)。其实我们最关注的是代理类的代理逻辑,因此如果将代理的生成自动化(因为代理类的生成的规则是general
的,可以泛化。先实现被代理的接口、然后方法转发,就是这么简单。),
而将代理逻辑分离出来,所有的代理逻辑全部发生在这里;通过这样的解耦,代码可维护性会增强、侵入性会减小,这就是动态代理的思想。
具体来讲区别如下图:
静态代理
静态代理的代理类多处出现代理逻辑的代码,并且同时静态代理的代理类需要自己硬编码。
动态代理
动态代理的代理类类似于一个方法路由,对被代理对象的任何被代理方法的调用,都会被该路由转发到InvocationHandler代理逻辑处理类中,从而将代理类的生成和代理类的代理逻辑分开。
Java
动态代理生成的代理类是直接在内存中按照class
文件格式生成了一个二进制文件,然后类加载器加载该二进制类文件,最后实例化一个代理类的实例。
三、Java动态代理源码分析
前面已经介绍了Java
动态代理的基本用法,主要涉及到如下几个类和方法如下:(JDK7
)
java.lang.reflect.Proxy
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException
sun.misc.ProxyGenerator
public static byte[] generateProxyClass(final String name, Class[] interfaces)
java.lang.reflect.InvocationHandler
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
具体源代码如下:
/**
* Returns an instance of a proxy class for the specified interfaces
* that dispatches method invocations to the specified invocation
* handler. This method is equivalent to:
* <pre>
* Proxy.getProxyClass(loader, interfaces).
* getConstructor(new Class[] { InvocationHandler.class }).
* newInstance(new Object[] { handler });
* </pre>
*
* <p>{@code Proxy.newProxyInstance} throws
* {@code IllegalArgumentException} for the same reasons that
* {@code Proxy.getProxyClass} does.
*
* @param loader the class loader to define the proxy class
* @param interfaces the list of interfaces for the proxy class
* to implement
* @param h the invocation handler to dispatch method invocations to
* @return a proxy instance with the specified invocation handler of a
* proxy class that is defined by the specified class loader
* and that implements the specified interfaces
* @throws IllegalArgumentException if any of the restrictions on the
* parameters that may be passed to {@code getProxyClass}
* are violated
* @throws NullPointerException if the {@code interfaces} array
* argument or any of its elements are {@code null}, or
* if the invocation handler, {@code h}, is
* {@code null}
*/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
if (h == null) {
throw new NullPointerException();
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass(loader, interfaces);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
Constructor cons = cl.getConstructor(constructorParams);
return cons.newInstance(new Object[] { h });
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
} catch (IllegalAccessException e) {
throw new InternalError(e.toString());
} catch (InstantiationException e) {
throw new InternalError(e.toString());
} catch (InvocationTargetException e) {
throw new InternalError(e.toString());
}
}
/**
* Returns the {@code java.lang.Class} object for a proxy class
* given a class loader and an array of interfaces. The proxy class
* will be defined by the specified class loader and will implement
* all of the supplied interfaces. If a proxy class for the same
* permutation of interfaces has already been defined by the class
* loader, then the existing proxy class will be returned; otherwise,
* a proxy class for those interfaces will be generated dynamically
* and defined by the class loader.
*
* <p>There are several restrictions on the parameters that may be
* passed to {@code Proxy.getProxyClass}:
*
* <ul>
* <li>All of the {@code Class} objects in the
* {@code interfaces} array must represent interfaces, not
* classes or primitive types.
*
* <li>No two elements in the {@code interfaces} array may
* refer to identical {@code Class} objects.
*
* <li>All of the interface types must be visible by name through the
* specified class loader. In other words, for class loader
* {@code cl} and every interface {@code i}, the following
* expression must be true:
* <pre>
* Class.forName(i.getName(), false, cl) == i
* </pre>
*
* <li>All non-public interfaces must be in the same package;
* otherwise, it would not be possible for the proxy class to
* implement all of the interfaces, regardless of what package it is
* defined in.
*
* <li>For any set of member methods of the specified interfaces
* that have the same signature:
* <ul>
* <li>If the return type of any of the methods is a primitive
* type or void, then all of the methods must have that same
* return type.
* <li>Otherwise, one of the methods must have a return type that
* is assignable to all of the return types of the rest of the
* methods.
* </ul>
*
* <li>The resulting proxy class must not exceed any limits imposed
* on classes by the virtual machine. For example, the VM may limit
* the number of interfaces that a class may implement to 65535; in
* that case, the size of the {@code interfaces} array must not
* exceed 65535.
* </ul>
*
* <p>If any of these restrictions are violated,
* {@code Proxy.getProxyClass} will throw an
* {@code IllegalArgumentException}. If the {@code interfaces}
* array argument or any of its elements are {@code null}, a
* {@code NullPointerException} will be thrown.
*
* <p>Note that the order of the specified proxy interfaces is
* significant: two requests for a proxy class with the same combination
* of interfaces but in a different order will result in two distinct
* proxy classes.
*
* @param loader the class loader to define the proxy class
* @param interfaces the list of interfaces for the proxy class
* to implement
* @return a proxy class that is defined in the specified class loader
* and that implements the specified interfaces
* @throws IllegalArgumentException if any of the restrictions on the
* parameters that may be passed to {@code getProxyClass}
* are violated
* @throws NullPointerException if the {@code interfaces} array
* argument or any of its elements are {@code null}
*/
//根据一组接口列表interfaces,返回一个由类加载器loader加载的实现了所有interfaces的类对象
//如果实现接口列表interfaces的类已经被加载过了;则直接返回缓存的类对象
public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)
throws IllegalArgumentException
{
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
Class<?> proxyClass = null;
/* collect interface names to use as key for proxy class cache */
String[] interfaceNames = new String[interfaces.length];
// for detecting duplicates
Set<Class<?>> interfaceSet = new HashSet<>();
//验证接口列表的接口对于参数给定的类加载器loader是否是可见的;
//同时检查接口列表interfaces的合法性(无重复的接口、必须是接口类型)
for (int i = 0; i < interfaces.length; i++) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
String interfaceName = interfaces[i].getName();
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(interfaceName, false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != interfaces[i]) {
throw new IllegalArgumentException(
interfaces[i] + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
if (interfaceSet.contains(interfaceClass)) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
interfaceSet.add(interfaceClass);
interfaceNames[i] = interfaceName;
}
/*
* Using string representations of the proxy interfaces as
* keys in the proxy class cache (instead of their Class
* objects) is sufficient because we require the proxy
* interfaces to be resolvable by name through the supplied
* class loader, and it has the advantage that using a string
* representation of a class makes for an implicit weak
* reference to the class.
*/
List<String> key = Arrays.asList(interfaceNames);
/*
* Find or create the proxy class cache for the class loader.
*/
Map<List<String>, Object> cache;
synchronized (loaderToCache) {
cache = loaderToCache.get(loader);
if (cache == null) {
cache = new HashMap<>();
loaderToCache.put(loader, cache);
}
/*
* This mapping will remain valid for the duration of this
* method, without further synchronization, because the mapping
* will only be removed if the class loader becomes unreachable.
*/
}
/*
* Look up the list of interfaces in the proxy class cache using
* the key. This lookup will result in one of three possible
* kinds of values:
* null, if there is currently no proxy class for the list of
* interfaces in the class loader,
* the pendingGenerationMarker object, if a proxy class for the
* list of interfaces is currently being generated,
* or a weak reference to a Class object, if a proxy class for
* the list of interfaces has already been generated.
*/
synchronized (cache) {
/*
* Note that we need not worry about reaping the cache for
* entries with cleared weak references because if a proxy class
* has been garbage collected, its class loader will have been
* garbage collected as well, so the entire cache will be reaped
* from the loaderToCache map.
*/
do {
//cache 的key是接口名数组生成的list;value是一个代理类对象的弱引用
Object value = cache.get(key);
if (value instanceof Reference) {
proxyClass = (Class<?>) ((Reference) value).get();
}
if (proxyClass != null) {
// proxy class already generated: return it
return proxyClass;
//如果是一个标志符号,则说明有另外的线程正在创建代理类;则该线程挂起等待
} else if (value == pendingGenerationMarker) {
// proxy class being generated: wait for it
try {
cache.wait();
} catch (InterruptedException e) {
/*
* The class generation that we are waiting for should
* take a small, bounded time, so we can safely ignore
* thread interrupts here.
*/
}
continue;//被唤醒后;继续check是否存在类对象
} else {
/*
* No proxy class for this list of interfaces has been
* generated or is being generated, so we will go and
* generate it now. Mark it as pending generation.
*/
//不存在代理类的时候,需要自己去生成代理类;生成代理类之前先置一个状态标志对象pendingGenerationMarker
cache.put(key, pendingGenerationMarker);
break;
}
} while (true);
}
try {
String proxyPkg = null; // package to define proxy class in
/*
* 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 (int i = 0; i < interfaces.length; i++) {
int flags = interfaces[i].getModifiers();
if (!Modifier.isPublic(flags)) {
String name = interfaces[i].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,
proxyPkg = ""; // use the unnamed package
}
{
/*
* Choose a name for the proxy class to generate.
*/
long num;
synchronized (nextUniqueNumberLock) {
num = nextUniqueNumber++;
}
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Verify that the class loader hasn't already
* defined a class with the chosen name.
*/
/*
* Generate the specified proxy class.
*/
//重点在这里;调用ProxyGenerator的静态方法去生成一个代理类,得到类文件的字节数组
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces);
try {
//用类加载器加载二进制类文件,得到代理类对象
proxyClass = 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());
}
}
// add to set of all generated proxy classes, for isProxyClass
proxyClasses.put(proxyClass, null);
} finally {
/*
* We must clean up the "pending generation" state of the proxy
* class cache entry somehow. If a proxy class was successfully
* generated, store it in the cache (with a weak reference);
* otherwise, remove the reserved entry. In all cases, notify
* all waiters on reserved entries in this cache.
*/
synchronized (cache) {
if (proxyClass != null) {
//加入缓存
cache.put(key, new WeakReference<Class<?>>(proxyClass));
} else {
cache.remove(key);
}
cache.notifyAll();//不管创建代理类是否成功,唤醒在cache上面等待的线程
}
}
return proxyClass;
}
/**
* Generate a class file for the proxy class. This method drives the
* class file generation process.
*/
//根据得到的方法信息、类信息;按照JVM的规范动态的生成一个代理类的二进制文件
//该过程比较复杂涉及到许多JVM的指令
private byte[] generateClassFile() {
/* ============================================================
* Step 1: Assemble ProxyMethod objects for all methods to
* generate proxy dispatching code for.
*/
/*
* Record that proxy methods are needed for the hashCode, equals,
* and toString methods of java.lang.Object. This is done before
* the methods from the proxy interfaces so that the methods from
* java.lang.Object take precedence over duplicate methods in the
* proxy interfaces.
*/
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);
/*
* Now record all of the methods from the proxy interfaces, giving
* earlier interfaces precedence over later ones with duplicate
* methods.
*/
for (int i = 0; i < interfaces.length; i++) {
Method[] methods = interfaces[i].getMethods();
for (int j = 0; j < methods.length; j++) {
addProxyMethod(methods[j], interfaces[i]);
}
}
/*
* For each set of proxy methods with the same signature,
* verify that the methods' return types are compatible.
*/
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
checkReturnTypes(sigmethods);
}
/* ============================================================
* Step 2: Assemble FieldInfo and MethodInfo structs for all of
* fields and methods in the class we are generating.
*/
try {
methods.add(generateConstructor());
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
// add static field for method's Method object
fields.add(new FieldInfo(pm.methodFieldName,
"Ljava/lang/reflect/Method;",
ACC_PRIVATE | ACC_STATIC));
// generate code for proxy method and add it
methods.add(pm.generateMethod());
}
}
methods.add(generateStaticInitializer());
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception");
}
if (methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
}
if (fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
}
/* ============================================================
* Step 3: Write the final class file.
*/
/*
* Make sure that constant pool indexes are reserved for the
* following items before starting to write the final class file.
*/
cp.getClass(dotToSlash(className));
cp.getClass(superclassName);
for (int i = 0; i < interfaces.length; i++) {
cp.getClass(dotToSlash(interfaces[i].getName()));
}
/*
* Disallow new constant pool additions beyond this point, since
* we are about to write the final constant pool table.
*/
cp.setReadOnly();
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);
try {
/*
* Write all the items of the "ClassFile" structure.
* See JVMS section 4.1.
*/
// u4 magic;
dout.writeInt(0xCAFEBABE);
// u2 minor_version;
dout.writeShort(CLASSFILE_MINOR_VERSION);
// u2 major_version;
dout.writeShort(CLASSFILE_MAJOR_VERSION);
cp.write(dout); // (write constant pool)
// u2 access_flags;
dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
// u2 this_class;
dout.writeShort(cp.getClass(dotToSlash(className)));
// u2 super_class;
dout.writeShort(cp.getClass(superclassName));
// u2 interfaces_count;
dout.writeShort(interfaces.length);
// u2 interfaces[interfaces_count];
for (int i = 0; i < interfaces.length; i++) {
dout.writeShort(cp.getClass(
dotToSlash(interfaces[i].getName())));
}
// u2 fields_count;
dout.writeShort(fields.size());
// field_info fields[fields_count];
for (FieldInfo f : fields) {
f.write(dout);
}
// u2 methods_count;
dout.writeShort(methods.size());
// method_info methods[methods_count];
for (MethodInfo m : methods) {
m.write(dout);
}
// u2 attributes_count;
dout.writeShort(0); // (no ClassFile attributes for proxy classes)
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception");
}
return bout.toByteArray();
}
通过反编译生成的动态代理类的文件,可以得到
final class $Proxy0
extends Proxy
implements IReal
{
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler paramInvocationHandler)
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
{
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 doSomeThing(String paramString)
{
try
{
this.h.invoke(this, m3, new Object[] { paramString });//方法的转发;反射调用InvocationHandler的invoke方法
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
{
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()
{
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("trick.IReal").getMethod("doSomeThing", new Class[] { Class.forName("java.lang.String") });
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());
}
}
}
java
动态代理的应用在框架中十分广泛,例如Spring
框架用动态代理来实现AOP
、Struts2
框架利用动态代理实现拦截器。
AOP
中的代理逻辑点
又称为切面的切入点(cut point)
。另外,实现AOP
概念的方式是动态代理,但动态代理的形式有很多种,JDK
提供的这种只是其中一种,还有涉及到类加载器加载类前、加载类后植入字节码等形式。
See Also
[1] 彻底理解JAVA动态代理
[2] JDK动态代理实现原理
[3]AOP动态代理的实现机制