设计模式--代理模式

参考:尚硅谷 学校课件 

因为代理模式比较重要,所以单独置为一篇。 

代理模式:

代理模式的基本介绍

1) 代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理 对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的 功能操作,即扩展目标对象的功能。

2) 被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象

3) 代理模式有不同的形式, 主要有三种 静态代理、动态代理 (JDK代理、接口代理)和 Cglib代理 (可以在内存动态的创建对象,而不需要实现接口, 他是属于动态代理的范畴) 。

 

 角色说明:

Subject:抽象主题角色

抽象主题类声明了真实主题类和代理类的公共方法,它可以是接口、抽象类或具体类,客户端针对抽象主题类编程,一致性地对待真实主题和代理主题,典型的抽象主题类代码如下:

public interface AbstractSubject
{
    public void request();
}

 

RealSubject:真实主题角色

真实主题类继承了抽象主题类,提供了业务方法的具体实现,其典型代码如下:

public class RealSubject implements AbstractSubject
{  
    public void request()
    {  
        //业务方法具体实现代码  
    }  
} 

Proxy:代理主题角色,

代理类也是抽象主题类的子类,它维持一个对真实主题对象的引用,调用在真实主题中实现的业务方法,在调用时可以在原有业务方法的基础上附加一些新的方法来对功能进行扩充或约束,最简单的代理类实现代码如下:

public class Proxy implements AbstractSubject
{  private RealSubject realsubject = new RealSubject(); //维持一个对真实主题对象的引用  
    public void prerequest()   
    {       …...      }  
    public void request()   
    {  
        prerequest();  
        realsubject.request(); //调用真实主题对象的方法  
        postrequest();  
    }  
    public void postrequest()   
    {       ……     }  
} 

 静态代理示例:

实例一:论坛权限控制代理
在一个论坛中已注册用户和游客的权限不同,已注册的用户拥有发帖、修改自己的注册信息、修改自己的帖子等功能;而游客只能看到别人发的帖子,没有其他权限。使用代理模式来设计该权限管理模块。
在本实例中我们使用代理模式中的保护代理,该代理用于控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。

结构解析:

 

 代码示例:

//===================
//抽象主题,也就是被代理的抽象方法
public interface AbstractPermission
{
    public void modifyUserInfo();
    public void viewNote();
    public void publishNote();
    public void modifyNote();
    public void setLevel(int level);
}

//======================
//被代理具体方法,其实这里与Spring关系很紧密,后续看Spring源码细嗦
public class RealPermission implements AbstractPermission
{    
    public void modifyUserInfo()
    {
        System.out.println("修改用户信息!");
    }

    public void viewNote()
    {   }
    
    public void publishNote()
    {
        System.out.println("发布新帖!");
    }
    
    public void modifyNote()
    {
        System.out.println("修改发帖内容!");
    }
    
    public void setLevel(int level)
    {    }
}

//===================
//代理,对被代理对象功能补充
public class PermissionProxy implements AbstractPermission
{
    private RealPermission permission=new RealPermission();
    private int level=0; 
        
    public void modifyUserInfo()
    {
        if(0==level)
        {
            System.out.println("对不起,你没有该权限!");
        }
        else if(1==level)
        {
            permission.modifyUserInfo();
        }
    }
    
    public void viewNote()
    {
        System.out.println("查看帖子!");
    }
    
    public void publishNote()
    {
        if(0==level)
        {
            System.out.println("对不起,你没有该权限!");
        }
        else if(1==level)
        {
            permission.publishNote();
        }        
    }
    
    public void modifyNote()
    {
        if(0==level)
        {
            System.out.println("对不起,你没有该权限!");
        }
        else if(1==level)
        {
            permission.modifyNote();
        }        
    }
    
    public void setLevel(int level)
    {
        this.level=level;
    }
}

动态代理:

要想实现动态代理,需要解决的问题?
问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。
问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。

 1.JDK动态代理

JDK解决方案:

问题一:使用Proxy方法,这个方法详细说明参考上篇反射: https://www.cnblogs.com/wangid3/p/14159834.html

