复习宝典之设计模式

查看更多宝典,请点击《金三银四,你的专属面试宝典》html

第四章:设计模式

设计模式是前人(通常都是大师)对程序设计经验的总结,学习并应用设计模式可使咱们开发的项目更加规范、便于扩展和维护。程序员

1)面向对象四大特征

封装:即将对象封装成一个高度自治和相对封闭的个体,对象状态(属性)由这个对象本身的行为(方法)来读取和改变。面试

张三这我的,他的姓名等属性,要有本身提供的获取或改变的方法来操做。算法

private name setName getName编程

抽象:就是找出一些事物的类似和共性之处,而后将这些事物归为一个类,这个类只考虑这些事物的类似和共性之处,而且会忽略与当前主题和目标无关的那些方面,将注意力集中在与当前目标有关的方面。 就是把现实生活中的对象,抽象为类。设计模式

继承:在定义和实现一个类的时候,能够在一个已经存在的类的基础之上来进行,把这个已经存在的类所定义的内容做为本身的内容,并能够加入若干新的内容,或修改原来的方法使之更适合特殊的须要,这就是继承。遗产继承缓存

多态:是指程序中定义的引用变量所指向的具体类型和经过该引用变量发出的方法调用在编程时并不肯定,而是在程序运行期间才肯定,即一个引用变量倒底会指向哪一个类的实例对象,该引用变量发出的方法调用究竟是哪一个类中实现的方法,必须在由程序运行期间才能决定。安全

Object obj = new xxx();网络

UserDao userDao = new UserDaoJdbcImpl();多线程

UserDao userDao = new UserDaoHibernateImpl();

靠的是父类或接口定义的引用变量能够指向子类或具体实现类的实例对象(里氏替换原则),而程序调用的方法在运行期才动态绑定,就是引用变量所指向的具体实例对象的方法,也就是内存里正在运行的那个对象的方法,而不是引用变量的类型中定义的方法。

扩展:一个程序就是一个世界,世界由不一样的种类组成,每一个类别下的个体就是一个个对象。封装是对对象属性的私有化,而后经过公开的方法进行访问。继承是is-a的关系,子类会继承父类的非私有属性与方法。多态分静态的多态(构造函数的重载)与动态的多态(编译时多态,里氏替换原则),表面意义是一种类型在不一样状况下的不一样形态【如类的多态,行为(接口)的多态】。

 

2)五大设计原则

单一职责原则(即一个类只负责一项职责

  单一职责原则(Single Responsibility Principle,SRP):就一个类而言,应该仅有一个引发它变化的缘由。即一个类应该只负责一个功能领域中的相应职责。

  单一职责原则是实现高内聚、低耦合的指导方针,它是最简单但又最难运用的原则,须要设计人员发现类的不一样职责并将其分离,而发现类的多重职责须要设计人员具备较强的分析设计能力和相关实践经验。

开闭原则(一个类或方法应该对扩展开放,对修改关闭

  开闭原则(Open-Closed Principle,OCP): 是指软件实体(类、模块、函数等等)应该能够扩展,可是不可修改。即软件实体应该尽可能在不修改原有代码的状况下进行扩展。

  为了知足开闭原则,须要对系统进行抽象化设计,抽象化是开闭原则的关键。

里氏替换原则(子类能够扩展父类的功能,但不能改变父类原有的功能,子类对象实例化父类对象

  里氏替换原则(Liskov Substitution Principle,LSP):全部引用父类的地方必须可以透明的使用子类的对象。即子类型必须可以替换掉它们的父类型。

  里氏替换原则告诉咱们,在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,若是一个软件实体使用的是一个子类对象的话,那么它不必定可以使用基类对象。所以在程序中尽可能使用基类类型来对对象进行定义,而在运行时再肯定其子类类型,用子类对象来替换父类对象。同时,里氏代换原则是实现开闭原则的重要方式之一。

它包含如下含义:

1) 子类能够实现父类的抽象方法,但不能覆盖父类的非抽象方法。

2) 子类的方法重载父类的方法时,方法的入参要比父类方法的入参更宽松。

3) 子类的方法实现父类的抽象方法时,方法的返回值要比父类更严格。

