【Java知识点详解 2】动态代理

一、代理模式

代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

著名的代理模式例子为引用计数(英语:reference counting)指针对象。

当一个复杂对象的多份副本须存在时,代理模式可以结合享元模式以减少存储器用量。典型作法是创建一个复杂对象及多个代理者,每个代理者会引用到原本的复杂对象。而作用在代理者的运算会转送到原本对象。一旦所有的代理者都不存在时,复杂对象会被移除。

二、组成

抽象角色:通过接口或抽象类声明真实角色实现的业务方法。

代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。

真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

三、优点

1、职责清晰

真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰。

2、保护对象

代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用。

3、高扩展性

四、模式结构

一个是真正的你要访问的对象(目标类),一个是代理对象,真正对象与代理
  对象实现同一个接口,先访问代理类再访问真正要访问的对象。

代理模式分为静态代理、动态代理。

静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。

五、静态代理

创建一个接口,然后创建被代理的类实现该接口并且实现该接口中的抽象方法。之后再创建一个代理类,同时使其也实现这个接口。在代理类中持有一个被代理对象的引用,而后在代理类方法中调用该对象的方法。

使用静态代理很容易就完成了对一个类的代理操作。但是静态代理的缺点也暴露了出来:由于代理只能为一个类服务,如果需要代理的类很多,那么就需要编写大量的代理类,比较繁琐。

六、动态代理

1、动态代理流程图

【Java知识点详解 2】动态代理_第1张图片

2、动态代理代码实现

(1)代理类

利用反射机制在运行时创建代理类。
接口、被代理类不变,我们构建一个ProxyInvocationHandler类来实现InvocationHandler接口。

package com.guor.aop.dynamicproxy;
 
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
 
public class ProxyInvocationHandler implements InvocationHandler {
	private Object target;
	
	public Object getTarget() {
		return target;
	}
 
	public void setTarget(Object target) {
		this.target = target;
	}
 
	//生成得到代理类
	public Object getProxy() {
		return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
	}
	
	//处理代理实例,并返回结果
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		log(method.getName());
		//动态代理的本质就是使用反射机制来实现
		Object result = method.invoke(target, args);
		return result;
	}
	
	public void log(String msg) {
		System.out.println("执行了"+msg+"方法");
	}
}

 通过Proxy类的静态方法newProxyInstance返回一个接口的代理实例。针对不同的代理类,传入相应的代理程序控制器InvocationHandler。 

(2)被代理类UserService

package com.guor.aop;
 
public interface UserService {
	public void add();
	public void delete();
	public void update();
	public void query();
}
package com.guor.aop;
 
public class UserServiceImpl implements UserService {
 
	public void add() {
		System.out.println("add");
	}
 
	public void delete() {
		System.out.println("delete");
	}
 
	public void update() {
		System.out.println("update");
	}
 
	public void query() {
		System.out.println("query");
	}
 
}

(3)执行动态代理

package com.guor.aop.dynamicproxy;
 
import com.guor.aop.UserService;
import com.guor.aop.UserServiceImpl;
 
public class Client {
 
	public static void main(String[] args) {
		//真实角色
		UserService userService = new UserServiceImpl();
		
		//代理角色
		ProxyInvocationHandler pih = new ProxyInvocationHandler();
		//通过调用程序处理角色来处理我们要调用的接口对象
		pih.setTarget(userService);
		
		UserService proxy = (UserService) pih.getProxy();
		proxy.update();
	}
 
}

(4)控制台输出 

【Java知识点详解 2】动态代理_第2张图片

七、动态代理底层实现

1、动态代理具体步骤

  • 通过实现 InvocationHandler 接口创建自己的调用处理器;
  • 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
  • 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
  • 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

2、源码分析

(1)newProxyInstance

既然生成代理对象是用的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 cl = getProxyClass0(loader, intfs);
 
	/*
	 * Invoke its constructor with the designated invocation handler.
	 */
	try {
		if (sm != null) {
			checkNewProxyPermission(Reflection.getCallerClass(), cl);
		}
 
		final Constructor cons = cl.getConstructor(constructorParams);
		final InvocationHandler ih = h;
		if (!Modifier.isPublic(cl.getModifiers())) {
			AccessController.doPrivileged(new PrivilegedAction() {
				public Void run() {
					cons.setAccessible(true);
					return null;
				}
			});
		}
		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);
	}
}

(2)getProxyClass0

利用getProxyClass0(loader, intfs)生成代理类Proxy的Class对象。

private static Class getProxyClass0(ClassLoader loader,Class... interfaces) {
	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
	return proxyClassCache.get(loader, interfaces);
}

【Java知识点详解 2】动态代理_第3张图片

(3)ProxyClassFactory

ProxyClassFactory内部类创建、定义代理类,返回给定ClassLoader 和interfaces的代理类。

private static final class ProxyClassFactory implements BiFunction[], 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, 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 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.
			 */
			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.
		 */
		long num = nextUniqueNumber.getAndIncrement();
		String proxyName = proxyPkg + proxyClassNamePrefix + num;
 
		/*
		 * Generate the specified proxy class.
		 */
		byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
			proxyName, interfaces, accessFlags);
		try {
			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());
		}
	}
}

(4)generateProxyClass

一系列检查后,调用ProxyGenerator.generateProxyClass来生成字节码文件。

public static byte[] generateProxyClass(final String name,Class[] interfaces,int accessFlags)
{
	ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
	final byte[] classFile = gen.generateClassFile();
 
	if (saveGeneratedFiles) {
		java.security.AccessController.doPrivileged(
		new java.security.PrivilegedAction() {
			public Void run() {
				try {
					int i = name.lastIndexOf('.');
					Path path;
					if (i > 0) {
						Path dir = Paths.get(name.substring(0, i).replace('.', File.separatorChar));
						Files.createDirectories(dir);
						path = dir.resolve(name.substring(i+1, name.length()) + ".class");
					} else {
						path = Paths.get(name + ".class");
					}
					Files.write(path, classFile);
					return null;
				} catch (IOException e) {
					throw new InternalError(
						"I/O exception saving generated file: " + e);
				}
			}
		});
	}
 
	return classFile;
}

【Java知识点详解 2】动态代理_第4张图片

(5)generateClassFile

生成代理类字节码文件的generateClassFile方法:

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 (Class intf : interfaces) {
		for (Method m : intf.getMethods()) {
			addProxyMethod(m, intf);
		}
	}
 
	/*
	 * For each set of proxy methods with the same signature,
	 * verify that the methods' return types are compatible.
	 */
	for (List 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 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", e);
	}
 
	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 (Class intf: interfaces) {
		cp.getClass(dotToSlash(intf.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(accessFlags);
									// 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 (Class intf : interfaces) {
			dout.writeShort(cp.getClass(
				dotToSlash(intf.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", e);
	}
 
	return bout.toByteArray();
}

字节码生成后,调用defineClass0来解析字节码,生成了Proxy的Class对象。在了解完代理类动态生成过程后,生产的代理类是怎样的,谁来执行这个代理类。

其中,在ProxyGenerator.generateProxyClass函数中 saveGeneratedFiles定义如下,其指代是否保存生成的代理类class文件,默认false不保存。

在前面的示例中,我们修改了此系统变量:

System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

 

上一篇:【全栈最全Java框架总结】SSH、SSM、Springboot

下一篇:Spring常用注解(绝对经典)

你可能感兴趣的:(Java,SE)