class ProxyFactory{
    //调用此方法,返回一个代理类的对象。解决问题一
    public static Object getProxyInstance(Object obj){//obj:被代理类的对象
        MyInvocationHandler handler = new MyInvocationHandler();
        handler.bind(obj);
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
    }
}

问题二:

class MyInvocationHandler implements InvocationHandler{
    private Object obj;//需要使用被代理类的对象进行赋值
    public void bind(Object obj){
        this.obj = obj;
    }
    //当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke()
    //将被代理类要执行的方法a的功能就声明在invoke()中
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        HumanUtil util = new HumanUtil();//1
        util.method1();//2
        //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
        //obj:被代理类的对象
        Object returnValue = method.invoke(obj,args);
        util.method2();//3
        //1 2 3 这三条语句是使用另外的类对被代理方法进行增强,可以叫做通用方法
        //上述方法的返回值就作为当前类中的invoke()的返回值。
        return returnValue;
    }
}

解决上面两个问题,我们就可以使用JDK动态代理

public class ProxyTest {
    public static void main(String[] args) {
        SuperMan superMan = new SuperMan();
        //proxyInstance:代理类的对象
        Human proxyInstance =(Human)ProxyFactory.getProxyInstance(superMan);
        proxyInstance.eat("四川麻辣烫");//结果执行代理方法,即被代理方法加上通用方法
    }
}

CGlib动态代理:

cglib解决方案:cglib适合处理被代理类想被代理使用的类,没有实现特定接口时使用,他以继承的方式实现多态。

问题一:

class CGlibFactory{
    //调用此方法,返回一个代理类的对象。解决问题一
    public static Object getCGlibCGlibInstance(Object obj){//obj:被代理类的对象
        MyInteceptor myInterceptor= new MyInterceptor();
        myInterceptor.bind(obj);
        return Enhancer.create.create(obj.getClass(),myInterceptor);
    }
}

问题二:

intercepto()方法参数:

Object为由CGLib动态生成的代理类实例

Method为上文中实体类所调用的被代理的方法引用,

Object[]为参数值列表

MethodProxy为生成的代理类对方法的代理引用。

目前咱了解的比较浅,Method参数不怎么使用,判断方法名字可以用,通过该类反射执行被代理或者代理类的方法没见过使用Method。该功能由MethodProxy实现,补充的是,MethodProxy有两个方法,分别是invoke和invokeSuper,有两个参数,实现该方法的类对象和参数列表:

invokeSuper调用的是被代理类的方法, 但只有代理类才存在基类, 必须使用代理类作为obj参数调用
invoke调用的是增强方法, 必须使用被代理类的对象调用, 使用代理类会造成OOM

也就是 MethodProxy.invoke(obj,args);和JDK动态代理差不多

(invokeSuper效率更高,用的更多,not sure)

class MyInterceptor implements MethodInterceptor{
    private Object obj;//需要使用被代理类的对象进行赋值
    public void bind(Object obj){
        this.obj = obj;
    }
    //当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:intercept()
    //将被代理类要执行的方法a的功能就声明在intercept()中
    @Override
  public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
        HumanUtil util = new HumanUtil();//1
        util.method1();//2
        //Object o  CGLib动态生成的代理类实例
        if("save".equals(method.getName())){//专门拦截的方法
         System.out.println("特殊操作-----");
        return methodProxy.invokeSuper(o,objects);
     }
        Object returnValue = methodProxy.invokeSuper(o,objects);
        util.method2();//3
        //1 2 3 这三条语句是使用另外的类对被代理方法进行增强,可以叫做通用方法
        //上述方法的返回值就作为当前类中的invoke()的返回值。
        return returnValue;
    }
}

使用cglib:

public class ProxyTest {
    public static void main(String[] args) {
        Human human = new Human();
        //proxyInstance:代理类的对象
        Human proxyInstance =(Human)CGlibFactory.getCGlibProxyInstance(human);
        proxyInstance.eat("四川麻辣烫");//结果执行代理方法,即被代理方法加上通用方法
    }
}

 

posted @ 2020-12-19 16:42  wangid3  阅读(152)  评论(0)    收藏  举报