依赖倒置原则(上层模块不该该依赖低层模块的实现,而应该依赖其抽象

  依赖倒置原则(Dependency Inversion Principle,DIP):抽象不该该依赖细节,细节应该依赖于抽象。即应该针对接口编程,而不是针对实现编程。

  在大多数状况下,咱们会同时使用开闭原则、里氏代换原则和依赖倒转原则,开闭原则是目标,里氏代换原则是基础,依赖倒转原则是手段。

依赖倒置的优势:减小类之间的耦合,提升系统的稳定性,减小并行开发引发的风险。

接口隔离原则(一个类对另外一个类的依赖应该创建在最小的接口上

  接口隔离原则(Interface Segregation Principle,ISP):使用专门的接口,而不使用单一的总接口,即客户端不该该依赖那些它不须要的接口。

  根据接口隔离原则,当一个接口太大时,咱们须要将它分割成一些更细小的接口,使用该接口的客户端仅需知道与之相关的方法便可。每个接口应该承担一种相对独立的角色,不干不应干的事,该干的事都要干。

 

3)设计模式基础

通常地设计模式分为三类共23种:

3.1. 建立型模式

单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式。

这些设计模式提供了一种在建立对象的同时隐藏建立逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例须要建立哪些对象时更加灵活。

3.2. 结构型模式

适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。

这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象得到新功能的方式。

3.3. 行为型模式

模版模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、责任链模式、访问者模式。

这些设计模式特别关注对象之间的通讯。

 

4)单例模式

确保一个类最多只有一个实例,并提供一个全局访问点。

有些对象咱们只须要一个:线程池,缓存,硬件设备,若是多个实例会形成冲突,结果不一致等问题,能够用静态变量来实现,或者程序员之间协商一个全局变量。

具体实现原理:能够将一个类的构造函数私有化,而后在该类内部建立本身的对象。

饱汉模式:无论是否用到,都先建立好

/**
* @author: 肖德子裕
* @date: 2019/02/28 20:50
* @description: 饱汉模式
*/
public class Singleton2 {
  private static Singleton2 uniqeInstance=new Singleton2();

  private Singleton2() {

  }

  public static Singleton2 getInstance(){
      return uniqeInstance;
  }
}

 

饥汉模式:须要时才建立

/**
* @author: 肖德子裕
* @date: 2019/02/28 20:50
* @description: 饥汉模式
*/
public class Singleton {
  private static Singleton uniqeInstance=null;

  private Singleton() {

  }

  public static Singleton getInstance(){
      if (uniqeInstance==null){
          uniqeInstance=new Singleton();
      }
      return uniqeInstance;
  }
}

 

在多线程的状况下,可能2个线程同时访问该方法,这样的话就会建立2个实例对象,因此使用双重检查加锁解决该问题

public static ChocolateFactory getInstance() {
  if (uniqueInstance == null) {
      synchronized (ChocolateFactory.class) {
          if (uniqueInstance == null) {
              uniqueInstance = new ChocolateFactory();
          }
      }
  }
  return uniqueInstance;
}

扩展知识:

synchronized是Java中的关键字,是一种同步锁。它修饰的对象有如下几种:

  1. 修饰一个代码块,被修饰的代码块称为同步语句块,其做用的范围是大括号{}括起来的代码,做用的对象是调用这个代码块的对象;

  2. 修饰一个方法,被修饰的方法称为同步方法,其做用的范围是整个方法,做用的对象是调用这个方法的对象;

  3. 修改一个静态的方法,其做用的范围是整个静态方法,做用的对象是这个类的全部对象;

  4. 修改一个类,其做用的范围是synchronized后面括号括起来的部分,做用主的对象是这个类的全部对象。

修饰一个代码块:一个线程访问一个对象中的synchronized(this)同步代码块时,其余试图访问该对象的线程将被阻塞。

当两个并发线程(thread1和thread2)访问同一个对象(syncThread)中的synchronized代码块时,在同一时刻只能有一个线程获得执行,另外一个线程受阻塞,必须等待当前线程执行完这个代码块之后才能执行该代码块。Thread1和thread2是互斥的,由于在执行synchronized代码块时会锁定当前的对象,只有执行完该代码块才能释放该对象锁,下一个线程才能执行并锁定该对象。

