代理模式——子类代理、静态代理、动态代理,面向接口编程

一、代理模式定义:

  • 目标对象不可访问,通过代理对象增强功能访问。
  • 举个例子:
    • 房东==>目标对象
    • 房屋中介==>代理对象
    • 租客==>客户端

二、代理模式的作用:

  • 控制目标对象的访问。
    • 举个例子:为了避免麻烦,房东(目标对象)拒绝与租客(客户端)频繁见面,房屋中介(代理对象)也会控制你与房屋中介(代理对象)见面。
  • 增强功能。
    • 举个例子:房屋中介(代理对象)会给租客(客户端)提供一系列房东(目标对象)提供不了的服务(功能)。

三、代理模式分类:

  • 子类代理。
  • 静态代理。
  • 动态代理。

四、子类代理模式:

  1. 特点:
  • 通过子类继承父类,子类在父类的基础上进行功能业务的增加。
  1. 子类代理实现:
  • 父类BookService :
public class BookService {
    public void buy(){
        System.out.println("购书业务...");
    }
}
  • 子类SBookService:
public class SBookService extends BookService{
    public void buy(){
        System.out.println("购书前业务...");//增加功能
        super.buy();//使用父类业务功能
        System.out.println("购书完成...");
    }
}

五、静态代理模式:

  1. 特点:
  • 目标对象和代理对象实现同一个业务接口。
  • 目标对象必须实现接口。
  • 代理对象在程序运行前就已经存在。
  • 能够灵活地进行目标对象的切换,却无法进行功能的灵活处理。(使用动态代理解决此问题)
  • 代理对象无法实现目标对象的功能,却可以在目标对象的基础上增加其他功能。
  1. 静态代理实现:

在这里插入图片描述

  • 业务接口:
package com.user.cailinhao.service;

/**
 * 业务接口
 */
public interface Service {
    //规定唱歌的业务功能
    void sing();
}
  • 目标对象1:刘德华
package com.user.cailinhao.service.impl;

import com.user.cailinhao.service.Service;

/**
 * 刘德华:目标对象1
 */
public class SuperStarLiu implements Service {
    @Override
    public void sing() {
        System.out.println("我是刘德华,我正在表演");
    }
}
  • 目标对象2:周润发
package com.user.cailinhao.service.impl;

import com.user.cailinhao.service.Service;

/**
 * 周润发:目标对象2
 */
public class SuperStarZhou implements Service {
    @Override
    public void sing() {
        System.out.println("我是周润发,我正在表演");
    }
}

  • 代理对象:假设刘德华和周润发公用同一个助理
package com.user.cailinhao.service.impl;

import com.user.cailinhao.service.Service;

/**
 * 助理:代理对象
 */
public class Agent implements Service {
    //面向接口编程,类中的成员变量设计为接口
    public Service target;
    //面向接口编程,方法的参数设计为接口,传入目标对象,完成代理功能
    public Agent(Service target) {
        this.target = target;
    }

    @Override
    public void sing() {
        System.out.println("预定时间");
        System.out.println("预定场地");
        //重点:业务功能必须由目标对象亲自实现
        /*new SuperStarLiu().sing();这样就写死了,不灵活*/
        //请谁谁唱,很灵活
        target.sing();
        System.out.println("结算费用");
    }
}
  • 客户端:
package com.user.cailinhao.test;

import com.user.cailinhao.service.Service;
import com.user.cailinhao.service.impl.Agent;
import com.user.cailinhao.service.impl.SuperStarLiu;
import org.junit.Test;

public class MyTest {
    @Test
    public void testAgent(){
        /*
        Service agent = new Agent();
        agent.sing();
        */
    }
    public void testAgent1(){
        //面向客户,具体谁来唱由客户选择
        Service agent = new Agent(new SuperStarLiu());
        agent.sing();
    }
}


六、动态代理模式:

  1. 特点:
  • 静态代理存在的问题:Service接口中如果添加其他方法,比如表演show(),会使得实现接口的目标对象和代理对象的代码都要改变。
  • 代理对象在程序运行的过程中动态在内存构建,可以灵活地进行业务功能的切换。

七、JDK动态代理:

  1. 特点:
  • 目标对象必须实现业务接口。
  • JDK代理对象不需要实现业务接口。
  • JDK动态代理的对象在程序运行前不存在。在程序运行时动态地在内存中构建。
  • JDK动态代理灵活地进行业务功能的切换。
  • 目标对象中的非接口中的方法不能被代理。
  1. 重点类说明:
  • Proxy类:
    它是java.lang.reflect.Proxy包下的类,它有一个方法Proxy.newProxy.newProxyInstance(…)专门用来生成动态代理对象
public static Object newProxyInstance(ClassLoader lodaer,//类加载器,完成目标对象的加载
									  Class<?>[] interfaces,//目标对象实现的所有接口
									  InvocationHandler h,//类似于静态代理的Agent功能,代理的功能和目标对象功能的调用在这里
									  ) throws IllegalArgumentException
{.......}
  • Method类:
    反射机制,用来进行目标对象方法的反射调用

  • InvocationHandler接口:
    它是实现代理和业务功能
    的,我们在调用时使用匿名内部类实现。

  1. JDK动态代理实现:
  • 代理工厂:重点!!!!!!!!!!!
package com.user.cailinhao.proxy;

