设计模式
从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题。
设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联和组合关系的充分理解。
正确使用设计模式具有一下优点:
1、可以提高程序员的思维能力、编码能力和设计能力。
2、使程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期。
3、使设计的代码可重用性高、可读性强、可靠行高、灵活性好、可维护性强。
1、3大类
创建性模式:
单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式
结构型模式:
适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式,代理模式
行为型模式:
模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、接解释器模式、状态模式、策略模式、职责链模式、访问者模式。
2、OOP七大法则
- 开闭原则:对扩展开放,对修改关闭。
- 里氏替换原则:继承必须确保超类所拥有的性质在子类中仍然成立。
- 依赖倒置原则:要面向接口编程,不要面向实现编程。
- 单一职责原则:控制类的粒度大小、将对象解耦、提高其内聚性。
- 接口隔离原则:要为各个类建立他们需要专用接口。
- 迪米特法则:只与你的朋友直接交谈,不跟陌生人说话。
- 合成复用原则:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
3、单例模式
按照惯有的分类方式,设计模式总共分为3大类:1、创建型 2、结构型 3、行为型。
单例模式便是创建型设计模式的一种,它确保某一个类在系统中只有一个实例,并自行实例化,同时向外部提供获取这个唯一实例的接口。从这段描述中,我们不难可以得到单例模式的三大特性:
单例类只有一个实例。
单例类必须自己实例化自己。
单例类需要向外提供实例。
3.1、饿汉式
/**
* 上面是饿汉式单例模式的标准代码,
* EagerSingleton类的实例因为变量instance申明为static的关系,
* 在类加载过程中便会执行。由此带来的好处是Java的类加载机制本身为我们保证了实例化过程的线程安全性,
* 缺点是这种空间换时间的方式,即使类实例本身还未用到,实例也会被创建。
* */
class EagerSingleton {
// 静态变量,类在创建之初就会执行实例化动作。
private static EagerSingleton instance = new EagerSingleton();
// 私有化构造函数,使外界无法创建实例
private EagerSingleton(){}
// 为外界提供获取实例接口
public static EagerSingleton getInstance(){
return instance;
}
}
3.2、懒汉式
/**
* 懒汉式
* 这个缺点的原因,涉及到并发编程的原子性。
* 实例中,创建实例的代码逻辑失去了原子性从而导致可能存在多个实例创建的情况。
* */
public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton(){
System.out.println(Thread.currentThread().getName()+"ok");
}
// 为外界提供获取实例接口
public static LazySingleton getInstance(){
if(instance == null){
instance = new LazySingleton(); // 懒加载
}
return instance;
}
/**
* 多线程并发
* 发生了多次实例化对象
* */
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
LazySingleton.getInstance();
}).start();
}
}
}
3.3、懒汉式加锁
/**
* 双重检测锁
* */
class Singleton2 {
//volatile 原子性操作
private volatile static Singleton2 instance = null;
/**
* 设置标志位
* */
private static boolean youyuan = false;
private Singleton2(){
synchronized (Singleton2.class){
if (youyuan == false){
youyuan = true;
}else {
throw new RuntimeException("不要试图使用反射破坏异常");
}
}
System.out.println(Thread.currentThread().getName()+"ok");
}
public static Singleton2 getInstance(){
//先检查实例是否存在,如果不存在才进入下面的同步块
if(instance == null){
//同步块,线程安全的创建实例
synchronized (Singleton2.class) {
//再次检查实例是否存在,如果不存在才真正的创建实例
if(instance == null){
instance = new Singleton2();//不是一个原子性操作
}
}
}
return instance;
}
/**
* 线程安全
* */
// public static void main(String[] args) {
// for (int i = 0; i < 10; i++) {
// new Thread(()->{
// Singleton2.getInstance();
// }).start();
// }
// }
//可以通过反射破坏
public static void main(String[] args) throws Exception{
//Singleton2 instance = Singleton2.getInstance();
Constructor<Singleton2> declaredConstructor = Singleton2.class.getDeclaredConstructor();
Field youyuan = Singleton2.class.getDeclaredField("youyuan");
youyuan.setAccessible(true);
declaredConstructor.setAccessible(true);
Singleton2 singleton2 = declaredConstructor.newInstance();
//修改字段
youyuan.set(singleton2,false);
Singleton2 singleton21 = declaredConstructor.newInstance();
System.out.println(singleton21);
System.out.println(singleton2);
}
}
3.4、静态内部类
/**
* 静态内部类
* */
class Singleton3 {
private Singleton3(){
System.out.println(Thread.currentThread().getName()+"ok");
}
// 只有当类被调用时,才会加载
private static class SingletonHolder{
// 静态初始化器,由JVM来保证线程安全
private static Singleton3 instance = new Singleton3();
}
public static Singleton3 getInstance(){
return SingletonHolder.instance;
}
}
3.5、枚举类
/**
* 枚举类,本身也是一个类,不能通过反射获取字段
* */
enum Singleton {
uniqueInstance;
public Singleton singletonOperation(){
// 单例类的其它操作
return uniqueInstance;
}
public static void main(String[] args) throws Exception{
Constructor<Singleton> declaredConstructor = Singleton.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
Singleton singleton = declaredConstructor.newInstance();
System.out.println(singleton);
}
}
4、工厂模式
实现了创建者和调用者的分离。
4.1、简单工厂模式
用来生产同一等级结构中的任意产品(对于增加新的产品,需要覆盖已有代码)
Car接口
public interface Car {
public void carName();
}
Car实现类
public class Wuling implements Car {
@Override
public void carName() {
System.out.println("Wuling");
}
}
静态工厂
public class CarFactory {
public static Car getCar(String name){
if (name.equals("DaZhong")){
return new DaZhong();
}else if (name.equals("Wuling")){
return new Wuling();
}else{
return null;
}
}
}
测试
public class Main {
public static void main(String[] args) {
// Wuling wuling = new Wuling();
// wuling.carName();
Car wuling = CarFactory.getCar("Wuling");
wuling.carName();
}
}
4.2、工厂方法模式
用来生产同一等级结构中的固定产品(支持增加任意产品)
4.3、抽象工厂模式
围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。
抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定他们具体的类。
适用场景:
-
客户端不依赖于产品类实例如何被创建、实现等细节。
-
强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量的重复代码。
-
提供一个产品类的库,所有的产品以同样的接口出现,从而使得客户端不依赖于具体的实现。
优点
-
具体产品在应用层的代码隔离,无需关心创建的细节。
-
将一个系列的产品统一到一起创建。
4.4、小结
小结
1、简单工厂模式(静态工厂模式):虽然某种程度上不符合设计原则,但实际使用最多。
2、工厂方法模式:不修改已有类的前提上,通过增加新的工厂类实现扩展。
3、抽象工厂模式:不可以增加产品,可以增加产品族。
应用场景
1、JDk中Calendar的getInstance方法
2、JDBC中的Connection对象的获取
3、Spring中IOC容器创建管理bean对象
4、反射中Class对象的newInstance方法
核心本质
-
实例化对象不使用new,用工厂方法替代。
-
将选择实现类,创建对象统一管理和控制,从而将调用者跟我们的实现类解耦。
5、建造者模式
建造者模式也属于创建型模式,他提供了一种创建对象的最佳方式。
定义
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
主要作用
在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。用户只需要给出指定复杂对象的类型和内容,建造者模式负责按顺序创建复杂对象(把内部的创建过程和细节隐藏起来)。
例子
工厂(建造者模式):负责制造汽车。
汽车购买者(用户):只需要说出你需要的型号,然后直接购买就可以使用了
5.1、监视者模式
指挥
//指挥:核心。负责指挥构建一个工程,工程如何构建,由它决定
public class Director {
//指挥工人按照顺序建房子
public Product build(Builder builder){
builder.buildA();
builder.buildB();
builder.buildC();
builder.buildD();
return builder.getProduct();
}
}
具体的建造者
//抽象的建造者:方法
public abstract class Builder {
abstract void buildA(); //地基
abstract void buildB(); //钢筋水泥
abstract void buildC(); //铺电线
abstract void buildD(); //粉刷
//完工:得到产品
abstract Product getProduct();
}
//具体的建造者:工人
public class Worker extends Builder {
private Product product;
public Worker() {
this.product = new Product();
}
@Override
void buildA() {
product.setBuildA("地基");
System.out.println("地基");
}
@Override
void buildB() {
product.setBuildA("钢筋水泥");
System.out.println("钢筋水泥");
}
@Override
void buildC() {
product.setBuildA("铺电线");
System.out.println("铺电线");
}
@Override
void buildD() {
product.setBuildA("粉刷");
System.out.println("粉刷");
}
@Override
Product getProduct() {
return product;
}
}
产品
package org.javaboy.builder;
/**
* @Author you猿
* @Date 2020/6/27 12:09
*/
//产品:房子
public class Product {
private String buildA; //地基
private String buildB; //钢筋水泥
private String buildC; //铺电线
private String buildD; //粉刷
public String getBuildA() {
return buildA;
}
public void setBuildA(String buildA) {
this.buildA = buildA;
}
public String getBuildB() {
return buildB;
}
public void setBuildB(String buildB) {
this.buildB = buildB;
}
public String getBuildC() {
return buildC;
}
public void setBuildC(String buildC) {
this.buildC = buildC;
}
public String getBuildD() {
return buildD;
}
public void setBuildD(String buildD) {
this.buildD = buildD;
}
@Override
public String toString() {
return "Product{" +
"buildA='" + buildA + '\'' +
", buildB='" + buildB + '\'' +
", buildC='" + buildC + '\'' +
", buildD='" + buildD + '\'' +
'}';
}
}
测试
public class Test {
public static void main(String[] args) {
//指挥
Director director = new Director();
//指挥 具体的工人完成 产品
Product build = director.build(new Worker());
System.out.println(build.toString());
}
}
5.2、匿名模式
建造者
//建造者
public abstract class Builder {
abstract Builder builderA(String msg); //汉堡
abstract Builder builderB(String msg); //可乐
abstract Builder builderC(String msg); //薯条
abstract Builder builderD(String msg); //甜点
abstract Product getProduct();
}
public class Worker extends Builder {
private Product product;
public Worker( ) {
this.product = new Product();
}
@Override
Builder builderA(String msg) {
product.setBuilderA(msg);
return this;
}
@Override
Builder builderB(String msg) {
product.setBuilderB(msg);
return this;
}
@Override
Builder builderC(String msg) {
product.setBuilderC(msg);
return this;
}
@Override
Builder builderD(String msg) {
product.setBuilderD(msg);
return this;
}
@Override
Product getProduct() {
return product;
}
}
产品
package org.javaboy.builder.demo2;
/**
* @Author you猿
* @Date 2020/6/27 20:13
*/
public class Product {
private String builderA = "汉堡";
private String builderB = "可乐";
private String builderC = "薯条";
private String builderD = "甜点";
public String getBuilderA() {
return builderA;
}
public void setBuilderA(String builderA) {
this.builderA = builderA;
}
public String getBuilderB() {
return builderB;
}
public void setBuilderB(String builderB) {
this.builderB = builderB;
}
public String getBuilderC() {
return builderC;
}
public void setBuilderC(String builderC) {
this.builderC = builderC;
}
public String getBuilderD() {
return builderD;
}
public void setBuilderD(String builderD) {
this.builderD = builderD;
}
@Override
public String toString() {
return "Product{" +
"builderA='" + builderA + '\'' +
", builderB='" + builderB + '\'' +
", builderC='" + builderC + '\'' +
", builderD='" + builderD + '\'' +
'}';
}
}
测试
public class Test {
public static void main(String[] args) {
//服务员
Worker worker = new Worker();
//链式编程
Product product = worker.builderA("冰淇淋").builderB("烤鸡腿")
.getProduct();
System.out.println(product.toString());
}
}
5.3、优缺点
优点
- 产品的建造和表示分离,实现了解耦。
- 将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰。
- 具体的建造者类之间是相互独立的,这有利于系统的扩展。增加新的具体建造者无需修改原有类库的代码,符合开闭原则。
缺点
- 创建的产品一般具有较多的共同点,其组成部分相似:如果产品之间差异性很大,不适用于建造者模式。
5.4、应用场景
应用场景:
-
需要生成的产品对象有复杂的内部结构,这些产品对象具备共性;
-
隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。
-
适合于一个具有较多的零件(属性)的产品(对象)的创建过程。
建造者与抽象工厂模式的比较:
-
与抽象工厂模式相比,建造者模式返回一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族。
-
在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,它侧重于-步步构造一 个复杂对象,返回-一个完整的对象。
-
如果将抽象工厂模式看成汽车配件生产工厂,生产-一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回-辆完整的汽车!
6、原型模式
Prototype、Cloneable接口、clone()方法、 克隆
6.1、浅克隆
1、实体类
/**
* 1、实现一个接口 Cloneable
* 2、重写一个方法
* */
//video
public class Video implements Cloneable {
private String name;
private Date createTime;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public Video(String name, Date createTime) {
this.name = name;
this.createTime = createTime;
}
public Video() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
@Override
public String toString() {
return "Video{" +
"name='" + name + '\'' +
", createTime=" + createTime +
'}';
}
}
2、克隆
public static void main(String[] args) throws CloneNotSupportedException {
//原型对象 v1
Date date = new Date();
Video v1 = new Video("youyuan", date);
//v1 克隆 v2
Video v2 = (Video) v1.clone();//克隆出来的对象和原来是一模一样的
System.out.println("v1="+v1);
System.out.println("v2="+v2);
System.out.println("=========================");
date.setTime(12435);
System.out.println("v1="+v1);
System.out.println("v2="+v2);
}
6.2、深克隆
- 序列化与反序列化
- 改造clone()方法
1、实体类
/**
* 1、实现一个接口 Cloneable
* 2、重写一个方法
* */
//video
public class Video implements Cloneable {
private String name;
private Date createTime;
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone();
//实现深克隆~
Video v = (Video) obj;
v.createTime = ((Date) this.createTime.clone());
return obj;
}
public Video(String name, Date createTime) {
this.name = name;
this.createTime = createTime;
}
public Video() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
@Override
public String toString() {
return "Video{" +
"name='" + name + '\'' +
", createTime=" + createTime +
'}';
}
}
2、克隆
public static void main(String[] args) throws CloneNotSupportedException {
//原型对象 v1
Date date = new Date();
Video v1 = new Video("youyuan", date);
//v1 克隆 v2
Video v2 = (Video) v1.clone();//克隆出来的对象和原来是一模一样的
System.out.println("v1="+v1);
System.out.println("v2="+v2);
System.out.println("=========================");
date.setTime(12435);
System.out.println("v1="+v1);
System.out.println("v2="+v2);
}
7、配器模式
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作!
7.1、角色分析
- 目标接口:客户所期待的接口,目标可以是具体的或抽象的类,也可以是接口。(USB接口)
- 需要适配的类:需要适配的类或者适配者类。(网线)
- 适配器:通过包装一个需要适配的对象,把原接口转换成目标对象。
7.2、对象适配器的优点
- 一个对象适配器可以把多个不同的适配者适配到同一目标
- 可以适配一个适配者的子类,由于适配器和适配者之间是关联关系,适配者的子类也可通过该适配器进行适配。
7.3、类适配器缺点
- 一次最多只能适配一个适配者类,不能同时适配多个适配者。
- 类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性。
7.4、适用场景
- 系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码。
- 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可以在将来引进的类一起工作。
package org.javaboy.adapter;
/**
* @Author you猿
* @Date 2020/5/21 18:43
*/
//1. 继承(类适配器,单继承)
//2. 组合(对象适配器:常用)
//真正的适配者,需要连接USB,连接网线
public class Adapter implements NetToUsb {
//extend Adaptee 类适配器
//网线
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void handleRequest() {
//super.connectInternet();
this.adaptee.connectInternet();
}
public static void main(String[] args) {
// Computer computer = new Computer(); //电脑
// Adaptee adaptee = new Adaptee(); //网线
// Adapter adapter = new Adapter(); //转换接口
// computer.net(adapter);
Computer computer = new Computer(); //电脑
Adaptee adaptee = new Adaptee(); //网线
Adapter adapter = new Adapter(adaptee);
computer.net(adapter);
}
}
//网线,要被适配的类。
class Adaptee {
public void connectInternet(){
System.out.println("连接到网络");
}
}
//客户端类,想上网,插不上网线
class Computer {
//电脑需要连接上转接器才可以上网
public void net(NetToUsb netToUsb){
//上网的实现,找个转接头
netToUsb.handleRequest();
}
}
//接口转换器的抽象实现
interface NetToUsb{
//作用:处理请求,将网线插入usb
public void handleRequest();
}
8、桥接模式
桥接模式是将抽象部分与它的实现分离,使得他们都可以独立地变化。它是一种对象结构型模式,又称为柄提模式或接口模式。
品牌
//品牌
public interface Brand {
void info();
}
//苹果品牌
public class Apple implements Brand {
@Override
public void info() {
System.out.print("苹果");
}
}
//联想品牌
public class Lenovo implements Brand {
@Override
public void info() {
System.out.print("联想");
}
}
电脑类型
//抽象的电脑类型类
public abstract class Computer {
//组合 品牌
protected Brand brand;
public Computer(Brand brand) {
this.brand = brand;
}
public void info(){
brand.info();//自带品牌
}
}
//台式
class Desktop extends Computer{
public Desktop(Brand brand) {
super(brand);
}
@Override
public void info() {
super.info();
System.out.println("台式机");
}
}
//苹果
class Laptop extends Computer{
public Laptop(Brand brand) {
super(brand);
}
@Override
public void info() {
super.info();
System.out.println("笔记本");
}
}
组合
public class Test {
public static void main(String[] args) {
//苹果 笔记本
Computer computer1 = new Laptop(new Apple());
computer1.info();
//联想台式机
Computer computer2 = new Desktop(new Lenovo());
computer2.info();
}
}
8.1、最佳实践
-
如果一个系统需要在构建的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使必们在抽象层建立-个关联关系。抽象化角色和实现化角色可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一 个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。
-
一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
-
虽然在系统中使用继承是没有问题的, 但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。
场景:
1、Java语言通过Java虚拟机实现了平台的无关性。
2、AWT中的Peer架构
3、JDBC驱动程序也是桥接模式的应用之一。
8.2、优缺点
好处分析:
-
桥接模式偶尔类似于多继承方案,但是多继承方案违背了类的单一职责原则,复用性比较差,类的个数也非常多,桥接模式是比多继承方案更好的解决方法。极大的减少了子类的个数,从而降低管理和维护的成本。
-
桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。符合开闭原则,就像- -座桥,可以把两个变化的维度连接起来!
劣势分析:
-
桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
-
桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。
9、静态代理
角色分析:
-
抽象角色:一般使用接口或者抽象类来解决
-
真实角色:被代理的角色
-
代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作。
-
客户:访问代理对象的人。
代码步骤
1、接口
//租房
public interface Rent {
public void rent();
}
2、真实角色
public class Host implements Rent {
@Override
public void rent() {
System.out.println("房东要出租房子");
}
}
3、中介
public class Proxy implements Rent{
private Host host;
public Proxy(){
}
public Proxy(Host host) {
this.host = host;
}
@Override
public void rent() {
host.rent();
}
//看房
public void seeHouse(){
System.out.println("中介带你看房");
}
//签合同
public void heTong(){
System.out.println("签合同");
}
//收中介费
public void fare(){
System.out.println("收中介费");
}
}
4、客户端访问代理角色
public class Client {
public static void main(String[] args) {
//房东要租房子
Host host = new Host();
//代理,中介帮房东租房子
Proxy proxy = new Proxy(host);
//租房直接找中介
proxy.rent();
}
}
代理模式的好处:
- 可以使真实的操作更加纯粹,不用去关注一些公共的业务。
- 公共也就交给代理角色,实现了业务的分工。
- 公共业务发生扩展的时候,方便集中管理。
缺点:
- 一个真实的角色就会产生一个代理角色。代码量会翻倍,开发效率会变低
9.1、AOP实例
AOP的实现
代码:
public interface UserService {
public void add();
public void update();
public void delete();
public void query();
}
package org.javaboy.staticproxy.demo2;
//真实对象
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("add");
}
@Override
public void update() {
System.out.println("update");
}
@Override
public void delete() {
System.out.println("delete");
}
@Override
public void query() {
System.out.println("query");
}
}
//代理对象
public class UserServiceProxy implements UserService {
private UserServiceImpl userService;
public UserServiceProxy(UserServiceImpl userService) {
this.userService = userService;
}
@Override
public void add() {
log("日志");
System.out.println("add");
}
@Override
public void update() {
System.out.println("update");
}
@Override
public void delete() {
System.out.println("delete");
}
@Override
public void query() {
System.out.println("query");
}
//日志方法
public void log(String msg){
System.out.println("使用了"+msg+"方法");
}
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
}
测试:
public class Client {
public static void main(String[] args) {
UserServiceProxy userServiceProxy = new UserServiceProxy(new UserServiceImpl());
userServiceProxy.add();
}
}
10、动态代理
-
动态代理和静态代理角色一样
-
动态代理的代理类是动态生成的,不是我们直接写好的。
-
动态代理分为两大类:基于接口的动态代理,基于类的动态代理。
1、基于接口 =》JDK动态代理【使用】
2、基于类:cglib
3、java字节码实现:javasist
需要了解两个类:
- Proxy:代理
- InvocationHandler:调用处理程序。
10.1、租房实例
代码
//房东
public class Host implements Rent {
@Override
public void rent() {
System.out.println("房东要出租房子");
}
}
//租房
public interface Rent {
public void rent();
}
//用这个类,自动生成代理类!
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);
}
//处理代理实例,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质,就是使用反射机制实现
Object res = method.invoke(target, args);
return res;
}
}
//测试
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理角色:未创建
ProxyInvocationHandler handler = new ProxyInvocationHandler();
//通过调用程序处理角色来处理我们要调用的接口对象
handler.setTarget(host);
//这里的proxy就是动态生成的,并没有写(面向接口编程)
Rent proxy = (Rent) handler.getProxy();
proxy.rent();
}
}
封装接口
//用这个类,自动生成代理类!
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);
}
//处理代理实例,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName());
//动态代理的本质,就是使用反射机制实现
Object res = method.invoke(target, args);
return res;
}
}
//测试
public class Client {
public static void main(String[] args) {
//真实角色
UserServiceImpl userService = new UserServiceImpl();
//代理角色,不存在
ProxyInvocationHandler handler = new ProxyInvocationHandler();
//设置代理对象
handler.setTarget(userService);
//动态生成代理类
UserService proxy = (UserService) handler.getProxy();
proxy.query();
}
}