建立了两个SyncThread的对象syncThread1和syncThread2,线程thread1执行的是syncThread1对象中的synchronized代码(run),而线程thread2执行的是syncThread2对象中的synchronized代码(run);咱们知道synchronized锁定的是对象,这时会有两把锁分别锁定syncThread1对象和syncThread2对象,而这两把锁是互不干扰的,不造成互斥,因此两个线程能够同时执行。

当一个线程访问对象的一个synchronized(this)同步代码块时,另外一个线程仍然能够访问该对象中的非synchronized(this)同步代码块。

Synchronized修饰一个方法很简单,就是在方法的前面加synchronized,public synchronized void method(){//todo}; synchronized修饰方法和修饰一个代码块相似,只是做用范围不同,修饰代码块是大括号括起来的范围,而修饰方法范围是整个函数。

在用synchronized修饰方法时要注意如下几点:

  1. synchronized关键字不能继承。

  2. 在定义接口方法时不能使用synchronized关键字。

  3. 构造方法不能使用synchronized关键字,但可使用synchronized代码块来进行同步。

 

5)工厂模式

遵循依赖抽象原则:

变量不要持有具体类的引用;不要让类继承自具体类,要继承自抽象类或接口;不要覆盖基类中已经实现的方法。

5.1 简单工厂模式:

定义了一个建立对象的类,由这个类来封装实例化对象的行为

问题:如一家披萨店,在下订单时会根据不一样的披萨实例化不一样的对象,这样之后每次提出新品种或者下架某品种,就要去修改订单里面的实例化操做。

以下能够定义一个简单的工厂类来封装实例化过程:

/**
* 简单工厂模式
*/
public class SimplePizzaFactory {
  /**
    * 传入披萨的类型
    *
    * @param ordertype
    * @return
    */
  public Pizza CreatePizza(String ordertype) {
      Pizza pizza = null;

      if (ordertype.equals("cheese")) {
          pizza = new CheesePizza();
      } else if (ordertype.equals("greek")) {
          pizza = new GreekPizza();
      } else if (ordertype.equals("pepper")) {
          pizza = new PepperPizza();
      }
      return pizza;
  }
}

 

/**
* 披萨订单模块
*/
public class OrderPizza {
  SimplePizzaFactory mSimplePizzaFactory;

  public OrderPizza(SimplePizzaFactory mSimplePizzaFactory) {
    setFactory(mSimplePizzaFactory);
  }

  public void setFactory(SimplePizzaFactory mSimplePizzaFactory) {
    Pizza pizza = null;
    String ordertype;
    this.mSimplePizzaFactory = mSimplePizzaFactory;
    //工厂生产披萨并进行包装发货
    do {
        ordertype = gettype();
        pizza = mSimplePizzaFactory.CreatePizza(ordertype);
        if (pizza != null) {
          pizza.prepare();
          pizza.bake();
          pizza.cut();
          pizza.box();
        }
    } while (true);
  }

  private String gettype() {
    try {
        BufferedReader strin = new BufferedReader(new InputStreamReader(
              System.in));
        System.out.println("input pizza type:");
        String str = strin.readLine();
        return str;
    } catch (IOException e) {
        e.printStackTrace();
        return "";
    }
  }
}

 

/**
* 披萨店中购买披萨
*/
public class PizzaStroe {
  public static void main(String[] args) {
    SimplePizzaFactory mSimplePizzaFactory;
    OrderPizza mOrderPizza;
    mOrderPizza=new OrderPizza(new SimplePizzaFactory());
  }
}

 

5.2 方法工厂模式:

定义了一个建立对象的抽象方法,由子类决定要实例化的类。

问题:若是一家披萨店要在全国开连锁,这样地方的配料确定不同,因此单纯的将工厂复制过去是不行的。

以下能够将建立披萨的方法抽象化,让子类继承后去实例化具体的对象:

/**
* 抽象化下订单的模块
*/
public abstract class OrderPizza {
  public OrderPizza() {
      Pizza pizza = null;
      String ordertype;
      do {
          ordertype = gettype();
          pizza = createPizza(ordertype);

          pizza.prepare();
          pizza.bake();
          pizza.cut();
          pizza.box();
      } while (true);
  }

  abstract Pizza createPizza(String ordertype);

