结构型模式之适配器、桥接、装饰者、外观、代理模式

本文详细介绍了结构型设计模式中的五种模式:适配器模式用于使不兼容接口融合工作,桥接模式实现抽象与实现的分离以提高扩展性,装饰者模式动态增强对象功能,外观模式简化子系统调用,代理模式在不影响原有对象基础上增加额外功能。每种模式都结合实际案例进行了深入解析,包括适用场景和优缺点分析。
摘要由CSDN通过智能技术生成

目录

适配器模式

桥接模式

装饰者模式

外观模式

代理模式


适配器模式

我们先来看一个案例

需求:电源适配的项目,要让手机能够使用220V的交流电;注意,手机只接受5.5V的电压;

术语

  • source:待适配的类/对象/接口
  • Adapter:适配器
  • destination:适配后可用的类/对象/接口

角色

  • source        Power220V===》220V的电源
  • Adapter        PowerAdapt===》电源适配器
  • Destination        Power5V===》5V的电源

分类:

  • 类适配器模式Adapter 类,通过继承 source 类,实现 Destination 类接口,完成 source->Destination 的适配。
  • 对象适配器模式将 Adapter 类作修改,不是继承 source 类,而是持有 source 类的实例,以解决兼容性的问题。 即:持有 source 类,实现 Destination 类接口,完成source->Destination 的适配
  • 接口适配器模式当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求。适用于一个接口不想使用其所有的方法的情况

适配器模式分类三种体现形式思想都大同小异,这里就只介绍最常用的 对象适配器模式;

先看一下流程图

现在我们来看代码

下面是没有使用对象适配器的代码

/**
 * @author zc
 * @site www.javaxl.com
 * @company
 * @create  2020-02-22 15:51
 *
 * 220V的电压
 */
public class Voltage220V {
    private double voltage;

    public Voltage220V() {
        this.voltage = 220;
    }

    public double getVoltage() {
        return voltage;
    }

    public void setVoltage(double voltage) {
        this.voltage = voltage;
    }
}


public class Phone {
//    充电
    public void charge(Voltage220V voltage220V){
        double voltage = voltage220V.getVoltage() / 40;
        System.out.println("最终手机充电所用电压:" + voltage + "V");
    }
}


public class Client {
    public static void main(String[] args) {
        Phone phone = new Phone();
//        已知有一个220V的电源,要用它给手机进行充电,我们只能将220V的电源进行处理后才能给手机充上电
//        还一种方案:新增5V的一个Voltage5V,Voltage的电压可以被手机使用
//        但是这违背现实生活现象,现实生活中只有220V的电源,其他的电源都是通过适配得来的
        phone.charge(new Voltage220V());
    }
}

再看一下使用适配器之后的代码

/**
 * @author zc
 * @site www.javaxl.com
 * @company
 * @create  2020-02-22 15:51
 *
 * 220V的电压
 */
public class Voltage220V {
    private double voltage;

    public Voltage220V() {
        this.voltage = 220;
    }

    public double getVoltage() {
        return voltage;
    }

    public void setVoltage(double voltage) {
        this.voltage = voltage;
    }
}

/**
 * 目标接口
 */
interface Voltage5V{
    double getVoltage();
}

/**
 * 适配器:里面封装了source源到destination目标的过程
 */
class VoltageAdapter implements  Voltage5V{
    private Voltage220V voltage220V;

    public VoltageAdapter(Voltage220V voltage220V) {
        this.voltage220V = voltage220V;
    }

    @Override
    public double getVoltage() {
        return voltage220V.getVoltage() / 40;
    }
}


package com.javaxl.design.adapter.after;


public class Phone {
//    充电
    public void charge(Voltage5V voltage5V){
        double voltage = voltage5V.getVoltage();
        System.out.println("最终手机充电所用电压:" + voltage + "V");
    }
}


public class Client {
    public static void main(String[] args) {
        Phone phone = new Phone();
//        已知有一个220V的电源,要用它给手机进行充电,我们只能将220V的电源进行处理后才能给手机充上电
//        VoltageAdapter适配器对Voltage220V这个不能直接使用的电源适配后就可以使用了
        phone.charge(new VoltageAdapter(new Voltage220V()));
    }
}

注:

对象适配器(Adapter)模式最大的作用还是将原本不兼容的接口融合在一起工作

适配器的应用场景:

SpringMvc框架中HandlerAdapter类

安卓中监听适配器类中就使用了接口适配器(上拉、下拉...)

