目录
一:代理模式
1.引用场景
(1)在异国他乡的A要给朋友C送朵花,但是距离限制
(2)于是打电话给与C同城的B委托他去买花并且送给C
在这个案例中实际上是A送花给C,但是由于各种原因,A只能委托B来送花
2.引出概念
(1)代理模式的概念:在有些情况下第三方客户端(C)不能或者不想直接调用里一个对象(A),只能通过中间对象(B)在其进行中介调和。
(2)相关角色:
抽象角色(或者方法):声明真实对象和代理对象的共同接口,(比如上图中的A和B的共同行为:送花)
真是角色:真实所为的对象,(比如上图中的A对象)
代理角色:替代真实角色所进行行为的角色(比如上图中的B),其中包含的对真实角色行为的具体实现(比如上图中的B可能
需要先收到A给他转账,然后去花店买花,最后才能送出)。
3.代理模式
代理模式分为:静态代理和动态代理,之后再举完例子会详细比较。
二:静态代理
1.抽象角色
package test.com;
/**
* 抽象角色,送花行为
* @author monxz
*
* @time 2019年7月17日
*/
public abstract class Subject {
//这里抽象出A和C的共同行为送花
public abstract void sendFlower();
}
2.真实角色
package test.com;
/**
* 真实角色A
* @author monxz
*
* @time 2019年7月17日
*/
public class RealSubject extends Subject{
/**
* 真实角色A,想要给C送花
*/
@Override
public void sendFlower() {
System.err.println("我是A,其实是我要给C送花的!");
}
}
3.代理角色
package test.com;
/**
* 代理角色B
* @author monxz
*
* @time 2019年7月17日
*/
public class ProxySubject extends Subject{
private RealSubject realSubject=null;
@Override
public void sendFlower() {
if(realSubject ==null ) {
realSubject =new RealSubject();
}
realSubject.sendFlower();
//具体实现对A的行为
Jtsx();
}
//具体实现A的行为:送花
private void Jtsx() {
System.err.println("A老哥,我是B,您的专属xx送花!");
System.err.println("A老哥,请您在平台付款哦");
System.err.println("A老哥,我已经在xxx花店买完花了");
System.err.println("A老哥,我已经出发送花了!");
}
}
4.客户端
package test.com;
public class test {
//c开始接收到花了
public static void main(String[] args) {
Subject subject=new ProxySubject();
subject.sendFlower();
System.err.println("是A么?我这边已经接到B送的花了,谢谢你");
}
}
三.动态代理实现一--jdk的动态代理
1.引出场景
在上面的静态代理的例子中,代理角色在完成对真实角色的具体实现,其实是使用了真实角色的对象(其实就是在proxySubject中的sendFlower()使用了new RealSubject())。这种情况下,如果又来一个真实角色(比如E)需要代理角色完成,那么就有需要创建一个新的realSubject。如下图:这里就要创建N多个RealSubject。
其实说白了,在静态代理中,依赖的是类的适配,一个类适配一个代理。这样情况下,即使在同种类型下,实现多个代理也要创建多个不同的类。有没有办法把同一种场景抽象出来,然后以多个不同对象来适配呢?这样就引出了动态代理了,在动态代理中,一个对象适配一个代理,那么把这些对象共同抽象出一个类就可以了。
2.引出概念
这里我们使用jdk的动态代理,使得在代码在运行时,能够动态适配方法等。这里大家就明白了使用jdk的动态代理依赖jdk的反射原理(java.lang.reflect)。
(1)Interface InvocationHandler:
该接口中仅定义了一个方法Object:invoke(Objectobj,Method method, Object[] args)。在实际使用时,第一个参数obj一般是
指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。
(2)Proxy:
该类即为动态代理类,作用类似于上例中的ProxySubject,其中主要包含以下内容:
Protected Proxy(InvocationHandler h):构造函数,估计用于给内部的h赋值。
3.这里依次贴出代理
package test.com;
/**
* 抽象角色,送花行为
* @author monxz
*
* @time 2019年7月17日
*/
//抽象类改为接口的实现
public interface Subject {
//这里抽象出多个不同的真实角色的共同行为送花
public void sendFlower();
}
package test.com;
import java.util.Random;
/**
* 真实角色
* @author monxz
*
* @time 2019年7月17日
*/
public class RealSubject implements Subject{
//加个随机数,区分用户。
private Integer name =new Random().nextInt(100)+ 0;
@Override
public void sendFlower() {
System.err.println("=============我是"+name+",我想委托你们给c送花");
}
}
package test.com;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 代理角色B
* @author monxz
*
* @time 2019年7月17日
*/
//现在可以代理任何行为了
public class ProxySubject implements InvocationHandler{
private Object obj;
public ProxySubject(Object obj) {
this.obj=obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
clq();
//方法的执行
method.invoke(obj,args);
hcl();
return null;
}
//前置处理
public void clq() {
System.err.println("您好,这里是xxx代办");
System.err.println("请问您要帮办什么业务呢?");
}
//后置处理
public void hcl() {
System.err.println("好的,我们即将为您服务!");
}
}
package test.com;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class test {
public static void main(String[] args) {
RealSubject rl=new RealSubject();
InvocationHandler handler=new ProxySubject(rl);
Class cls=rl.getClass();
//=====引向方法
Subject subject=(Subject) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(),handler);
subject.sendFlower();
}
}
4.说明
看完这段代码,我们发现不仅仅解决了动态代理的问题,还充分扩大了在运行时动态添加。
四:动态代理实现二--cglib
1.引出问题
在使用jdk的动态代理,我们发现进行创建代理对象是基于接口作为参数的(因为在后面脑袋里方法中式以接口内的方法名称进
行调用的),也就是说如果方法不是以接口定义那就很坑爹了啊。
2.引出概念
这里我们就引出一种新的实现动态代理cglib。cglib针对的是类来实现动态代理的(区分这里的类实现和静态代理的类依赖的区别哦),针对指定的业务生成一个子类,并覆盖其中的实现。这里我们发现其可以不需要抽出抽象角色或者这么说其抽象角色的实现完全可以放在真实角色中。
3.贴出代码
说明,这里依赖于cglib.jar(如果你不是maven项目还需要引入org.ow2.asm » asm.jar)
package test.com;
import java.util.Random;
/**
* 真实角色
* @author monxz
*
* @time 2019年7月17日
*/
public class RealSubject {
//加个随机数,区分用户。
private Integer name =new Random().nextInt(100)+ 0;
public void sendFlower() {
System.err.println("=============我是"+name+",我想委托你们给c送花");
}
}
package test.com;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* 代理角色B
* @author monxz
*
* @time 2019年7月17日
*/
//现在可以代理任何行为了
public class ProxySubject implements MethodInterceptor{
private Object target;
//相当于JDK动态代理中的绑定
public Object getInstance(Object target) {
this.target = target; //给业务对象赋值
Enhancer enhancer = new Enhancer(); //创建加强器,用来创建动态代理类
enhancer.setSuperclass(this.target.getClass()); //为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
//设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
enhancer.setCallback(this);
// 创建动态代理类对象并返回
return enhancer.create();
}
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
clq();
arg3.invokeSuper(arg0, arg2);
hcl();
return null;
}
//前置处理
public void clq() {
System.err.println("您好,这里是xxx代办");
System.err.println("请问您要帮办什么业务呢?");
}
//后置处理
public void hcl() {
System.err.println("好的,我们即将为您服务!");
}
}
package test.com;
public class test {
public static void main(String[] args) {
RealSubject rl= new RealSubject();
ProxySubject ps=new ProxySubject();
RealSubject rlcglib=(RealSubject) ps.getInstance(rl);
rlcglib.sendFlower();
}
}
4.jdk动态代理和cglib动态代理的区别
cglib的动态代理是基于类的继承来实现的,jdk是基于接口实现的,区别就是java中的继承和接口的区别搂。
五:代理模式使用的原因和场景
在上述中,我们仅仅提出了一些有些偏差的使用户场景。
实际上,在spring中的AOP中就是了jdk的动态代理和cglib的动态代理。