【设计模式】结构型01代理模式(Proxy Pattern)
代理模式(Proxy Pattern)
定义:顾名思义,增加中间层,为其他对象提供一种代理以控制对这个对象的访问。核心在于代理二字。
1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。
静态代理与动态代理:
静态代理类:由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理类:在程序运行时,运用反射机制动态创建而成。
静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。
静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。
动态代理是实现JDK里的InvocationHandler接口的invoke方法,但注意的是代理的是接口,也就是你的业务类必须要实现接口,通过Proxy里的newProxyInstance得到代理对象。
代码:静态代理:
这里笔者通过一个替考的例子,演示了小学生王小虎找人替考被骗子骗的故事:
package com.pat.proxy.staticproxy;
/**
* 统一接口
* @author ZX
*
*/
public interface Student {
void exam();
}
/**
* 被代理类-学生王小虎
* @author ZX
*
*/
class BadStudent implements Student{
private String name="王小虎";
@Override
public void exam() {
System.out.println(name+"参加考试");
}
public BadStudent(){
}
public BadStudent(String name){
this.name=name;
}
}
/**
* 代理类-替考的骗子
* 静态代理只代理固定的类
* @author ZX
*
*/
class Swindler implements Student{
private String name;
private BadStudent badStudent;
@Override
public void exam() {
//非重点,前后可以添加某些操作
System.out.println(name+"进入考场");
badStudent= new BadStudent(name);
badStudent.exam();
System.out.println(name+"没交试卷");
}
public Swindler(String name){
this.name=name;
}
}
测试类:
package com.pat.proxy.staticproxy;
public class Test {
public static void main(String[] args) {
Student s = new Swindler("老司机");
s.exam();
}
}
结果:
老司机进入考场
老司机参加考试
老司机没交试卷
这就说明了一个问题,做生意得讲诚信,你好好搞替考业务,发展起来说不定还能开个连锁。这样子做生意是不会走的长远的!但是考虑到这个替考人没上过小学,也只能怪小虎人品不好了。代码:动态代理:
以下是动态代理的演示,采用了JDK提供的工具,省了几十行代码:
package com.pat.proxy;
/**
* 本类中
*/
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 被代理类接口
*/
public interface People {
public int getNum() ;
}
/**
* 被代理类实现类
*/
class Peopleimpl implements People{
private int num=100;
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
}
/**
*代理类,需要实现InvocationHandler接口
*/
class MyInv implements InvocationHandler{
//被代理对象
Object obj;
public MyInv(Object obj){
this.obj=obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(obj, args);
return result;
}
}
测试类:
package com.pat.proxy;
import java.lang.reflect.Proxy;
import java.util.Objects;
/**
* 测试类:
* 补充,非空判断这么用比较装逼:
* People p2=null;
* Objects.requireNonNull(p2,"第二个对象不能为空");
*/
public class Test2 {
public static void main(String[] args) {
//动态代理有JDK和CGLIB两种工具,cglib是更底层的写法,所以效率会高一些
People p = new Peopleimpl();
//真正的代理者。
MyInv my = new MyInv(p);
//第一个参数是类加载器,第二个参数是这个代理者实现哪些接口(与被代理者实现的是相同的接口)
People pp= (People)Proxy.newProxyInstance(p.getClass().getClassLoader(),p.getClass().getInterfaces(),my);
System.out.println(pp.getNum());
}
}
结果:
100
动态代理JDK中的实现:
每日手记:
2018年5月15日11:16:07
Returns an instance of a proxy class for the specified interfaces
返回一个指定接口的代理实例
that dispatches method invocations to the specified invocation
handler.
将方法调用分派到指定的处理程序
/**
//糊上注释是为了看一下老外是怎么写注释的,即使看不懂全部,也可以对比自己的注释参考一下,其实注释写得多也给人感觉很牛逼
* 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.查找或生成指定的代理类。
//如果由给定的加载器定义的代理类。
//给定的接口存在,这将简单地返回缓存的副本(这个缓存取自哪里?猜想:类加载???);
//否则,它将通过ProxyClassFactory创建代理类。
//如果没有接口参数intfs,则会抛出空指针
*/
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);
}
}