桥接模式

术语

  • Bridge:桥接
  • Abstraction:抽象类
  • Implementor:实现
  • concrete:具体的

角色

  • Client 类:桥接模式的调用者
  • Abstraction:维护了 Implementor/ 即它的实现类 ConcreteImplementorA.., 二者是聚合关系,Abstraction充当桥接类
  • xxxAbstraction :抽象的具体子类
  • Implementor :行为实现类的接口
  • ConcreteImplementorA /B:具体行为的实现类A、B

 来看一个案例

需求:手机(型号 + 品牌)操作问题;

​完成手机各品牌各型号的项目设计;

​列如:折叠式的华为、小米、Vivo,直立式的华为、小米、Vivo,旋转式的、滑盖的...

​要求该项目设计易于扩展

 

上图是常见的需求设计方案,非常不便于维护,手机的型号与品牌耦合度太高,当要新增滑盖(Slide)式的手机时,对应的品牌手机也要新增;

接下来我们来看看桥接模式是如何设计的?

 现在来看一下代码

/**
 * @author zc
 * @site www.javaxl.com
 * @company
 * @create  2020-02-22 17:29
 * <p>
 * 手机型号
 */
public abstract class Abstraction {
    protected Implementor implementor;

    public abstract void call();
}

class Folded extends Abstraction {
    private String name = "折叠式";

    Folded(Implementor implementor) {
        this.implementor = implementor;
    }

    @Override
    public void call() {
        System.out.println(this.implementor.getName() + this.name + "正在通话中");
    }
}

class Upright extends Abstraction {
    private String name = "直立式";

    Upright(Implementor implementor) {
        this.implementor = implementor;
    }

    @Override
    public void call() {
        System.out.println(this.implementor.getName() + this.name + "正在通话中");
    }
}

class Slide extends Abstraction {
    private String name = "滑盖式";

    Slide(Implementor implementor) {
        this.implementor = implementor;
    }

    @Override
    public void call() {
        System.out.println(this.implementor.getName() + this.name + "正在通话中");
    }
}
/**
 * @author zc
 * @site www.javaxl.com
 * @company
 * @create  2020-02-22 17:29
 * 手机品牌
 */
public interface Implementor {
    String getName();
}

class HW implements Implementor{
    private String name = "华为";

    @Override
    public String getName() {
        return name;
    }
}

class Mi implements Implementor{
    private String name = "小米";

    @Override
    public String getName() {
        return name;
    }
}

class Vivo implements Implementor{
    private String name = "Vivo";

    @Override
    public String getName() {
        return name;
    }
}


public class Client {
    public static void main(String[] args) {
        Folded folded = new Folded(new HW());
        folded.call();

        Upright upright = new Upright(new Mi());
        upright.call();

        Slide slide = new Slide(new Vivo());
        slide.call();
    }
}

从结果可以看出来:

​使用桥接模式,对该项目进行设计,型号或品牌的扩展,都不会影响另一方;

即手机型号扩展,手机品牌不受影响;手机品牌的上市退市,不会影响到手机型号;

优点:

  • 实现了抽象和实现部分的分离,从而极大的提供了系统的灵活性对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了,其它的部分由具体业务来完成桥接模式替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本

缺点:

  • 桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计和编程
  • 桥接模式要求正确识别出系统中两个独立变化的维度(抽象、和实现),因此其使用范围有一定的局限性,即需要有这样的应用场景。

装饰者模式

术语

  • Component:组件(主体)

  • concreteComponent:被装饰者

  • Decorator:装饰者

注意:concreteComponent、Decorator都会实现或继承Component

案例

单体咖啡与调味组合的饮料计价项目

角色

  • Client 类:装饰模式的调用者
  • Component:主体===》饮料(Drink)
  • concreteComponent:被装饰者===》咖啡(Coffee)
  • xxxConcreteComponent:具体的被装饰者(ChinaCoffee)美式咖啡、欧式咖啡、中式咖啡
  • Decorator:装饰器===》调料
  • xxxDecorator:子装饰器糖类、奶酪、牛油、芥末

这个项目最容易想到的设计方案就是采用继承的设计方案,设计思路如下:

​ Drink===》饮品

​ Juice===》果汁

​ .......

​ Coffee===》咖啡

​ ChinaCoffee===》中式咖啡

​ ASeasoningChinaCoffee===》被A调料修饰的中式咖啡

​ BSeasoningChinaCoffee===》被B调料修饰的中式咖啡

