java中的代理

一.什么是代理

代理的本质是一种设计模式

我们以银行转账系统为例:

1.在不适用代理的情况下实现银行转账系统:
在这里插入图片描述
我们需要将验证、转账、事后服务三个方法写成一个大方法,这样一来代码的功能量过大,我们需要通过代理来降低代码功能量。

2.通过代理实现银行转账系统:
在这里插入图片描述

我们不妨只将转账这个核心功能交给银行自己负责,其他功能交给需要的代理商(比如支付宝、微信等)。这样一来,我们既能降低银行自己本身的工作量,也可以使用户通过代理商进行转账的核心功能,用户通过代理商也无法直接接触到核心功能

在这里插入图片描述

二.代理的好处

1.防止用户直接访问核心,带来一些不必要的危机

2.能够对核心方法进行功能的增强

三.代理的应用

代理分为静态代理和动态代理

静态代理是基于jdk实现

动态代理分为基于jdk实现的动态代理和基于CGLB实现的动态代理
在这里插入图片描述

四.静态代理

1.静态代理的运行模式

在这里插入图片描述
在这里插入图片描述
其模式为由核心类(目标类)生成核心对象(目标对象),由代理类生成代理对象,在由代理对象代理核心对象,而核心类和代理类都会实现同一个接口。
接口起到了一个沟通(通知)的作用,即通知代理类所要代理的核心是什么。

2.通过静态代理实现银行转账系统

zhuanzhang接口:

public interface ZhuanZhang {
    //定义核心方法(转账)
    public void zhuanzhang(String A,String B,Double money);
}

YinHang类:

public class YinHang implements ZhuanZhang{
    public void zhuanzhang(String A,String B,Double money){
        System.out.println(A+"给"+B+"转了"+money+"元");
    }
}

ZhiFuBao类:


public class ZhiFuBao implements ZhuanZhang{
    //定义被代理的类---核心类
    private YinHang yinHang=new YinHang();
 
    private void yanzheng(String A,String B,Double money){
        System.out.println("对A进行了身份验证");
        System.out.println("对B进行了身份验证");
        System.out.println("对转账金额进行了验证");
    }
 
    public void zhuanzhang(String A,String B,Double money){
        yanzheng(A,B,money);
        yinHang.zhuanzhang(A,B,money);
        fuwu();
    }
    private void fuwu(){
        System.out.println("转账完成后进行了服务");
    }
}

Test类:

public class Test {
    public static void main(String[] args) {
        ZhiFuBao zhiFuBao=new ZhiFuBao();
        zhiFuBao.zhuanzhang("张三","李四",100.0);
    }
}

结果如下:
在这里插入图片描述

执行流程如下:
在这里插入图片描述
main()方法入栈,创建zhifubao对象,其中包括转账的方法、验证和服务方法。还会创建yinhang对象。该对象中也有转账方法。首先是zhifubao的转账方法入栈,然后调用yinhang的转账方法在这个过程中也完成了业务的增强。

3.静态代理的优点

1.代理对象和目标对象在编译时就确定了关系,所以这种代理方式的安全性较高。
2.代理类可以拥有额外的功能,比如可以在调用目标方法前后增加一些额外的操作。

4.静态代理的缺点

1.用一个代理类代理多个目标类是很难实现的,工作量巨大。
2.一旦目标接口增加方法,代理类也需要修改,不符合开闭原则(对扩展开放,对修改关闭)

举例:

以衣服工厂和鞋子工厂为例:

销售衣服:

Clothes接口:

public interface Clothes {
    public void ByClothes(String size);
}

ClothesFactory类:

public class ClothesFactory implements Clothes{
    @Override
    public void ByClothes(String size) {
        System.out.println("定制一件"+size+"的衣服");
    }
}

XSD类:

public class XSD implements Clothes{
    //代理目标类
    private ClothesFactory clothesFactory=new ClothesFactory();
    private void fuwu(){
        System.out.println("销售店进行一系列服务");
    }
 
    @Override
    public void ByClothes(String size) {
        clothesFactory.ByClothes(size);
        fuwu();
    }
}

Test类:

public class Test {
    public static void main(String[] args) {
        //静态代理
        XSD xsd=new XSD();
        xsd.ByClothes("XXL");
}

继续销售鞋子:

Shoes接口:

public interface Shoes {
    public void ByShoes(String size);
}

ShoesFactory类:

public class ShoesFactory implements Shoes{
    @Override
    public void ByShoes(String size) {
        System.out.println("定制一件"+size+"的鞋子");
    }
}

XSD类:

public class XSD implements Clothes,Shoes{
    //代理目标类
    private ClothesFactory clothesFactory=new ClothesFactory();
    private ShoesFactory shoesFactory=new ShoesFactory();
 
    private void fuwu(){
        System.out.println("销售店进行一系列服务");
    }
 
    @Override
    public void ByClothes(String size) {
        clothesFactory.ByClothes(size);
        fuwu();
    }
 
    @Override
    public void ByShoes(String size) {
        shoesFactory.ByShoes(size);
        fuwu();
    }
}

首先定义一个Clothes接口和一个核心方法ByClothes,在创建一个核心类ClothesFactory并实现核心方法ByClothes,接着创建代理类XSD,实现核心方法,首先第一步代理目标类,创建ClothesFactory对象,然后实现代理类的核心方法,调用核心类对象的核心方法。然后添加一个服务方法。在创建一个Test类测试。

接着定义一个Shoes接口和一个核心方法ByShoes,在创建一个核心类ShoeFactory并实现核心方法Byshoes。接着在代理类继承Shoes接口并 在代理类当中创建ShoeFactory对象,并实现Shoes接口当中的核心方法。

此时我们发现每增加一个目标类,就需要创建新的接口并在代理类当中不断进行扩充。每个代理对象代理的目标太多,模式图如下。

在这里插入图片描述

五.动态代理

动态代理是指在程序运行时动态地创建代理类,无需程序员手动编写代理类的源代码。动态代理主要依靠JDK的反射(Reflection)API来实现。Java中提供了两种动态代理机制:JDK动态代理和CGLIB动态代理。
基于上述问题就引出了动态代理。以上述问题为例(衣服工厂、鞋子工厂)。一个代理类可以实现多个的代理对象,每个代理对象只匹配一个目标对象,即代理类动态的生成一个代理对象去匹配一个目标对象。
在这里插入图片描述
而对于静态代理来说,哪怕如动态代理一样生成一个新的对象,新的对象依旧包含所有目标对象,无法单独匹配一个对象,比如在上述举例中Test类中创建两个XSD对象,其展示图和内存图如下。

在这里插入图片描述
在这里插入图片描述

1.动态代理的实现

DTXSD类

public class DTXSD{
    private Object object;
    private DTXSD(Object o){
        object=o;
    }
}

Test类:

public class Test{
    public static void main(String[] args){
        ClothesFactory clothesFactory=new ClothesFactory();
        DTXSD dtxsd1=new DTXSD(clothesFactory);
        //也可以写成:DTXSD dtxsd1=new DTXSD(new ClothesFactory());
 
 
        ShoesFactory shoesFactory=new ShoesFactory();
        DTXSD dtxsd2=new DTXSD(shoesFactory);
    }
}

流程图:
在这里插入图片描述

2.动态代理模式的调用

(1)DTXSD类中写一个getProxyInstance()方法

//动态实现相关接口
//object.getClass():获取目标类的类对象
    public Object getProxyInstance(){
        return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces() ,this);
}

(2)在Test类中调用getProxyInstance()方法

调用getProxyInstance()方法就能获取接口,就能知道目标类中的核心方法是什么

DTXSD dtxsd1=new DTXSD(new ClothesFactory());
dtxsd1.getProxyInstance();

(3)调用该核心方法

DTXSD类中实现InvocationHandler接口,实现一个invoke()方法

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return null;
}

三个参数 :

(1)Object:jdk创建的代理类,无需赋值

(2)Method:目标类中的方法,jdk提供,无需赋值

(3)Object[]:目标类中的方法的参数,jdk提供,无需赋值

调用核心方法:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        method.invoke(object,args);
        return null;
}

(4)知道核心方法了,用接口去接收

Clothes clothe=(Clothes) dtxsd1.getProxyInstance();
Shoes shoes=(Shoes) dtxsd2.getProxyInstance();

为什么用接口进行接收?
因为在整个DTXSD类中,只是理论的知道了核心方法是什么,想要调用只能通过Clothes接口接收,对里面的ByClothes()方法进行调用,因为在整个流程中没有任何一个地方能显性的调用ByClothes()方法。

如果想要调用,只能通过Clothes接口或者ClothesFactory类,但是通过ClothesFactory类调用不行,原因如下

public class Test {
    public static void main(String[] args) {
        ClothesFactory clothesFactory=new ClothesFactory();
        clothesFactory.ByClothes("XXL");
        System.out.println("--------------------------------");
        DTXSD dtxsd1=new DTXSD(clothesFactory);
        Clothes clothes=(Clothes) dtxsd1.getProxyInstance();
        clothes.ByClothes("XL");
    }
}

在这里插入图片描述
第一种调用方法的流程图:
在这里插入图片描述
第二种调用方法的流程图:
在这里插入图片描述
代码如下:

Clothes接口:

public interface Clothes {
    public void ByClothes(String size);
}

ClothesFactory类:

public class ClothesFactory implements Clothes{
    @Override
    public void ByClothes(String size) {
        System.out.println("定制一件"+size+"的衣服");
    }
}

Shoes接口:

public interface Shoes {
    public void ByShoes(String size);
}

ShoesFactory类:

public class ShoesFactory implements Shoes{
    @Override
    public void ByShoes(String size) {
        System.out.println("定制一件"+size+"的鞋子");
    }
}

DTXSD类:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
 
//动态代理类
public class DTXSD implements InvocationHandler {
    private Object object;
    public DTXSD(Object o){
        object=o;
    }
 
    public Object getProxyInstance(){
        return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces() ,this);
    }
 
 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        method.invoke(object,args);
        fuwu();
        return null;
    }
 
    private void fuwu(){
        System.out.println("销售点进行一系列服务");
    }
}

Test类:


public class Test {
    public static void main(String[] args) {
 
        //动态代理
        DTXSD dtxsd1=new DTXSD(new ClothesFactory());
        Clothes clothe=(Clothes) dtxsd1.getProxyInstance();//这里是用接口进行接收
        clothe.ByClothes("XXL");
 
        DTXSD dtxsd2=new DTXSD(new ShoesFactory());
        Shoes shoes=(Shoes) dtxsd2.getProxyInstance();//用接口进行接收
        shoes.ByShoes("42");
    }
}

输出如下:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值