import com.user.cailinhao.service.Service;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * 代理工厂用于动态生成代理对象Agent,通过传入target参数生成一个专门为target目标对象代理的Agent,通过实现接口的invoke方法实现代理对象Agent的额外功能
 */
public class ProxyFactory {
    //面向接口编程,类中的成员变量设计为接口
    Service target;
    //面向接口编程,方法的参数设计为接口,传入目标对象,完成代理功能
    public ProxyFactory(Service target) {
        this.target = target;
    }
    //返回动态代理对象
    public Object getAgent(){
        return Proxy.newProxyInstance(//获取动态代理对象用这个方法
                target.getClass().getClassLoader(),//类加载器,在JVM中完成目标对象的加载
                target.getClass().getInterfaces(),//目标对象实现的所有接口,为了获取所有接口中的方法
                new InvocationHandler() {//这是一个实现代理功能的接口,类似于静态代理的Agent功能,代理的功能和目标对象功能的调用在这里,采用匿名内部类实现
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//返回:目标对象方法的返回值
                        //proxy是创建好的代理对象
                        //method是目标对象的某个方法,可能是sing()、show()
                        //args是目标对象的方法的参数


                        //代理功能
                        System.out.println("预定时间");
                        System.out.println("预定场地");
                        //目标对象的功能
                        //target.sing();这么些不行,还是写死了,等同静态代理
                        Object obj = method.invoke(target,args);//反射机制给方法传参数,调用target对象的invoke方法并传参数args,来的演员想唱歌就唱歌,想跳舞就跳舞
                        //代理功能
                        System.out.println("结算费用");
                        return obj;//目标对象方法的返回值
                    }
                }
        );
    }
}

  • 业务接口:
package com.user.cailinhao.service;

/**
 * 业务接口
 */
public interface Service {
    void sing();
    void show();
}

  • 目标对象1:刘德华
package com.user.cailinhao.service.impl;

import com.user.cailinhao.service.Service;

/**
 * 目标对象1:刘德华
 */
public class SuperStarLiu implements Service {
    @Override
    public void sing() {
        System.out.println("我是刘德华,我在唱歌");
    }

    @Override
    public void show() {
        System.out.println("我是刘德华,我在表演");
    }
}

  • 目标对象2:周润发
package com.user.cailinhao.service.impl;

import com.user.cailinhao.service.Service;
/**
 * 目标对象2:周润发
 */
public class SuperStarZhou implements Service {
    @Override
    public void sing() {
        System.out.println("我是周润发,我在唱歌");
    }

    @Override
    public void show() {
        System.out.println("我是周润发,我在表演");
    }
}

  • 客户端:
package com.user.cailinhao.test;

import com.user.cailinhao.proxy.ProxyFactory;
import com.user.cailinhao.service.Service;
import com.user.cailinhao.service.impl.SuperStarLiu;
import org.junit.Test;

public class MyTest {
    @Test
    public void testJDK(){
        ProxyFactory factory = new ProxyFactory(new SuperStarLiu());//目标对象创建代理对象
        Service agent = (Service) factory.getAgent();//!!!!!强转成Service接口才能调用sing方法
        agent.sing();
    }
    @Test
    public void testJDK1(){
        ProxyFactory factory = new ProxyFactory(new SuperStarLiu());//目标对象创建代理对象
        Service agent = (Service) factory.getAgent();//!!!!!强转成Service接口才能调用sing方法
        agent.show();
    }
    @Test
    public void testJDK2(){
        ProxyFactory factory = new ProxyFactory(new SuperStarLiu());//目标对象创建代理对象
        Service agent = (Service) factory.getAgent();//!!!!!强转成Service接口才能调用sing方法
        System.out.println(agent.getClass());//class jdk.proxy2.$Proxy4,这是一个JDK动态代理对象的类型
    }
}


八、CBLib动态代理:

  1. 特点:
  • 又称为子类动态代理,通过动态地在内存中构建子类对象,重写父类的方法进行代理功能的增强。
  • 如果目标对象没有实现接口,则只能通过CGLib自雷代理来进行功能增强。
  1. CBLib动态代理实现:
  • 业务接口:
package com.cailinhao.service;

/**
 * 业务接口
 */
public interface Service {
    void sing();
}

  • 目标对象:
package com.cailinhao.service.impl;

import com.cailinhao.service.Service;

/**
 * 目标对象1:刘德华
 */
public class SuperStarLiu implements Service {
    @Override
    public void sing() {
        System.out.println("我是刘德华,我在唱歌");
    }
}

  • 代理对象:
package com.cailinhao.service.sub;

import com.cailinhao.service.impl.SuperStarLiu;

/**
 * 代理对象
 */
public class SubSuperStarLiu extends SuperStarLiu {
    @Override
    public void sing() {
        System.out.println("预定时间");
        System.out.println("预定地点");
        super.sing();
        System.out.println("结算费用");
    }
}

  • 客户端:
package com.cailinhao;

import com.cailinhao.service.impl.SuperStarLiu;
import org.junit.Test;

public class MyTest {
    @Test
    public void testAgent(){
        SuperStarLiu liu = new SuperStarLiu();
        liu.sing();
    }
}

九:面向接口编程:

  • 类中的成员方法设计成接口。
  • 方法的参数设计为接口。
  • 方法返回值设计为接口。
  • 使用时使接口指向实现类。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

姓蔡小朋友

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

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

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

打赏作者

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

抵扣说明:

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

余额充值