​ ......

​ XxxCoffee===》其它咖啡

​ ASeasoningXxxCoffee===》被A调料修饰的其它咖啡

​ BSeasoningXxxCoffee===》被B调料修饰的其它咖啡

​ ......

上面每个类中都有getPrice计价的功能,从结果上来看,确实可以完成项目需求,但是整个设计体系过于臃肿,不便于后期的扩展与维护;

用前面的桥接模式来进行设计,可以解决体系臃肿问题

现在看一下没有使用装饰模式的代码

/**
 * @author zc
 * @site www.javaxl.com
 * @company
 * @create  2020-02-22 18:27
 *
 * 饮料包括单体咖啡+调料
 */
public abstract class Drink {
    protected double price;
    protected int n;
    protected DrinkSeasoning seasoning;

    public abstract double getPrice();
}

/**
 * 单体咖啡
 */
abstract class Coffee extends Drink {
}

/**
 * 单体果汁
 */
abstract class Juice extends Drink {
}

class ChinaCoffee extends Coffee{
    ChinaCoffee(double price,int n){
        this.price = price;
        this.n = n;
    }

    @Override
    public double getPrice() {
        return this.price*this.n+this.seasoning.getPrice();
    }
}


package com.javaxl.design.decorator.before;

/**
 * @author zc
 * @site www.javaxl.com
 * @company
 * @create  2020-02-22 18:32
 *
 * 调料的抽象接口
 */
public interface DrinkSeasoning {
    public abstract double getPrice();
}

/**
 * A类调料
 */
class ADrinkSeasoning implements  DrinkSeasoning{
    protected double price;
    protected int n;
    ADrinkSeasoning(double price,int n){
        this.price = price;
        this.n = n;
    }
    @Override
    public double getPrice() {
        return this.price*this.n;
    }
}


/**
 * B类调料
 */
class BDrinkSeasoning implements  DrinkSeasoning{
    private double price;
    protected int n;
    BDrinkSeasoning(double price,int n){
        this.price = price;
        this.n = n;
    }
    @Override
    public double getPrice() {
        return this.price*this.n;
    }
}


public class Client {
    public static void main(String[] args) {
        ChinaCoffee chinaCoffee = new ChinaCoffee(6,1);
        ADrinkSeasoning aDrinkSeasoning = new ADrinkSeasoning(2,2);
        chinaCoffee.seasoning = aDrinkSeasoning;
        System.out.println("中式咖啡1份+A调料2份,最终价格为:"+chinaCoffee.getPrice());

//        思考1:如果我要下单中式咖啡1份+A调料3份+B调料2份,计算出最终的价格,那代码该怎么改动呢?
//        思考2:在原有的咖啡订单下,追加B调料2份,计算出最终的价格,那代码该怎么改动呢?
    }
}

上面的代码存在问题:

思考1:如果我要下单中式咖啡1份+A调料3份+B调料2份,计算出最终的价格,那代码该怎么改动呢? seasoning只能代表一种调料

思考2:在原有的咖啡订单下,追加B调料2份,计算出最终的价格,那代码该怎么改动呢? 原有的设计没办法应对这种场景

思考3:Drink饮品单体饮料种类多,调料种类也多,会带来什么问题?

Drink抽象类中聚合List<DrinkSeasoning>才可以解决上述的前两个问题;但是,在原有订单追加调料,相当于给原有对象进行装饰,这类的问题更加适合用装饰模式来解决;

现在来看一下使用装饰模式进行设计

 代码逻辑如下

/**
 * @author zc
 * @site www.javaxl.com
 * @company
 * @create  2020-02-22 18:27
 * <p>
 * 饮料包括单体咖啡+调料
 */
public abstract class Drink {
    protected double price;
    protected int n;

    public abstract double getPrice();
}

/**
 * 单体咖啡
 */
abstract class Coffee extends Drink {
}

/**
 * 单体果汁
 */
abstract class Juice extends Drink {
}

class ChinaCoffee extends Coffee {
    ChinaCoffee(double price, int n) {
        this.price = price;
        this.n = n;
    }

    @Override
    public double getPrice() {
        return this.price * this.n;
    }
}


package com.javaxl.design.decorator.after;

/**
 * @author zc
 * @site www.javaxl.com
 * @company
 * @create  2020-02-22 22:26
 */
public class DecoratorDrink extends Drink {
    private Drink drink;