  private String gettype() {
      try {
          BufferedReader strin = new BufferedReader(new InputStreamReader(
                  System.in));
          System.out.println("input pizza type:");
          String str = strin.readLine();
          return str;
      } catch (IOException e) {
          e.printStackTrace();
          return "";
      }
  }
}

 

/**
* 伦敦的披萨店
*/
public class LDOrderPizza extends OrderPizza {
  @Override
  Pizza createPizza(String ordertype) {
      Pizza pizza = null;
      if (ordertype.equals("cheese")) {
          pizza = new LDCheesePizza();
      } else if (ordertype.equals("pepper")) {
          pizza = new LDPepperPizza();
      }
      return pizza;
  }
}

 

/**
* 纽约的披萨店
*/
public class NYOrderPizza extends OrderPizza {
  @Override
  Pizza createPizza(String ordertype) {
      Pizza pizza = null;
      if (ordertype.equals("cheese")) {
          pizza = new NYCheesePizza();
      } else if (ordertype.equals("pepper")) {
          pizza = new NYPepperPizza();
      }
      return pizza;
  }
}

 

/**
* 披萨店中购买披萨
*/
public class PizzaStroe {
  public static void main(String[] args) {
    OrderPizza mOrderPizza;
    mOrderPizza=new NYOrderPizza();
  }
}

 

5.3 抽象工厂模式

定义了一个接口用于建立相关或有依赖关系的对象族,而无需明确指定具体类。

/**
* 抽象工厂模式
*/
public interface AbsFactory {
  public Pizza CreatePizza(String ordertype) ;
}

 

/**
* 伦敦工厂
*/
public class LDFactory implements AbsFactory {
  @Override
  public Pizza CreatePizza(String ordertype) {
    Pizza pizza = null;
    if (ordertype.equals("cheese")) {
        pizza = new LDCheesePizza();
    } else if (ordertype.equals("pepper")) {
        pizza = new LDPepperPizza();
    }
    return pizza;
  }
}

 

/**
* 纽约工厂
*/
public class NYFactory implements AbsFactory {
  @Override
  public Pizza CreatePizza(String ordertype) {
    Pizza pizza = null;
    if (ordertype.equals("cheese")) {
        pizza = new NYCheesePizza();
    } else if (ordertype.equals("pepper")) {
        pizza = new NYPepperPizza();
    }
    return pizza;
  }
}

 

/**
* 下订单的模块
*/
public class OrderPizza {
  AbsFactory mFactory;

  public OrderPizza(AbsFactory mFactory) {
    setFactory(mFactory);
  }

  public void setFactory(AbsFactory mFactory) {
    Pizza pizza = null;
    String ordertype;
    this.mFactory = mFactory;
    do {
        ordertype = gettype();
        pizza = mFactory.CreatePizza(ordertype);
        if (pizza != null) {
          pizza.prepare();
          pizza.bake();
          pizza.cut();
          pizza.box();
        }
    } while (true);
  }

  private String gettype() {
    try {
        BufferedReader strin = new BufferedReader(new InputStreamReader(
              System.in));
        System.out.println("input pizza type:");
        String str = strin.readLine();
        return str;
    } catch (IOException e) {
        e.printStackTrace();
        return "";
    }
  }
}

 

/**
* 披萨店中购买披萨
*/
public class PizzaStroe {
  public static void main(String[] args) {
    OrderPizza mOrderPizza;
    mOrderPizza=new OrderPizza(new LDFactory());
  }
}

 

6)策略模式

分别封装行为接口,实现算法族,超类里放行为接口对象,在子类里具体设置行为对象。

策略模式注重功能实现。

原则:分离变化部分,封装接口,基于接口编程各类功能,此模式让行为算法的变化独立于算法的使用者。

将行为定义为接口:

/**
* 飞行
*/
public interface FlyBehavior {
  void fly();
}

具体的实现类:

/**
* 会飞
*/
public class GoodFlyBehavior implements FlyBehavior {
  @Override
  public void fly() {
      System.out.println("--GoodFly--");
  }
}

 

/**
* 不会飞
*/
public class NoFlyBehavior implements FlyBehavior {
  @Override
  public void fly() {
      System.out.println("--NoFly--");
  }
}

若是还有其余行为也能够向上面同样,先定义接口,在实现,好比叫声能够分呱呱叫还有其余叫。

定义一个鸭子类:

