-
代理模式
(1) 代理模式下一共有4种角色
1° Subject接口 —> 用户访问的超类型
2° SubjectImpl类 —> Subject接口的实现类
3° SubjectProxy类 —> SubjectImpl的代理类
4° Client —> 访问Subject的用户,它不在乎Subject接口的具体实现,只要是Subject超类型即可
-
静态代理
(1) 过程就是:为每个需要代理的类的外面套上一层
(2) 静态代理__存在的问题__:
很多时候,代理类的功能是类似的(例如统计被代理方法的执行时间是共通的需求);但是,为了给这些需要代理的类套上一个壳子,如果使用了静态代理,意味着明明是相同的功能(统计运行时间),却要每个类都加上它们的代理类,要手动编写很多个代理类
示例
第一个要被代理的类是 Subject1.class
此时要实现第一个代理Subject1类的代理类ServiceControlSubjectProxy1
public class ServiceControlSubjectProxy1 implements Subject1 { private Subject1 subject; public ServiceControlSubjectProxy1(Subject1 subject) { this.subject = subject; } public String request() { if ( currentTime.isAfter(6:00) ) { return null; } return this.subject.request(); } }
然后有第二个被代理的类 Subject2.class
很不幸还要实现第二个代理Subject2类的代理类ServiceControlSubjectProxy2
public class ServiceControlSubjectProxy2 implements Subject2 { private Subject2 subject; public ServiceControlSubjectProxy1(Subject2 subject) { this.subject = subject; } public void request() { if ( currentTime.isAfter(6:00) ) { return null; } this.subject.request(); } }
明明ServiceControlSubjectProxy1和ServiceControlSubjectProxy2的功能是一样的,但是很不幸,静态代理就是要每个都实现一遍
-
Java原生动态代理
(1) Java原生动态代理主要由一个Proxy类和一个InvocationHandler接口搞定,其中InvocationHandler就是我们要在代理中添加的逻辑(对于相同的逻辑,我们只需要实现一次InvocationHandler接口就可以用到所有需要代理的地方);而Proxy类提供的static方法newProxyInstance()是用来动态产生代理类的
(2) InvocationHandler接口
InvocationHandler接口只有一个方法invoke(Object proxy, Method method, Object[] args);
这个方法会在调用代理实例的方法时被触发;proxy参数代表触发它的那个代理实例;method代表触发的那个方法,注意必须要是接口中的方法;args代表方法中的参数,如果没有的话就是null,基本类型(比如int)会变成包装类传进来
invoke()方法的返回值是Object超类型:这意味着如果接口方法本该返回基本类型,而在这里返回了null,就会报NullPointerException异常;如果这里返回的类型和接口方法返回的类型无法cast,那么会报ClassCastException异常
public interface InvocationHandler { /** * Processes a method invocation on a proxy instance and returns * the result. This method will be invoked on an invocation handler * when a method is invoked on a proxy instance that it is * associated with. * * @param proxy the proxy instance that the method was invoked on * * @param method the {@code Method} instance corresponding to * the interface method invoked on the proxy instance. The declaring * class of the {@code Method} object will be the interface that * the method was declared in, which may be a superinterface of the * proxy interface that the proxy class inherits the method through. * * @param args an array of objects containing the values of the * arguments passed in the method invocation on the proxy instance, * or {@code null} if interface method takes no arguments. * Arguments of primitive types are wrapped in instances of the * appropriate primitive wrapper class, such as * {@code java.lang.Integer} or {@code java.lang.Boolean}. * * @return the value to return from the method invocation on the * proxy instance. If the declared return type of the interface * method is a primitive type, then the value returned by * this method must be an instance of the corresponding primitive * wrapper class; otherwise, it must be a type assignable to the * declared return type. If the value returned by this method is * {@code null} and the interface method's return type is * primitive, then a {@code NullPointerException} will be * thrown by the method invocation on the proxy instance. If the * value returned by this method is otherwise not compatible with * the interface method's declared return type as described above, * a {@code ClassCastException} will be thrown by the method * invocation on the proxy instance. * * @throws Throwable the exception to throw from the method * invocation on the proxy instance. The exception's type must be * assignable either to any of the exception types declared in the * {@code throws} clause of the interface method or to the * unchecked exception types {@code java.lang.RuntimeException} * or {@code java.lang.Error}. If a checked exception is * thrown by this method that is not assignable to any of the * exception types declared in the {@code throws} clause of * the interface method, then an * {@link UndeclaredThrowableException} containing the * exception that was thrown by this method will be thrown by the * method invocation on the proxy instance. * * @see UndeclaredThrowableException */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
(3) Proxy类
为了动态产生一个代理类,要用到的就是newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法;
其中,loader参数代表代理类用的类加载器;interfaces代表代理类要实现的所有接口(注意,必须是接口,不能是类);h代表代理类实际添加的业务逻辑对应的InvocationHandler;
newProxyInstance返回的是一个由loader类加载器标记的、实现了所有interfaces的实例
注意这些interface的名字必须要能被找到(也就是说用class.forName()要找的到,因为要靠反射实现动态代理)
/** * Returns a proxy instance for the specified interfaces * that dispatches method invocations to the specified invocation * handler. * <p> * <a id="restrictions">{@code IllegalArgumentException} will be thrown * if any of the following restrictions is violated:</a> * <ul> * <li>All of {@code Class} objects in the given {@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:<p> * {@code Class.forName(i.getName(), false, cl) == i} * * <li>All of the types referenced by all * public method signatures of the specified interfaces * and those inherited by their superinterfaces * must be visible by name through the specified class loader. * * <li>All non-public interfaces must be in the same package * and module, defined by the specified class loader and * the module of the non-public interfaces can access all of * the interface types; 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>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 * @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 <a href="#restrictions"> * restrictions</a> on the parameters 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} * * @see <a href="#membership">Package and Module Membership of Proxy Class</a> * @revised 9 * @spec JPMS */ @CallerSensitive public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
(4) 示例
public class Solution { public static void main(String[] args) throws InterruptedException{ Person person = new MyPerson(1234, "cb"); Person personProxy = (Person) Proxy.newProxyInstance( person.getClass().getClassLoader(), person.getClass().getInterfaces(), new MyInvocationHandler(person)); System.out.println(personProxy.getId()); System.out.println(personProxy.getName()); Thread.sleep(300); try { personProxy.setId(5678); } catch (Exception e) { e.printStackTrace(); } Thread.sleep(300); try { personProxy.setName("kw"); } catch (Exception e) { e.printStackTrace(); } Thread.sleep(300); System.out.println(personProxy.getId()); System.out.println(personProxy.getName()); } } class MyInvocationHandler implements InvocationHandler { private Person person; public MyInvocationHandler(Person person) { this.person = person; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().startsWith("set")) { throw new IllegalAccessException(); } else { return method.invoke(person, args); } } } interface Person { String getName(); void setName(String name); void setId(int id); int getId(); } class MyPerson implements Person { private int id; private String name; public MyPerson(int id, String name) { this.id = id; this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void setId(int id) { this.id = id; } public int getId() { return id; } }
(5) 动态代理很遗憾的地方是,如果某个类没有实现任何interface,那么就无法对这个类添加代理(因为Proxy的static newProxyInstance方法明确指明了interfaces数组必须是interface)
(6) 为了解决(5)中的问题,有一个叫做CGLIB的开源的动态字节码生成库诞生了
-
CGLib
(1) CGLib的原理是:将横切逻辑(即InvocationHandler中实现的)放到子类中,然后让Client使用子类(因为子类和父类拥有相同的超类型,所以Client不管它实际用的是什么),只不过这个子类的字节码是动态生成的
(2) CGLib既可以针对某个实现了interface的类进行扩展,也可以对没有实现任何接口的类进行扩展。但是由于Java原生的Proxy和InvocationHandler两件套对前者支持的够好了,所以CGLib一般用于支持后一种情况
(3) CGLib的具体生成代理类字节码的流程和Proxy+InvocationHandler两件套很像
Enhancer enhancer = new Enhancer(); //这个类目标就是生成代理类(子类) enhancer.setSuperclass(MyPerson.class); //设置一下被代理类是谁 enhancer.setCallback(new RequestCallback()); //再设置一下横切逻辑的实现类是谁(跟InvocationHandler差不多) MyPerson proxy = (MyPerson)enhancer.create(); //这样一个代理子类就创建出来了 proxy.request(); //接下来就可以用这个代理了 ...
-
SpringAOP中,如果被代理对象实现了相应的interface,就用Java动态代理;
如果被代理对象没有实现相应的interface,就用CGLib
4_SpringAOP实现机制
最新推荐文章于 2024-05-16 20:45:51 发布