设计模式——代理模式

 

1、静态代理

         由三部分构成:抽象角色(真实角色和代理角色共同父接口,或者说是共有的功能)、真实角色(实现抽象角色)、代理角色(实现抽象角色,获取真实角色的引用,并附加操作)。除了真实对象和代理对象共同实现父接口的方式外,还可以让代理对象直接继承真实角色,但是此方法不够灵活,一般使用实现接口方式。

  • 抽象角色,声明真实对象和代理对象的共同接口。即真实对象和代理对象共同要实现的行为动作

  • 真实角色,代理角色所代理的对象,亦即我们最终要引用的对象

  • 代理角色,代理角色内部含有对真实角色的引用,从而可以去操作真实对象,同时代理对象提供与真实对象的接口,以便在任何时候都能代替真实对象。同时,代理对象在执行真实对象的操作时,也能附加它自己的操作,相当于对真实对象的封装。

         缺点:每当有一个真实对象需要被代理,就需要创建一个新的代理类,因为代理类内含有对真实对象的引用,代理类需要与真实对象一一对应。这样,当需要代理的真实对象比较多时,代理类就会急剧增加,增加了代码的复杂性,使代码变得尤其繁重,不易维护。也就是说,一个中介只能代理一个房东,这显然是不行的。

(1) 抽象角色(出租房屋)

public interface Rent { 
    public void rent(); 
}

(2) 真实角色(房东)

public class LandLord implements Rent { 
    @Override 
    public void rent() { 
        System.out.println("LandLord"); 
    } 
}

(3) 代理角色(房屋中介)(租房前收中介费,租房后收押金)

public class Proxy implements Rent { 
    private LandLord landLord; 

    @Override 
    public void rent() { 
        agencyFees(); 
        if (null == landLord) 
            landLord = new LandLord(); 
        landLord.rent(); 
        cashPledge(); 
    } 
    
    public void agencyFees() { 
        System.out.println("Agency Fees"); 
    } 
    
    public void cashPledge() { 
        System.out.println("Cash Pledge"); 
    } 
}

2、动态代理(JDK 和 Cglib)

         由三部分构成:抽象角色(真实角色父接口)、真实角色(实现抽象角色)、代理角色(获取真实角色的引用,并附加操作)。

(1) 使用JDK实现动态代理

         JDK实现动态代理需要使用newProxyInstance方法,而且需要重写invoke方法,该方法需要接收三个参数,完整的写法是:

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {}

         注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:

  • ClassLoader loader:指定当前目标对象使用类加载器,获取加载器的方法是固定的

  • Class<?>[] interfaces:目标对象实现的接口的类型,使用泛型方式确认类型

  • InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入

         从上述传入参数,可以看出,使用JDK实现动态代理时,被代理的对象(即真实角色)必须实现一个或者多个接口,否则Class<?>[] interfaces参数将出现错误,导致无法使用动态代理。

a、抽象角色(出租房屋)

public interface Rent { 
    public void rent(); 
}

b、真实角色(房东)

public class LandLord implements Rent { 
    @Override 
    public void rent() { 
        System.out.println("LandLord"); 
    } 
}

c、代理角色1(实现InvocationHandler接口,重写invoke方法)

public class DynamicProxy implements InvocationHandler { 
    private Object real; // 生成真实角色的两种方式 
    
    public DynamicProxy(Object real) { 
        this.real = real; 
    } 
    
    public void setReal(Object real) { 
        this.real = real; 
    } 

    @Override 
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        agencyFees(); 
        Object object = method.invoke(real, args); 
        cashPledge(); 
        return object; 
    } 
    
    public void agencyFees() { 
        System.out.println("Agency Fees"); 
    } 

    public void cashPledge() { 
        System.out.println("Cash Pledge"); 
    } 
}

         客户端调用动态代理:

LandLord landLord = new LandLord(); 
DynamicProxy handler = new DynamicProxy(landLord); 
Rent rent = (Rent) Proxy.newProxyInstance(LandLord.class.getClassLoader(), LandLord.class.getInterfaces(), handler); 
rent.rent();

d、代理角色2(将newProxyInstantce进一步封装,使客户端调用更加简单)

public class DynamicProxy { private Object real; // 生成真实角色的两种方式 

    public DynamicProxy(Object real) { 
        this.real = real; 
    } 

    public void setReal(Object real) { 
        this.real = real; 
    } 

    public Object getProxyInstance() { 
        return Proxy.newProxyInstance(real.getClass().getClassLoader(),real.getClass().getInterfaces(), 
            new InvocationHandler() { 
                @Override 
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
                    agencyFees(); 
                    Object object = method.invoke(real, args); 
                    cashPledge(); 
                    return object; 
                } 
            }); 
    } 

    public void agencyFees() { 
        System.out.println("Agency Fees"); 
    } 

    public void cashPledge() { 
        System.out.println("Cash Pledge"); 
    } 
}

         客户端调用动态代理:

LandLord landLord = new LandLord(); 
Rent rent = (Rent) new DynamicProxy(landLord).getProxyInstance(); 
rent.rent();

(2) 使用Cglib实现动态代理

         静态代理和JDK动态代理模式都要求被代理对象(即真实角色)实现一个或者多个接口,但有时候被代理对象只是一个单独的对象,并没有实现任何的接口,此时就可以使用继承的方式来实现动态代理,这种方法就叫做Cglib代理。

         Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口。它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)。Cglib包的底层是通过使用一个小而快的字节码处理框架ASM来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

a、抽象角色(出租房屋)

public interface Rent { 
    public void rent(); 
}

b、真实角色(房东)

public class LandLord implements Rent { 
    @Override 
    public void rent() { 
        System.out.println("LandLord"); 
    } 
}

c、代理角色(房东)

public class CGLibProxy implements MethodInterceptor { 
    private Object real; 
   
    public CGLibProxy(Object real) { 
        this.real = real; 
    } 

    public Object getProxyInstance() { 
        Enhancer enhancer = new Enhancer(); 
        enhancer.setSuperclass(real.getClass()); 
        enhancer.setCallback(this); 
        return enhancer.create(); 
    } 

    @Override 
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { 
        agencyFees(); 
        Object object = proxy.invokeSuper(obj, args); 
        cashPledge(); 
        return object; 
    } 

    public void agencyFees() { 
        System.out.println("Agency Fees"); 
    } 

    public void cashPledge() { 
        System.out.println("Cash Pledge"); 
    } 
}

         客户端调用动态代理:

LandLord landLord = new LandLord(); 
Rent rent = (Rent) new CGLibProxy(landLord).getProxyInstance(); 
rent.rent();

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值