/**
* 鸭子
*/
public abstract class Duck {
  /**
  * 飞行
  */
  FlyBehavior mFlyBehavior;
 
  /**
  * 叫声
  */
  QuackBehavior mQuackBehavior;

  public abstract void display();

  public Duck() {

  }

  public void Fly() {
    mFlyBehavior.fly();
  }

  public void Quack() {
    mQuackBehavior.quack();
  }

  public void SetQuackBehavoir(QuackBehavior qb) {
    mQuackBehavior = qb;
  }

  public void SetFlyBehavoir(FlyBehavior fb) {
    mFlyBehavior = fb;
  }

  public void swim() {
    System.out.println("~~im swim~~");
  }
}

鸭子下的子类:

/**
* 绿头鸭
*/
public class GreenHeadDuck extends Duck {
  public GreenHeadDuck() {
      //实例化具体的飞行方式
      mFlyBehavior = new GoodFlyBehavior();
      //实例化具体的叫声
      mQuackBehavior = new GaGaQuackBehavior();
  }

  @Override
  public void display() {
      System.out.println("**GreenHead**");
  }
}

这样就能实现按不一样需求实现:

public class StimulateDuck {
  public static void main(String[] args) {
      GreenHeadDuck mGreenHeadDuck = new GreenHeadDuck();
      RedHeadDuck mRedHeadDuck = new RedHeadDuck();

      mGreenHeadDuck.display();
      mGreenHeadDuck.Fly();
      mGreenHeadDuck.Quack();
      mGreenHeadDuck.swim();

      mRedHeadDuck.display();
      mRedHeadDuck.Quack();
      mRedHeadDuck.swim();
      mRedHeadDuck.Fly();
  }
}

 

7)模板模式

封装了一个算法步骤,并容许子类为一个或多个步骤方法提供实现;模板模式可使子类在不改变算法结构的状况下,从新定义算法中的某些步骤。

模板模式注重步骤实现。

/**
* @author: 肖德子裕
* @date: 2019/03/03 19:31
* @description: 模板模式
*/
public abstract class HotDrinkTemplate {
  /**
    * 这是一个模板方法
    */
  public final void prepareRecipe() {
      //烧水,固定
      boilWater();
      //喝什么,由子类实现
      brew();
      //加水,固定
      pourInCup();
      //经过钩子方法,让子类肯定是否配料
      if (wantCondimentsHook()) {
          //加配料
          addCodiments();
      } else {
          System.out.println("不加配料");
      }
  }

  /**
    * 钩子方法
    *
    * @return
    */
  public boolean wantCondimentsHook() {
      return true;
  }

  public final void boilWater() {
      System.out.println("Boiling water");
  }

  public abstract void brew();

  public final void pourInCup() {
      System.out.println("put in cup");
  }

  public abstract void addCodiments();
}

 

/**
* @author: 肖德子裕
* @date: 2019/03/03 19:52
* @description: 泡茶
*/
public class TeaWithHook extends HotDrinkTemplate{
  @Override
  public boolean wantCondimentsHook() {
      System.out.println("y/n:");
      BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
      String result="";
      try {
          result=in.readLine();
      }catch (IOException e){
          e.printStackTrace();
      }
      if (result.equals("n")){
          return false;
      }
      return true;
  }

  @Override
  public void brew() {
      System.out.println("泡茶");
  }

  @Override
  public void addCodiments() {
      System.out.println("加柠檬");
  }
}

 

/**
* @author: 肖德子裕
* @date: 2019/03/03 19:58
* @description: 测试
*/
public class Main {
  public static void main(String[] args) {
      TeaWithHook tea=new TeaWithHook();
      tea.prepareRecipe();
  }
}

 

模板方法模式的优势

  • 封装不变的内容,扩展可变部分,若是咱们要添加一个H3悍马模型,只须要继承父类就能够了。

  • 提取公共部分代码,便于维护

  • 行为由父类控制,子类实现。基本方法是由子类实现的,所以子类能够经过拓展的方法增长相应的功能,符合开闭原则。

模板方法模式的使用场景

  • 多个子类有公有的方法,而且逻辑基本相同时

  • 重复、复杂的算法,能够把核心算法设计为模板方法,周边的细节则有各个子类实现

  • 代码重构时,模板方法模式是一个常用的模式,把相同的代码抽取到父类中,而后用钩子方法约束其行为。

 