    public DecoratorDrink(Drink drink, double price, int n) {
        this.drink = drink;
        this.price = price;
        this.n = n;
    }

    @Override
    public double getPrice() {
        return this.price * this.n + drink.getPrice();
    }
}

class ADecoratorDrink extends DecoratorDrink {
    public ADecoratorDrink(Drink drink, double price, int n) {
        super(drink, price, n);
    }
}

class BDecoratorDrink extends DecoratorDrink {
    public BDecoratorDrink(Drink drink, double price, int n) {
        super(drink, price, n);
    }
}


package com.javaxl.design.decorator.after;


/**
 * @author zc
 * @site www.javaxl.com
 * @company
 * @create  2020-02-22 18:50
 */
public class Client {
    public static void main(String[] args) {
        ChinaCoffee chinaCoffee = new ChinaCoffee(6,1);
//        假定A类调料2元一份,B类调料3元一份
        Drink order = new ADecoratorDrink(chinaCoffee, 2, 2);
        System.out.println("中式咖啡1份+A调料2份,最终价格为:"+order.getPrice());

//        思考1:如果我要下单中式咖啡1份+A调料3份+B调料2份,计算出最终的价格,那代码该怎么改动呢?
        order = new ADecoratorDrink(order,2,1);
        System.out.println("式咖啡1份+A调料3份,最终价格为:"+order.getPrice());
        order = new BDecoratorDrink(order,3,2);
        System.out.println("式咖啡1份+A调料3份+B调料2份,最终价格为:"+order.getPrice());

//        思考2:在原有的咖啡订单下,追加B调料2份,计算出最终的价格,那代码该怎么改动呢?
        order = new BDecoratorDrink(order,3,2);
        System.out.println("式咖啡1份+A调料3份+B调料4份,最终价格为:"+order.getPrice());
    }
}

 注意:

  • 装饰者模式一般用于对原有功能进行增强/装饰
  • 动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性

外观模式

角色

  • Client 类        外观模式的调用者
  • ComponentA        子系统A 电脑
  • ComponentB        子系统B 投影仪
  • Facade        HomeTheaterFacade 家庭影院

我们来看一下案例,案例需求如下

需求:组装一个家庭影院;

电脑打开、投影仪打开、音箱打开、灯光调暗、零食拿出来,电影开始;

零食收起来、灯光调亮、音箱关闭、投影仪关闭、电脑关闭,电影结束;

先看一下没有使用外观模式的代码

/**
 * @author zc
 * @site www.javaxl.com
 * @company
 * @create  2020-02-23 16:29
 *
 * 电脑(故意写两个用不上的功能,依次体现外观模式的优点)
 */
public class ComponentA {
    public void m1(){
        System.out.println("电脑功能一...");
    }
    public void m2(){
        System.out.println("电脑功能二...");
    }

    public void on(){
        System.out.println("电脑打开...");
    }

    public void off(){
        System.out.println("电脑关闭...");
    }
}

//投影仪
class ComponentB {
    public void on(){
        System.out.println("投影仪打开...");
    }

    public void off(){
        System.out.println("投影仪关闭...");
    }
}

//音箱
class ComponentC {
    public void on(){
        System.out.println("音箱打开...");
    }

    public void off(){
        System.out.println("音箱关闭...");
    }
}

//、灯光
class ComponentD {
    public void on(){
        System.out.println("灯光调亮...");
    }

    public void off(){
        System.out.println("灯光调暗...");
    }
}

//零食
class ComponenE {
    public void on(){
        System.out.println("零食拿出来...");
    }

    public void off(){
        System.out.println("零食收起来...");
    }
}


public class Client {
    public static void main(String[] args) {
        new ComponentA().on();
        new ComponentB().on();
        new ComponentC().on();
        new ComponentD().off();
        new ComponenE().on();
        System.out.println("电影开始了...");

        System.out.println();
        new ComponenE().off();
        new ComponentD().on();
        new ComponentC().off();
        new ComponentB().off();
        new ComponentA().off();
        System.out.println("电影结束了...");
    }
}

从上面代码可以看出:

客户端调用依赖了所有的子系统(ABCDE),如果该需求反复出现,对于客户端调用而言,就不是很方便了;

另一方面,此需求完成只需要依赖各个子系统的其中一部分功能,其它功能客户端用不上,依照迪米特法则我们也应该解耦客户端与各个子系统的关系;

然后在来看一下使用外观模式的代码

/**
 * @author zc
 * @site www.javaxl.com
 * @company
 * @create  2020-02-23 16:29
 *
 * 电脑(故意写两个用不上的功能,依次体现外观模式的优点)
 */
public class ComponentA {
    public void m1(){
        System.out.println("电脑功能一...");
    }
    public void m2(){
        System.out.println("电脑功能二...");
    }

    public void on(){
        System.out.println("电脑打开...");
    }

    public void off(){
        System.out.println("电脑关闭...");
    }
}

//投影仪
class ComponentB {
    public void on(){
        System.out.println("投影仪打开...");
    }

    public void off(){
        System.out.println("投影仪关闭...");
    }
}

//音箱
class ComponentC {
    public void on(){
        System.out.println("音箱打开...");
    }

    public void off(){
        System.out.println("音箱关闭...");
    }
}

//、灯光
class ComponentD {
    public void on(){
        System.out.println("灯光调亮...");
    }

    public void off(){
        System.out.println("灯光调暗...");
    }
}

//零食
class ComponentE {
    public void on(){
        System.out.println("零食拿出来...");
    }

    public void off(){
        System.out.println("零食收起来...");
    }
}


public class ComponentFacade {
    ComponentA componentA =new ComponentA();
    ComponentB componentB = new ComponentB();
    ComponentC componentC = new ComponentC();
    ComponentD componentD = new ComponentD();
    ComponentE componenE = new ComponentE();
    public void on(){
        componentA.on();
        componentB.on();
        componentC.on();
        componentD.off();
        componenE.on();
        System.out.println("电影开始了...");
    }

    public void off(){
        componenE.off();
        componentD.on();
        componentC.off();
        componentB.off();
        componentA.off();
        System.out.println("电影结束了...");
    }
}


public class Client {
    public static void main(String[] args) {
        ComponentFacade componentFacade = new ComponentFacade();
        componentFacade.on();
        System.out.println();
        componentFacade.off();
    }
}

从上面可以看出,客户端只依赖了外观类,彻底解耦了与各个子系统之间的关系;

注意:

  • 屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复杂性
  • 对客户端与子系统的耦合关系 - 解耦,让子系统内部的模块更易维护和扩展
  • 当系统需要进行分层设计时,可以考虑使用 Facade 模式

应用场景:

后期代码维护,提取某些类中可用的方法聚合在一起

代理模式

类图:

分类

静态代理

角色

  •         接口 ITeacherDao
  •         目标对象 TeacherDAO
  •         代理类 TeacherDAOProxy

细节

  • 代理对象与目标对象要实现相同的接口
  • 调用的时候通过调用代理对象的方法来调用目标对象

案例代码如下:

/**
 * @author zc
 * @site www.javaxl.com
 * @company
 * @create  2020-02-24 9:35
 * <p>
 * 目标类
 */
public class TeacherDAO implements ITeacherDao {
    public void teach() {
        System.out.println("老师传授知识");
    }
}

//目标接口
interface ITeacherDao {
    void teach();
}

//代理类
class TeacherDAOProxy implements ITeacherDao {
    private ITeacherDao teacherDAO;

    public TeacherDAOProxy(ITeacherDao teacherDAO) {
        this.teacherDAO = teacherDAO;
    }

    @Override
    public void teach() {
        System.out.println("老师正式授课前的准备工作,如学生全部签到...");
        teacherDAO.teach();
        System.out.println("老师结束授课,如下课铃声响起...");
    }
}


public class Client {
    public static void main(String[] args) {
        TeacherDAOProxy proxy = new TeacherDAOProxy(new TeacherDAO());
        proxy.teach();
    }
}

动态代理

jdk代理

  • 角色​ 接口​ ITeacherDao​
  • 目标对象​ TeacherDAO ​
  • 代理类​ TeacherDAOProxy ​

细节​

  • 不需要实现接口,但是目标对象要实现接口,否则不能用动态代理​
  • 代理对象的生成,是利用 JDK 的 API,动态的在内存中构建代理对象​
  • 代理类所在包:java.lang.reflect.Proxy

案例代码如下:

/**
 * 目标接口
 */
interface ITeacherDao {
    String teach();

    ITeacherDao sleep(int minutes);
}

class TeacherDao implements ITeacherDao{
    @Override
    public String teach() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        return sdf.format(new Date())+":老师传授知识";
    }

    @Override
    public ITeacherDao sleep(int minutes) {
        System.out.println("老师睡了" + minutes + "分钟");
        return this;
    }

}

//真实代理类的外衣
class TeacherDaoProxy{
    private ITeacherDao target;

