文章目录
一、代理(Proxy)模式
代理模式也是23重设计模式之中的一种,是SpringAOP的核心机制,也是权限控制、服务控制、缓存、日志、限流、事务等功能模块常用技术。下面一起学习下:
代理模式(Proxy)定义:
给目标对象
提供一个代理对象,并由代理对象控制对目标对象的引用。
通俗的来讲代理模式就是我们生活中常见的中介。
比如卖火车票:我们可以从官网上直接购买,也可以到售票的窗口购买。从官方购买,在编程中,这个过程就是指从提供这个功能的类直接获取这个功能。
当然我们也可以从售票厅旁边的小超市,或者其他地方代售火车票的地方购买火车票,那么这些代销处就是代理了火车站的售票功能,那么他们就是代理。
目的:
1、通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性;功能提供类可以更加专注于主要功能的实现。
当火车站的火车票都交给代理商去做的时候,他们就可以更加专注于完成生产火车票,安排车次等核心业务,当我们需要进行退票、改签的时候也影响不到铁轨正常运营核心业务。
2、通过代理对象对原有的业务增强;
代理商代理了售卖火车票的功能的时候,他们可以在顾客购买火车票的时候加入他们自己的业务,比如下个APP,抢个票,这些业务是功能提供类不会提供的业务,但是我们可以从代理商这里获得这些业务。
代理模式的通用类图:
Subject
抽象对象:可以是抽象类,也可以是接口。抽象主题是一个普通的业务类型,声明了真是对象和代理对象的公共接口。
比如:卖火车票业务就是个公共接口。中国铁路局售票厅是真实对象,售票厅旁边的小超市是代理对象。
RealSubject
真实对象:也叫被代理对象,是业务逻辑的具体执行者。必须去实现抽象对象给的规定(接口)
Proxy
代理对象:也叫做代理类,必须实现抽象对象给的规定(接口)。它负责对真实角色的应用,把所有接口定义的方法限制代理人给真实对象实现,并且在具体真实对象处理完毕前后做预处理和善后处理工作。
比如:代理对象不生产火车票,只是售卖火车票的搬运工。
注意:
1、真实对象和代理对象必须实现公共接口
2、一个代理类可以代理多个真实对象(被代理者),因此一个代理类具体代理哪个具体主题角色,是由场景类决定的。通常情况下,一个接口只需要一个代理类,具体代理哪个实现类有高层模块决定。
二、静态代理和动态代理:
按照代理创建的时期来进行分类的话分为静态代理
和动态代理
2.1、静态代理
静态代理是由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.class文件就已经被创建了
如下代码:
接口类(抽象对象):
真实对象:
代理对象:
package test;
//小商店、超市。除了可以卖火车票以为还提供卖商品,烟酒等服务。如果买到假的火车票还可以回来返票
//代理对象: 代理对象包含真实对象,为真实对象的服务进行增强,和真实对象继承于同一个接口
public class Store implements TrainTicketFactory{
//1、包含真实对象
public TrainTicketFactory factory;
//可以为自己代理火车票
public Store(){
this.factory = new Store();
}
//为乘客代理火车票
public Store(TrainTicketFactory factory){
this.factory = factory;
}
@Override
public void sale_TrainTickets(String number) {
before();
factory.sale_TrainTickets(number);
after();
}
//预处理。(还可以买烟酒)
private void before() {
System.out.println("兄弟来包烟,火车上难免会寂寞!");
}
//后处理。(发现假票、假烟、假酒都可以来找小商店,小商店帮处理)
private void after() {
System.out.println("不好意思兄弟,拿了包假烟给你,重新给你换");
}
}
测试类:
结果:
静态代理优缺点:
优点:
业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点。代理使main()方法不需要知道实现类是什么,怎么做的,而客户端只需知道代理即可(解耦合)。对于如上的客户端代码,TrainTicketFactory Factory = new realSaleFactory();
Store store = new Store(Factory);
可以应用工厂方法将它隐藏。
缺点:
代理类和真是类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。如上的代码是只为TrainTicketFactory类的访问提供了代理,但是如果还要为其他类如realSaleFactory_B类提供代理的话,就需要我们再次添加代理Store_B的代理类。这样日积月累增加对象,代码越来越多,性能也持续下降。
由于静态代理的这个缺点,就需要使用动态代理。
2.2、动态代理
动态代理是在程序运行时通过反射机制动态创建的。主要是用到了java.lang.reflect.Proxy
类和InvocationHandler
接口。其中由Proxy(动态代理器)来创建动态代理对象,由InvocationHandler来指定动态代理对象去做什么样的事情。
动态代理的原理是:当客户有需要的时候,无论是买火车票还是烟酒,或者买充气娃娃,只要你有需求。可以根据你动态变化的需求,动态代理类会帮你找到能代购到各种商品的代理小商店,然后帮你进行代购。
区别:动态代理和静态代理的本质区别在于。代理对象在实时发生改变。
主要方法:
Proxy(调度器):
public static InvocationHandler getInvocationHandler(Object proxy) throws IllegalArgumentException
返回指定代理实例的调用处理程序。
InvocationHandler::
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
在代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。
从静态代理会发现:每个代理类只能为一个接口服务,这样程序开发中必然会产生许多的代理类。所以我们想办法通过一个代理类完成全部的代理功能,那么我们就需要用动态代理.
在上面的示例中,一个代理只能代理一种类型,而且是在编译器就已经确定被代理的对象。而动态代理是在运行时,通过反射机制
实现动态代理,并且能够代理各种类型的对象。
在Java中要想实现动态代理机制,需要:
java.lang.reflect.InvocationHandler
接口
和java.lang.reflect.Proxy
类的支持。
java.lang.reflect.InvocationHandler接口的定义:
invoke方法就是用来增强业务的
package java.lang.reflect;
public interface InvocationHandler {
/**
参数解释:
Object proxy 被代理对象
Method method 要调用的方法
Object[] args 方法调用时所需要的参数
*/
public Object (Object proxy, Method method, Object[] args)
throws Throwable;
}
java.lang.reflect.Proxy类的定义:
public class Proxy implements java.io.Serializable {
private static final long serialVersionUID = -2222568056686623797L;
/** parameter types of a proxy class constructor */
private static final Class<?>[] constructorParams =
{ InvocationHandler.class };
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
/**
* the invocation handler for this proxy instance.
* @serial
*/
protected InvocationHandler h;
/**
* Prohibits instantiation.
*/
private Proxy() {
}
/**
* Constructs a new {@code Proxy} instance from a subclass
* (typically, a dynamic proxy class) with the specified value
* for its invocation handler.
*
* @param h the invocation handler for this proxy instance
*
* @throws NullPointerException if the given invocation handler, {@code h},
* is {@code null}.
*/
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
/**
* 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 any of the given interfaces
* is non-public, the proxy class will be non-public. 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 SecurityException if a security manager, <em>s</em>, is present
* and any of the following conditions is met:
* <ul>
* <li> the given {@code loader} is {@code null} and
* the caller's class loader is not {@code null} and the
* invocation of {@link SecurityManager#checkPermission
* s.checkPermission} with
* {@code RuntimePermission("getClassLoader")} permission
* denies access.</li>
* <li> for each proxy interface, {@code intf},
* the caller's class loader is not the same as or an
* ancestor of the class loader for {@code intf} and
* invocation of {@link SecurityManager#checkPackageAccess
* s.checkPackageAccess()} denies access to {@code intf}.</li>
* </ul>
* @throws NullPointerException if the {@code interfaces} array
* argument or any of its elements are {@code null}
*/
@CallerSensitive
public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)
throws IllegalArgumentException
{
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
return getProxyClass0(loader, intfs);
}
/*
* Check permissions required to create a Proxy class.
*
* To define a proxy class, it performs the access checks as in
* Class.forName (VM will invoke ClassLoader.checkPackageAccess):
* 1. "getClassLoader" permission check if loader == null
* 2. checkPackageAccess on the interfaces it implements
*
* To get a constructor and new instance of a proxy class, it performs
* the package access check on the interfaces it implements
* as in Class.getConstructor.
*
* If an interface is non-public, the proxy class must be defined by
* the defining loader of the interface. If the caller's class loader
* is not the same as the defining loader of the interface, the VM
* will throw IllegalAccessError when the generated proxy class is
* being defined via the defineClass0 method.
*/
private static void checkProxyAccess(Class<?> caller,
ClassLoader loader,
Class<?>... interfaces)
{
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
ClassLoader ccl = caller.getClassLoader();
if (VM.isSystemDomainLoader(loader) && !VM.isSystemDomainLoader(ccl)) {
sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
}
ReflectUtil.checkProxyPackageAccess(ccl, interfaces);
}
}
/**
* Generate a proxy class. Must call the checkProxyAccess method
* to perform permission checks before calling this.
*/
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);
}
/*
* a key used for proxy class with 0 implemented interfaces
*/
private static final Object key0 = new Object();
/*
* Key1 and Key2 are optimized for the common use of dynamic proxies
* that implement 1 or 2 interfaces.
*/
/*
* a key used for proxy class with 1 implemented interface
*/
private static final class Key1 extends WeakReference<Class<?>> {
private final int hash;
Key1(Class<?> intf) {
super(intf);
this.hash = intf.hashCode();
}
@Override
public int hashCode() {
return hash;
}
@Override
public boolean equals(Object obj) {
Class<?> intf;
return this == obj ||
obj != null &&
obj.getClass() == Key1.class &&
(intf = get()) != null &&
intf == ((Key1) obj).get();
}
}
/*
* a key used for proxy class with 2 implemented interfaces
*/
private static final class Key2 extends WeakReference<Class<?>> {
private final int hash;
private final WeakReference<Class<?>> ref2;
Key2(Class<?> intf1, Class<?> intf2) {
super(intf1);
hash = 31 * intf1.hashCode() + intf2.hashCode();
ref2 = new WeakReference<Class<?>>(intf2);
}
@Override
public int hashCode() {
return hash;
}
@Override
public boolean equals(Object obj) {
Class<?> intf1, intf2;
return this == obj ||
obj != null &&
obj.getClass() == Key2.class &&
(intf1 = get()) != null &&
intf1 == ((Key2) obj).get() &&
(intf2 = ref2.get()) != null &&
intf2 == ((Key2) obj).ref2.get();
}
}
/*
* a key used for proxy class with any number of implemented interfaces
* (used here for 3 or more only)
*/
private static final class KeyX {
private final int hash;
private final WeakReference<Class<?>>[] refs;
@SuppressWarnings("unchecked")
KeyX(Class<?>[] interfaces) {
hash = Arrays.hashCode(interfaces);
refs = (WeakReference<Class<?>>[])new WeakReference<?>[interfaces.length];
for (int i = 0; i < interfaces.length; i++) {
refs[i] = new WeakReference<>(interfaces[i]);
}
}
@Override
public int hashCode() {
return hash;
}
@Override
public boolean equals(Object obj) {
return this == obj ||
obj != null &&
obj.getClass() == KeyX.class &&
equals(refs, ((KeyX) obj).refs);
}
private static boolean equals(WeakReference<Class<?>>[] refs1,
WeakReference<Class<?>>[] refs2) {
if (refs1.length != refs2.length) {
return false;
}
for (int i = 0; i < refs1.length; i++) {
Class<?> intf = refs1[i].get();
if (intf == null || intf != refs2[i].get()) {
return false;
}
}
return true;
}
}
/**
* A function that maps an array of interfaces to an optimal key where
* Class objects representing interfaces are weakly referenced.
*/
private static final class KeyFactory
implements BiFunction<ClassLoader, Class<?>[], Object>
{
@Override
public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
switch (interfaces.length) {
case 1: return new Key1(interfaces[0]); // the most frequent
case 2: return new Key2(interfaces[0], interfaces[1]);
case 0: return key0;
default: return new KeyX(interfaces);
}
}
}
/**
* 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<?> 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());
}
}
}
/**
* Returns an instance of a proxy class for the specified interfaces
* that dispatches method invocations to the specified invocation
* handler.
*
* <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 SecurityException if a security manager, <em>s</em>, is present
* and any of the following conditions is met:
* <ul>
* <li> the given {@code loader} is {@code null} and
* the caller's class loader is not {@code null} and the
* invocation of {@link SecurityManager#checkPermission
* s.checkPermission} with
* {@code RuntimePermission("getClassLoader")} permission
* denies access;</li>
* <li> for each proxy interface, {@code intf},
* the caller's class loader is not the same as or an
* ancestor of the class loader for {@code intf} and
* invocation of {@link SecurityManager#checkPackageAccess
* s.checkPackageAccess()} denies access to {@code intf};</li>
* <li> any of the given proxy interfaces is non-public and the
* caller class is not in the same {@linkplain Package runtime package}
* as the non-public interface and the invocation of
* {@link SecurityManager#checkPermission s.checkPermission} with
* {@code ReflectPermission("newProxyInPackage.{package name}")}
* permission denies access.</li>
* </ul>
* @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}
*/
@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<Void>() {
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);
}
}
private static void checkNewProxyPermission(Class<?> caller, Class<?> proxyClass) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
if (ReflectUtil.isNonPublicProxyClass(proxyClass)) {
ClassLoader ccl = caller.getClassLoader();
ClassLoader pcl = proxyClass.getClassLoader();
// do permission check if the caller is in a different runtime package
// of the proxy class
int n = proxyClass.getName().lastIndexOf('.');
String pkg = (n == -1) ? "" : proxyClass.getName().substring(0, n);
n = caller.getName().lastIndexOf('.');
String callerPkg = (n == -1) ? "" : caller.getName().substring(0, n);
if (pcl != ccl || !pkg.equals(callerPkg)) {
sm.checkPermission(new ReflectPermission("newProxyInPackage." + pkg));
}
}
}
}
/**
* Returns true if and only if the specified class was dynamically
* generated to be a proxy class using the {@code getProxyClass}
* method or the {@code newProxyInstance} method.
*
* <p>The reliability of this method is important for the ability
* to use it to make security decisions, so its implementation should
* not just test if the class in question extends {@code Proxy}.
*
* @param cl the class to test
* @return {@code true} if the class is a proxy class and
* {@code false} otherwise
* @throws NullPointerException if {@code cl} is {@code null}
*/
public static boolean isProxyClass(Class<?> cl) {
return Proxy.class.isAssignableFrom(cl) && proxyClassCache.containsValue(cl);
}
/**
* Returns the invocation handler for the specified proxy instance.
*
* @param proxy the proxy instance to return the invocation handler for
* @return the invocation handler for the proxy instance
* @throws IllegalArgumentException if the argument is not a
* proxy instance
* @throws SecurityException if a security manager, <em>s</em>, is present
* and the caller's class loader is not the same as or an
* ancestor of the class loader for the invocation handler
* and invocation of {@link SecurityManager#checkPackageAccess
* s.checkPackageAccess()} denies access to the invocation
* handler's class.
*/
@CallerSensitive
public static InvocationHandler getInvocationHandler(Object proxy)
throws IllegalArgumentException
{
/*
* Verify that the object is actually a proxy instance.
*/
if (!isProxyClass(proxy.getClass())) {
throw new IllegalArgumentException("not a proxy instance");
}
final Proxy p = (Proxy) proxy;
final InvocationHandler ih = p.h;
if (System.getSecurityManager() != null) {
Class<?> ihClass = ih.getClass();
Class<?> caller = Reflection.getCallerClass();
if (ReflectUtil.needsPackageAccessCheck(caller.getClassLoader(),
ihClass.getClassLoader()))
{
ReflectUtil.checkPackageAccess(ihClass);
}
}
return ih;
}
private static native Class<?> defineClass0(ClassLoader loader, String name,
byte[] b, int off, int len);
}
其中,最重要的方法是:
使用Proxy(动态代理器)生成动态代理对象
/**
参数说明:
ClassLoader loader 类的加载器
Class<?>[] interfaces 得到全部的接口
InvocationHandler h 得到InvocationHandler接口的子类的实例
*/
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
直接上代码:
1、抽象类
2、真实对象:
核心步骤:
3、动态创建代理对象的类
动态代理类只能代理接口(不支持抽象类),代理类都需要实现InvocationHandler类,实现invoke方法。invoke方法就是调用被代理接口的所有方法时需要调用的,返回的值是被代理接口的一个实现类
4、测试:
被代理对象target通过参数传递进来,我们通过target.getClass().getClassLoader()获取ClassLoader对象,然后通过target.getClass().getInterfaces()获取它实现的所有接口,然后将target包装到实现了InvocationHandler接口的ProxyHandler实现对象中。通过newProxyInstance函数我们就获得了一个动态代理对象。
动态代理对象工具类:
package test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//动态创建代理对象的工具类
public class StoreHandler implements InvocationHandler {
//被代理对象
private Object target;
//用于设置不同的代理对象
public void setTarget(Object target) {
this.target = target;
}
/**
* 绑定关系,也就是关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke方法
* @param target 绑定具体的动态实例
* @return 动态代理实例
*/
public Object getProxyInstance(Object target) {
this.target = target;
/*
该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例。
第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器。
第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的公共实现接口。
第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法
根据传入的目标返回一个代理对象
*/
Object result = Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
return result;
}
/**
* 关联的这个实现类的方法被调用时将被执行。InvocationHandler接口的方法。
*
* @param proxy 代理
* @param method 原对象被调用的方法。(需要增加的方法)
* @param args 方法的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//TODO 原对象方法调用前添加的预处理逻辑
Object ret = null;
try {
before();//前置增强
//调用目标方法(调用target对象中的方法)
ret = method.invoke(target, args);
after(); //后置增强
} catch (Exception e) {
//log.error("调用{}.{}发生异常", target.getClass().getName(), method.getName(), e);
throw e;
}
//TODO 原对象方法调用后添加的后处理逻辑
return ret;
}
//预处理(前置增强)。
private void before() {
System.out.println("为了购买的东西做过深刻的市场调研");
}
//后处理(后置增强)。
private void after() {
System.out.println("包装精美,包邮,提供一条龙服务");
}
}
测试类:
结果:
可以看到,我们可以通过ProxyHandler代理不同类型的对象,如果我们把对外的接口都通过动态代理来实现,那么所有的函数调用最终都会经过invoke函数的转发,因此我们就可以在这里做一些自己想做的操作,比如日志系统、事务、拦截器、权限控制等。
当前非常流行的面向切面的编程(Aspect Oriented Programming, AOP),其核心就是动态代理机制。
备注:
要实现动态代理的首要条件:被代理类必须实现一个接口,才能被代理。
(现在还有CGLIB可以实现不用实现接口也可以实现动态代理。后续研究)
动态代理优点、缺点:
优点:
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。而且动态代理的应用使我们的类职责更加单一,复用性更强。
缺点:
Proxy已经设计得非常优美,但是还是有一点点小小的遗憾之处:它始终无法摆脱仅支持 interface代理的桎梏,因为它的设计注定了这个遗憾。动态生成的代理类的继承关系图,已经注定有一个共同的父类叫 Proxy。Java 的继承机制注定了这些动态代理类们无法实现对 class 的动态代理,原因是多继承在 Java 中本质上就行不通。
三、CGLib动态代理
以上学习的是JDK的动态代理方式,下面我们学习下由第三提供的CGLib动态代理方式。
3.1、什么是CGLib?
CGLib(Code Generation Library)是一个强大的、高性能的、高质量的字节码的高度封装的生成库,底层是java字节码操作框架ASM
。它用于SpringAOP、测试等框架。
这里我们先简单说一下这两种代理方式最大的区别:JDK动态代理是基于接口的方式,换句话来说就是代理类和目标类(真实类)都实现同一个接口,那么代理类和目标类(真实类)的方法名就一样了;CGLib动态代理是代理类去继承目标类(真实类),然后重写其中目标类的方法啊,这样也可以保证代理类拥有目标类的同名方法。
JDK中的动态代理是通过反射类Proxy以及InvocationHandler回调接口实现的,但是,JDK中所要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高。
使用CGLib实现动态代理,完全不受代理类必须实现接口的限制,而且CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
CGLib框架结构:
CGLib 的API:
3.2 CGLIB 原理
CGLIB 原理:
动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。
CGLIB 底层:使用字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。
CGLIB缺点:对于final方法,无法进行代理
3.3、动态代理案例
jar包:
cglib-nodep-2.2.jar
:使用nodep包不需要关联asm的jar包,jar包内部包含asm的类.
cglib-2.2.jar
:使用此jar包需要关联asm的jar包,否则运行时报错.
1、在项目中导入jar包:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.3.0</version>
</dependency>
2、接口类:
2、真实类(接口实现类):
3、动态代理类:
package test;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
//此时2号小商店Store_2还不是一个真正的代理类,由于2号商店规模很大,他还想发展下家。
//所以2号商店在这只是一个代理对象拦截器
public class Store_2 implements MethodInterceptor {
//获取真正的代理类
public <T> T getProxy(Class clazz){
Enhancer enhancer = new Enhancer(); //字节码增强大一个类
enhancer.setSuperclass(clazz); //设置父类
enhancer.setCallback(this); //回调当前类
return (T) enhancer.create(); //返回代理类
}
@Override
/** 可以拦截到接口或目标类中的所有方法
* Object obj 目标对象
* Method method为目标方法
* Object[] params 目标方法参数,
* MethodProxy proxy CGlib方法代理对象
*/
public Object intercept(Object obj, Method method, Object[] params, MethodProxy proxy) throws Throwable {
System.out.println("调用前处理");
Object result = proxy.invokeSuper(obj, params);
System.out.println("调用后处理");
return result;
}
}
(1)proxy.invokeSuper(obj,arg) 调用代理类实例上的proxy方法的父类方法(即实体类TargetObject中对应的方法)
(2)Enhancer类是CGLib中的一个字节码增强器,它可以方便的对你想要处理的类进行扩展,以后会经常看到它。首先将被代理类Store_2设置成父类,然后设置拦截器,最后执行enhancer.create()动态生成一个代理类,并从Object强制转型成父类型TargetObject。
`(3)回调过滤器CallbackFilter。在CGLib回调时可以设置对不同方法执行不同的回调逻辑,或者根本不执行回调。
在JDK动态代理中并没有类似的功能,对InvocationHandler接口方法的调用对代理类内的所以方法都有效`
3.3.1、对实现类(真实类)的代理
3.3.2、对目标接口的代理
如果是目标接口做代理,因为只有接口,没有具体的实现,所以直接调会报错。我们得加入具体的实现才可以:
3.3.3、对普通类的代理
新建一个普通类:
然后修改动态代理类:
测试: