代理模式(Proxy)

代理模式(Proxy)

SpringAOP和SpingMVC底层实现使用了代理模式。

代理模式的定义与特点

代理模式的定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
代理模式的主要优点有:

  • 代理模式会造成系统设计中类的数量增加
  • 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
  • 增加了系统的复杂度;

那么如何解决以上提到的缺点呢?答案是可以使用动态代理方式

代理模式的结构与实现

代理模式的结构比较简单,主要是通过定义一个继承抽象主题的代理来包含真实主题,从而实现对真实主题的访问,下面来分析其基本结构和实现方法。
代理模式的主要角色如下。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OIBkjsgz-1622388001621)(C:\Users\蜗牛\AppData\Roaming\Typora\typora-user-images\image-20210528171646601.png)]

**静态代理模式:**由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的 .class 文件就已经存在了。

角色分析:
●抽象角色: 一般会使用接口或者抽象类来解决
●真实角色:被代理的角色
●代理角色:代理真实角色,代理真实角色后,我们一般会做- -些附属操作
●客户:访问代理对象的人。

**代理模式的好处: **

●可以使真实角色的操作更加纯粹!不用去关注一-些公共的业务
●公共也就就交给代理角色!实现了业务的分工!
●公共业务发生扩展的时候,方便集中管理!

缺点:
●一个真实角色就会产生一 个代理角色;代码量会翻倍** 开发效率会变低**

代码步骤:
1.接口

package GOF23.proxy.demo01;
//出租房子
public interface Rent {
    public void rent();
}

2.真实角色

package GOF23.proxy.demo01;
//出租房子
public class Host implements Rent {

    @Override
    public void rent() {
        System.out.println("房东要出租房子");

    }
}

3.代理角色

package GOF23.proxy.demo01;
//代理房东出租房子
public class Proxy implements Rent{
    private  Host host;

    public Proxy(){
    }
public Proxy(Host host){
        this.host=host;//直接代理房东租房子。
}
    @Override
    public void rent() {
        host.rent();
        fare();
    }

//    中介收中介费
    public void fare(){
        System.out.println("收中介费");
    }
}

4.客户端访问代理角色

package GOF23.proxy.demo01;
//租客
public class Tenant {
    public static void main(String[] args) {
        Host host=new Host();
//        host.rent();
        Proxy proxy = new Proxy(host);//中介帮房东租房子
        proxy.rent();//会调用中介的方法,比如中介要收费用,此时我们只面对中介

    }
}

**动态代理模式:**在程序运行时,运用反射机制动态创建而成

package GOF23.proxy.demo2;

public class UserServicelmpl implements UserSerevice{

    @Override
    public void add() {
        System.out.println("增加了一个用户");
    }

    @Override
    public void update() {
        System.out.println("更新了一个用户");
    }

    @Override
    public void delete() {
        System.out.println("删除了一个用户");
    }

    @Override
    public void query() {
        System.out.println("查找了一个用户");
    }
}

package GOF23.proxy.demo2;

public class Client {
    public static void main(String[] args) {
        UserServicelmpl userServicelmpl =new UserServicelmpl();
        userServicelmpl.add();
        userServicelmpl.update();
        userServicelmpl.delete();
        userServicelmpl.query();
    }
}

package GOF23.proxy.demo2;

import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;

public interface UserSerevice {
    public void add();
    public void update();
    public void delete();
    public void query();

}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kEAKxlfn-1622388001623)(C:\Users\蜗牛\AppData\Roaming\Typora\typora-user-images\image-20210530204008984.png)]

假设此时需要加一个日志功能,此时就使用代理模式非常适合,因为不用改变原代码

没有使用代理的情况,,代码如下,

package GOF23.proxy.demo2;

public class UserServicelmpl implements UserSerevice{

    @Override
    public void add() {
        System.out.println("日志:此时增加了一个用户");
        System.out.println("增加了一个用户");
    }

    @Override
    public void update() {
        System.out.println("日志:此时更新了一个用户");
        System.out.println("更新了一个用户");
    }

    @Override
    public void delete() {
        System.out.println("日志:此时删除了一个用户");
        System.out.println("删除了一个用户");
    }

    @Override
    public void query() {
        System.out.println("日志:此时查找了一个用户");
        System.out.println("查找了一个用户");
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WkTjMcBF-1622388001624)(C:\Users\蜗牛\AppData\Roaming\Typora\typora-user-images\image-20210530204025832.png)]

可见,对原代码进行了更改,我们是不推荐这样操作。那么,此时就使用代理模式非常适合,因为不用改变原代码

代码如下

增加了UserServiceProxy 类

package GOF23.proxy.demo2;

public class UserServiceProxy implements UserService {
    private UserServicelmpl userService;

    public void setUserService(UserServicelmpl userService){
        this.userService=userService;
    }
//    日志方法
    public void  log(String msg){
        System.out.println("日志:使用了"+msg+"方法!");
    }
    @Override
    public void add() {
        log("add");
        userService.add();

    }

    @Override
    public void update() {
        log("update");
userService.update();
    }

    @Override
    public void delete() {
        log("delete");
        userService.delete();

    }

    @Override
    public void query() {
        log("query");
        userService.query();

    }
}

结构如下

package GOF23.proxy.demo2;

public class Client {
    public static void main(String[] args) {
        UserServicelmpl  userServicelmpl =new UserServicelmpl();
//        userServicelmpl.add();
//        userServicelmpl.update();
//        userServicelmpl.delete();
//        userServicelmpl.query();
        UserServiceProxy proxy= new UserServiceProxy();
       proxy.setUserService(userServicelmpl);
      proxy.add();
      proxy.update();
      proxy.delete();
      proxy.query();

    }
}

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mKi1Rqf7-1622388001625)(C:\Users\蜗牛\AppData\Roaming\Typora\typora-user-images\image-20210530212236430.png)]

动态代理

动态代理和静态代理角色一-样
动态代理的代理类是动态生成的,不是我们直接写好的!
动态代理分为两大类:基于接口的动态代理,基于类的动态代理
基于接口–JDK动态代理
基于类: cglib
java字节码实现:

两个类:

Proxy :代理

InvocationHandler :调用处理程序

动态代理的好处:
●可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
●公共也就就交给代理角色!实现了业务的分工!
●公共业务发生扩展的时候,方便集中管理!
●一 个动态代理类代理的是一个接口, 一般就是对应的一 类业务

●一个动态代理类可以代理多个类,只要是实现了同一一个接口即可!|

使用工具ProxyInvocationHandler对UserServicelmpl设置动态代理。

1、 ProxyInvocationHandler

package GOF23.proxy.dome4;
//动态代理公式
import GOF23.proxy.demo3.Rent;

import java.lang.annotation.Target;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyInvocationHandler implements InvocationHandler {
    //被代理的接口
    private Object target;

    public void setTarget(Object target) {

        this.target = target;
    }

    //生成得到代理类
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
        //此步骤固定不变,下次用只用改“rent”.

    }

    //处理代理实例,并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //动态代理的本质就是使用反射机制实现,
        log(method.getName());
        Object result = method.invoke(target, args);

        return result;

    }
    //    日志方法
    public void  log(String msg){
        System.out.println("日志:使用了"+msg+"方法!");
    }
}

2、Client

package GOF23.proxy.dome4;
import GOF23.proxy.demo2.UserService;
import GOF23.proxy.demo2.UserServicelmpl;

import java.lang.annotation.Target;

public class Client {
    /*
     public static void main(String[] args) {
        //真实角色
        Host host=new Host();
        //代理角色,不存在
        ProxyInvocationHandler pih=new ProxyInvocationHandler();
        //通过调用程序处理角色来处理我们要调用的接口对象
         pih.setRent(host);
         Rent proxy=(Rent) pih.getProxy();//这里的proxy就是动态生成的
         proxy.rent();
     */
    public static void main(String[] args) {
        /*
        使用工具ProxyInvocationHandler对UserServicelmpl设置动态代理。
         */
        //真实角色
        UserServicelmpl userService=new UserServicelmpl();
        //代理角色,不存在
        ProxyInvocationHandler pih=new ProxyInvocationHandler();
        //通过调用程序处理角色来处理我们要调用的接口对象(设置要代理的对象)
        pih.setTarget(userService);
        //动态生成代理类
        UserService proxy=(UserService) pih.getProxy();
        proxy.add();
        proxy.delete();
    }
}

结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5pl0WNek-1622388001626)(C:\Users\蜗牛\AppData\Roaming\Typora\typora-user-images\image-20210530231842749.png)]
UserServicelmpl userService=new UserServicelmpl();
//代理角色,不存在
ProxyInvocationHandler pih=new ProxyInvocationHandler();
//通过调用程序处理角色来处理我们要调用的接口对象(设置要代理的对象)
pih.setTarget(userService);
//动态生成代理类
UserService proxy=(UserService) pih.getProxy();
proxy.add();
proxy.delete();
}
}




结果

[外链图片转存中...(img-5pl0WNek-1622388001626)]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hello蜗牛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值