    public TeacherDaoProxy(ITeacherDao target) {
        this.target = target;
    }

    public Object xxx(){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Object obj = null;
                        String methodName = method.getName();
                        System.out.println("目标方法" + methodName + ":jdk代理开始...");
                        System.out.println("真实代理对象:"+proxy.getClass());
                        System.out.println("目标对象:"+target.getClass());
                        if("sleep".equals(methodName)){
//                            method.invoke(target, args);
//                            obj = proxy;
                            obj = method.invoke(target, args);
                        }else {
//                        proxy是真实代理类,method是目标方法,args是目标方法携带的参数
                            obj = method.invoke(target, args);
                        }
                        System.out.println("目标方法" + methodName + ":jdk代理结束...");
                        return obj;
                    }
                });
    }
}


public class Client {
    public static void main(String[] args) {
        TeacherDaoProxy proxy = new TeacherDaoProxy(new TeacherDao());
        ITeacherDao ins = (ITeacherDao) proxy.xxx();
        System.out.println("===========代理类实例被使用   begin=============");
        System.out.println(ins);
        System.out.println("===========代理类实例被使用   end=============");
        System.out.println(ins.teach());
//        System.out.println(proxy.execute());
        System.out.println("===========代理类实例被使用   begin=============");
        ins.sleep(10);
        System.out.println("===========代理类实例被使用   end=============");
        ins.sleep(20).sleep(60);
    }
}

注意:java.lang.reflect.InvocationHandler.invoke第一个参数proxy的使用场景,链式编程中会用到;

 Cglib代理​

  • 角色​ 接口​ ITeacherDao​
  • 目标对象​ TeacherDAO ​
  • 代理类​ TeacherDAOProxy

细节​

  • 目标对象与代理对象都不需要实现接口​
  • Cglib 代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展​
  • Cglib 是一个强大的高性能的代码生成包,它可以在运行期扩展 java 类与实现 java 接口.它广泛的被许多 AOP 的框架使用​
  • Cglib 包的底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类​

注意:

  • 需要引入 cglib 的 jar 文件
  • 在内存中动态构建子类,注意代理的类不能为 final,否则报错java.lang.IllegalArgumentException:
  • 目标对象的方法如果为 final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.

案例代码如下:

class TeacherDao {
    public String teach() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        return sdf.format(new Date()) + ":老师传授知识";
    }

    public TeacherDao sleep(int minutes) {
        System.out.println("老师睡了" + minutes + "分钟");
        return this;
    }

}

//真实代理类的外衣
class TeacherDaoProxy implements MethodInterceptor {
    private Object target;

    public TeacherDaoProxy(Object target) {
        this.target = target;
    }

    //返回一个代理对象:	是 target  对象的代理对象
    public Object getProxyInstance() {
        //1. 创建一个工具类
        Enhancer enhancer = new Enhancer();
        //2. 设置父类
        enhancer.setSuperclass(target.getClass());
        //3. 设置回调函数
        enhancer.setCallback(this);
        //4. 创建子类对象,即代理对象
        return enhancer.create();
    }

    /**
     * @param proxyIns  由CGLib动态生成的代理类实例
     * @param method    上文中实体类所调用的被代理的方法引用
     * @param args      参数值列表
     * @param methodProxy   生成的代理类对方法的代理引用
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object proxyIns, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        String methodName = method.getName();
        Object res;
        System.out.println("目标方法" + methodName + ":cglib代理开始...");
        System.out.println("真实代理对象:" + proxyIns.getClass());
        System.out.println("目标对象:" + target.getClass());
        if ("sleep".equals(methodName)) {
//                            method.invoke(target, args);
//                            obj = proxy;
            res = method.invoke(target, args);
//            res = methodProxy.invokeSuper(proxyIns,args);
            res = proxyIns;
        } else {
//                        proxy是真实代理类,method是目标方法,args是目标方法携带的参数
            res = method.invoke(target, args);
        }
        System.out.println("目标方法" + methodName + ":cglib代理结束...");
        return res;
    }
}

public class Client {
    public static void main(String[] args) {
        TeacherDao proxy = (TeacherDao) new TeacherDaoProxy(new TeacherDao()).getProxyInstance();
        proxy.sleep(111).sleep(222);
    }
}

jdk代理与Cglib代理比较:

JDK中所要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高

​ 使用CGLib实现动态代理,完全不受代理类必须实现接口的限制,而且CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值