钩子方法源于设计模式中模板方法模式,模板方法模式中分为两大类:模版方法和基本方法,而基本方法又分为:抽象方法,具体方法,钩子方法。

好莱坞原则:明星无需联系经纪人问名具体流程,只需作好本身的事便可,经纪人会通知明星去哪里作什么。高层无需知道调用底层的细节,利于解耦。比如模板模式的子类无需知道模板的步骤,只需作好本身该实现的方法。

 

8)代理模式

为一个对象提供一个替身,以控制这个对象的访问;被代理的对象能够是远程对象,建立开销大的对象或须要安全控制的对象;代理模式有不少变体,都是为了控制与管理对象访问。

扩展:RMI

RMI远程方法调用是计算机之间经过网络实现对象调用的一种通信机制;使用这种机制,一台计算机上的对象能够调用另一台计算机上的对象来获取远程数据;在过去,TCP/IP通信是远程通信的主要手段,面向过程开发;而RPC使程序员更容易地调用远程程序,但在复杂的信息传讯时,RPC依然未能很好的支持;RMI被设计成一种面向对象开发方式,容许程序员使用远程对象来实现通讯。

代理模式分类:

静态代理:在软件设计时经常使用的代理通常是指静态代理,也就是在代码中显式指定的代理。

/**
* @author: 肖德子裕
* @date: 2018/10/12 11:23
* @description: 用户登陆接口
*/
public interface UserServer {
  void login();
}

 

/**
* @author: 肖德子裕
* @date: 2018/10/12 11:24
* @description: 用户登陆实现类
*/
public class UserServerImpl implements UserServer{
  @Override
  public void login() {
      System.out.println("用户登陆");
  }
}

 

/**
* @author: 肖德子裕
* @date: 2018/10/12 11:27
* @description: 静态代理模式
*/
public class UserServerProxy implements UserServer{
  private UserServer userServer;

  public UserServerProxy(UserServer userServer){
      this.userServer=userServer;
  }

  @Override
  public void login() {
      before();
      userServer.login();
      after();
  }

  public void before(){
      System.out.println("登陆以前");
  }

  public void after(){
      System.out.println("登陆以后");
  }
}

 

/**
* @author: 肖德子裕
* @date: 2018/10/12 11:32
* @description: 测试静态代理
*/
public class Main {
  public static void main(String[] args) {
      UserServer userServer=new UserServerProxy(new UserServerImpl());
      userServer.login();
  }
}

静态代理的缺点很明显:一个代理类只能对一个业务接口的实现类进行包装,若是有多个业务接口的话就要定义不少实现类和代理类才行。并且,若是代理类对业务方法的预处理、调用后操做都是同样的(好比:调用前输出提示、调用后自动关闭链接),则多个代理类就会有不少重复代码。这时咱们能够定义这样一个代理类,它能代理全部实现类的方法调用:根据传进来的业务实现类和方法名进行具体调用,那就是动态代理。

 

JDK动态代理:JDK动态代理所用到的代理类在程序调用到代理类对象时才由JVM真正建立,JVM根据传进来的 业务实现类对象 以及 方法名 ,动态地建立了一个代理类的class文件并被字节码引擎执行,而后经过该代理类对象进行方法调用。**咱们须要作的,只需指定代理类的预处理、调用后操做便可。

1)被代理对象必需要实现接口,才能产生代理对象,若是没有接口将不能使用动态代理技术;

2)动态代理可对方法进行加强,如增长事务的打开与提交;

3)在不破坏原有结构的状况下,生成动态代理对象,对原有方法进行加强。

注意: JDK动态代理的代理对象在建立时,须要使用业务实现类所实现的接口做为参数(由于在后面代理方法时须要根据接口内的方法名进行调用)。若是业务实现类是没有实现接口而是直接定义业务方法的话,就没法使用JDK动态代理了。而且,若是业务实现类中新增了接口中没有的方法,这些方法是没法被代理的(由于没法被调用)。

public interface BookFacade {  
  public void addBook();  
}

 

public class BookFacadeImpl implements BookFacade {   
  @Override  
  public void addBook() {  
      System.out.println("增长图书方法。。。");  
  }  
}

 

