代理模式定义:为另一个对象提供一个替身或者占位符以控制对这个对象的访问。---《Head First 设计模式》
代理模式换句话说就是给某一个对象创建一个代理对象,由这个代理对象控制对原对象的引用,并且创建这个代理对象后可以在调用原对象时增加一些额外操作。
下面就由一张代理原理图说明:
1.Subject抽象主题,Proxy和RealSubject都要实现的接口。这允许任何客户都可以像处理RealSubject对象一样地处理Proxy.
2.Proxy:代理类,必须持有所代理对象的引用。
3.RealSubject被代理的类,真正做事的对象。Proxy会控制对RealSubject的访问。
举一个栗子:我们有一个看电影的接口,此时有一个只有看电影的类、还有一个代理商,代理了这部电影,并在看电影开始和结束都加入了广告。
看电影接口:
/** * @author monkjavaer * @date 2018/09/05 22:18 */ public interface Subject { void seeMovie(); }
真正的电影:
/** * @author monkjavaer * @date 2018/09/05 22:19 */ public class RealSubject implements Subject{ @Override public void seeMovie() { System.out.println("RealSubject:看电影"); } }
代理商:
/** * 代理类 * @author monkjavaer * @date 2018/09/05 22:20 */ public class Proxy implements Subject { private Subject subject; public Proxy(Subject subject) { this.subject = subject; } @Override public void seeMovie() { System.out.println("Proxy:加广告"); subject.seeMovie(); System.out.println("Proxy:加广告"); } }
测试:
/** * 测试 * @author monkjavaer * @date 2018/09/05 22:22 */ public class ProxyTest { public static void main(String[] args) { Proxy proxy = new Proxy(new RealSubject()); proxy.seeMovie(); } }
结果:
Proxy:加广告
RealSubject:看电影
Proxy:加广告
上面就是我们传说中的静态代理。
好了,说了这么多,总结静态代理123。。。
优点: RealSubject通过Proxy实现了功能的增强。 在没有Proxy的时候,RealSubject依然可以执行。
缺点: Proxy要实现和RealSubject同样的接口Subject,Proxy只能为Subject进行代理;如果有很多接口的实现类需要代理,那么就需要很多的代理类。
嗯,,,既然静态代理有这个缺点,那我们啷个办呢。。。。。。
下面我们就引入动态代理。
jdk动态代理:java动态代理位于java.lang.reflect这个包下。其实现原理就是通过反射获取构造函数对象并生成代理类实例。
java Proxy 源码中有这样的一段描述:
/*
* 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);
}
JDK动态代理的实现步骤:
1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法
2.创建被代理的类以及接口
3.通过Proxy的静态方法newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理类
4.通过代理类调用方法
下面是具体的java代码实现:
场景:小明吃饭前后都有人帮忙做一些事情。。。
/** * 人 */ public interface People { void eat(); }
小明:
public class XiaoMing implements People { @Override public void eat() { System.out.println("小明吃红烧肉"); } }
代理类:
/** * 代理类 * @author monkjavaer * @date 2018/8/31 15:39 */ public class InvocationHandlerImpl implements InvocationHandler { /** * 代理的对象 */ private Object object; public InvocationHandlerImpl(Object object) { this.object = object; } /** * * @param proxy JDK动态生成的最终代理对象 * @param method 调用真实对象的某个方法的Method对象 * @param args 调用真实对象某个方法时接受的参数 * @return 代理对象 * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before:把饭做好"); //当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用 Object invokeObject = method.invoke(object, args); System.out.println("after:洗碗"); return invokeObject; } }
测试:
/** * 小明吃饭 * @author monkjavaer * @date 2018/8/31 15:53 */ public class ProxyTest { public static void main(String[] args) { People people = new XiaoMing(); InvocationHandlerImpl handler1 = new InvocationHandlerImpl(people); //类加载器 ClassLoader classLoader1 = people.getClass().getClassLoader(); //得到全部的接口 Class[] interfaces1 = people.getClass().getInterfaces(); //返回代理类的一个实例 People peopleProxy = (People) Proxy.newProxyInstance(classLoader1, interfaces1, handler1); peopleProxy.eat(); } }
根据上面的jdk动态代理我们得知:
jdk动态代理有局限,必须针对接口,如果没有接口像上面的例子People、目标没有实现接口那么这个办法就GG了。
这时我们就需要用到另一种代理Cglib动态代理。预知Cglib请听下回分解。。。