public class BookFacadeProxy implements InvocationHandler {  
//这其实业务实现类对象,用来调用具体的业务方法
  private Object target;
   
  /**
    * 绑定业务对象并返回一个代理类  
    */  
  public Object bind(Object target) {  
      this.target = target; //接收业务实现类对象参数

      //经过反射机制,建立一个代理类对象实例并返回。用户进行方法调用时使用
      //建立代理对象时,须要传递该业务类的类加载器(用来获取业务实现类的元数据,在包装方法是调用真正的业务方法)、接口、handler实现类
      return Proxy.newProxyInstance(target.getClass().getClassLoader(),  
              target.getClass().getInterfaces(), this); }  
  /**
    * 包装调用方法:进行预处理、调用后处理
    */  
  public Object invoke(Object proxy, Method method, Object[] args)  
          throws Throwable {  
      Object result=null;  

      System.out.println("预处理操做——————");  
      //调用真正的业务方法  
      result=method.invoke(target, args);  

      System.out.println("调用后处理——————");  
      return result;  
  }  
}  

 

public static void main(String[] args) {  
      BookFacadeImpl bookFacadeImpl=new BookFacadeImpl();
      BookFacadeProxy proxy = new BookFacadeProxy();  
      BookFacade bookfacade = (BookFacade) proxy.bind(bookFacadeImpl);  
      bookfacade.addBook();  
}  

 

在列举一个动态代理的例子:

public interface UserDao {
  public String creatSqlSession();
}

 

public class UserDaoImpl implements UserDao{
  @Override
  public String creatSqlSession() {
      return "建立SQLSESSION对象";
  }
}

 

public class UserDaoProxyFactory implements InvocationHandler{
  private UserDao userDao;

  public UserDaoProxyFactory(UserDao userDao){
      this.userDao=userDao;
  }

  public UserDao getUserDaoProxy(){
      //生成动态代理
      UserDao userProxy=(UserDao) Proxy.newProxyInstance(UserDaoProxyFactory.class.getClassLoader(),
              UserDaoImpl.class.getInterfaces(),
              this);
      //返回一个动态代理对象
      return userProxy;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      Object invoke=method.invoke(userDao,args);
      return invoke;
  }
}

 

public class Main {
  public static void main(String[] args) {
      UserDao userDao=new UserDaoImpl();
      UserDaoProxyFactory factory=new UserDaoProxyFactory(userDao);
      UserDao userProxy=factory.getUserDaoProxy();
      System.out.println(userProxy.creatSqlSession());
      System.out.println(userProxy instanceof UserDaoImpl);
  }
}

CGLIB动态代理:cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。由于采用的是继承,因此不能对final修饰的类进行代理。

public class BookFacadeImpl1 {  
  public void addBook() {  
      System.out.println("新增图书...");  
  }  
}  

 

public class BookFacadeCglib implements MethodInterceptor {  
  private Object target;//业务类对象,供代理方法中进行真正的业务方法调用
 
  //至关于JDK动态代理中的绑定
  public Object getInstance(Object target) {  
      this.target = target; //给业务对象赋值
      Enhancer enhancer = new Enhancer(); //建立增强器,用来建立动态代理类
      enhancer.setSuperclass(this.target.getClass()); //为增强器指定要代理的业务类(即:为下面生成的代理类指定父类)
      //设置回调:对于代理类上全部方法的调用,都会调用CallBack,而Callback则须要实现intercept()方法进行拦
      enhancer.setCallback(this);
      // 建立动态代理类对象并返回  
      return enhancer.create();
  }
  // 实现回调方法
  public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
      System.out.println("预处理——————");
      proxy.invokeSuper(obj, args); //调用业务类(父类中)的方法
      System.out.println("调用后操做——————");
      return null;
  }

 

public static void main(String[] args) {      
      BookFacadeImpl1 bookFacade=new BookFacadeImpl1();
      BookFacadeCglib cglib=new BookFacadeCglib();  
      BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(bookFacade);  
      bookCglib.addBook();  
}  

总结:

静态代理是经过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户经过代理类调用被包装过的业务方法;

JDK动态代理是经过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;

CGlib动态代理是经过继承业务类,生成的动态代理类是业务类的子类,经过重写业务方法进行代理;

相关文章
相关标签/搜索