结构型模式(Structural Pattern)用于将类或对象结合在一块儿造成更强大的结构,就像搭积木,能够经过简单的积木组合出复杂、功能强大的模型。java
结构型模式 | 重要程度 |
---|---|
适配器模式(Adapter) | ⭐⭐⭐⭐ |
桥接模式(Bridge) | ⭐⭐⭐ |
组合模式(Composite) | ⭐⭐⭐⭐ |
装饰者模式(Decorator) | ⭐⭐⭐ |
外观模式(Facade) | ⭐⭐⭐⭐⭐ |
享元模式(Flyweight) | ⭐ |
代理模式(Proxy) | ⭐⭐⭐⭐ |
生活中,充电插头有两脚的、三脚的,还有圆形的,若是想使这些插头都能工做,就须要一个多功能适配器git
适配器模式(Adapter Pattern)属于结构性模式,它能够将某个类的接口转换为客户端指望的另外一个接口表示,主要目的是兼容性,让本来因接口不匹配不能一块儿工做的两个类能够协同工做,其别名为包装器(Wrapper)。适配器模式主要分为三类:类适配器模式、对象适配器模式、接口适配器模式。github
Adapter 类继承 src 类,实现 dst 接口,完成 src 对 dst 的适配。web
插座(Voltage220V)的输出电压是220V,充电插头(Voltage5V)输出电压是5V,这时候就须要一个适配器(VoltageAdapter)转换电压,才能给手机(Phone)充电数据库
电源输出电压为220V编程
public class Voltage220V { public int output220V() { int src = 220; System.out.println("电源输出" + src + "V"); return src; } }
充电器输出电压为5V设计模式
public interface Voltage5V { int output5V(); }
适配器须要将220V转为5V数组
public class VoltageAdapter extends Voltage220V implements Voltage5V { @Override public int output5V() { int src = super.output220V(); int dst = src / 44; System.out.println("转换为" + dst + "V"); return dst; } }
手机接收5V电压,判断电压是否为5V缓存
public class Phone { public static void charging(Voltage5V voltage5V){ int v = voltage5V.output5V(); if(v == 5){ System.out.println("接收电压为5V,正常充电"); }else if(v > 5){ System.out.println("电压高于5V,没法充电"); } } }
测试方法安全
@Test public void test01(){ System.out.println("====类适配器模式===="); Phone.charging(new VoltageAdapter()); }
运行结果
====类适配器模式==== 电源输出220V 转换为5V 接收电压为5V,正常充电
基本的思路和类的适配器模式相同,只是将 Adapter 类作修改,使用聚合关系替代继承关系
沿用前面的代码,新建一个适配器,只是将原来的 Adapter 继承 src 类换为聚合的关系
public class VoltageAdapter2 implements Voltage5V { private Voltage220V voltage220V; public VoltageAdapter2(){ this.voltage220V = new Voltage220V(); } @Override public int output5V() { int src = this.voltage220V.output220V(); int dst = src / 44; return dst; } }
测试方法
@Test public void test02(){ System.out.println("====对象适配器模式===="); Phone.charging(new VoltageAdapter2(new Voltage220V())); }
运行结果
====对象适配器模式==== 电源输出220V 转换为5V 接收电压为5V,正常充电
接口适配器模式也可称为缺省适配器模式,当不须要实现接口的所有方法时,可先设计一个抽象类实现接口,并为该接口的每一个方法都提供一个默认实现,那么该抽象类的子类就能够有选择的覆盖父类的某些方法来实现需求。
适用于一个接口不想使用其全部的方法的状况
写一个接口,里面定义一些方法
public interface InterfaceMethod { void m1(); void m2(); void m3(); void m4(); }
一个抽象类,实现该接口
public abstract class AbstractAdapter implements InterfaceMethod { @Override public void m1() { } @Override public void m2() { } @Override public void m3() { } @Override public void m4() { } }
测试方法
@Test public void test(){ //使用匿名内部类的方式 AbstractAdapter adapter = new AbstractAdapter() { @Override public void m1() { System.out.println("我要用m1方法"); } }; adapter.m1(); }
运行结果
我要用m1方法
类适配器:以类给到,在 Adapter 里,就是将 src 当作类,继承
对象适配器:以对象给到,在 Adapter 里, 将 src 做为一个对象,持有
接口适配器:以接口给到,在 Adapter 里,将 src 做为一个接口,实现
桥接模式包含以下角色:
咱们以手机为例,手机有品牌(诺基亚、摩托罗拉)和样式(折叠式、直立式),咱们须要生产不一样的品牌和样式,好比折叠式诺基亚、直立式摩托罗拉... ...
「实现类接口」 - 手机品牌,都有开机和关机的功能
public interface PhoneBrand { void open(); void close(); }
「具体实现类」 - 手机品牌 Nokia 和 Moto
public class Nokia implements PhoneBrand { @Override public void open() { System.out.println("诺基亚开机..."); } @Override public void close() { System.out.println("诺基亚关机..."); } }
public class Moto implements PhoneBrand { @Override public void open() { System.out.println("摩托罗拉开机..."); } @Override public void close() { System.out.println("摩托罗拉关机..."); } }
「抽象类」 - 手机类,以聚合的方式与品牌产生联系,充当着“桥”的角色
public abstract class AbsPhone{ private PhoneBrand brand; public AbsPhone(PhoneBrand brand) { this.brand = brand; } protected void open(){ brand.open(); } protected void close(){ brand.close(); } }
「扩充抽象类」 - 折叠式手机 和 直立式手机
public class FoldingPhone extends AbsPhone{ public FoldingPhone(PhoneBrand brand) { super(brand); } @Override protected void open() { System.out.print("折叠式 - "); super.open(); } @Override protected void close() { System.out.print("折叠式 - "); super.close(); } }
public class UpRightPhone extends AbsPhone{ public UpRightPhone(PhoneBrand brand) { super(brand); } @Override protected void open() { System.out.print("直立式 - "); super.open(); } @Override protected void close() { System.out.print("直立式 - "); super.close(); } }
测试
@Test public void test(){ AbsPhone p1 = new FoldingPhone(new Nokia()); p1.open(); p1.close(); System.out.println(); AbsPhone p2 = new UpRightPhone(new Moto()); p2.open(); p2.close(); }
结果
折叠式 - 诺基亚开机... 折叠式 - 诺基亚关机... 直立式 - 摩托罗拉开机... 直立式 - 摩托罗拉关机...
若是咱们想建立其余类型的手机,只须要改变建立方式便可。
在 Java 中咱们一般使用 JDBC 链接数据库,可是数据库的种类有不少(MySQL、Oracle...),它们的链接方式、协议都不尽相同,很显然不能为每种数据库都写一个接口,这样就违背了精简设计原则,因而Java设计师就提供一套接口给厂商们本身实现,一套接口给用户调用。
咱们在使用 JDBC 的时候须要写这样的代码
Class.forName("数据库驱动名"); Connection conn = DriverManager.getConnection("数据库url", "用户名", "密码");
其过程是这样的:
Class.forName()
的时候,经过反射机制,将 .class
文件加载进Java虚拟机内存中,Driver
类初始化,执行如下代码,向 DriverManager
中注册一个驱动。DriverManager
是个 Driver
容器,管理不一样的 Driver
static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException("Can't register driver!"); } }
咱们获取链接时,DriverManager
就会根据驱动返回一个相应的数据库链接
@CallerSensitive public static Connection getConnection(String url, java.util.Properties info) throws SQLException { return (getConnection(url, info, Reflection.getCallerClass())); }
对于那些不但愿使用继承或由于多层次继承致使系统类的个数急剧增长的系统,桥接模式尤其适用。
一、组合模式(Composite Pattern)又叫部分总体模式,他建立了对象组的树形结构,将对象组合成树状结构以表示「总体 - 部分」的层次关系。
二、组合模式使得用户对单个对象和组合对象的访问具备一致性,即:组合能让客户以一致的方式处理个别对象以及组合对象
Component(抽象构件):定义参加组合对象的公有方法和属性,能够定义一些默认的行为和属性。
Composite(容器构件):树枝对象,它的做用是组合树枝结点和叶子结点造成一个树形结构。
Leaf(叶子构件):叶子构件的下面没有其余分支,也就是遍历的最小单位。
组合模式有两种实现:安全模式和透明模式,其结构以下图所示
Composite
类中声明并实现这些方法。要求:在页面展现出公司的部门组成(一个公司有多个部门,每一个部门有多个小组);
这是一种很明显的树形结构,所以能够用组合模式解决
「抽象构件」:OrganizationComponent
public abstract class OrganizationComponent { private String name; public OrganizationComponent(String name) { this.name = name; } protected void add(OrganizationComponent component) { throw new UnsupportedOperationException("不支持添加操做"); } protected void remove(OrganizationComponent component) { throw new UnsupportedOperationException("不支持删除操做"); } protected abstract void print(); public String getName() { return name; } public void setName(String name) { this.name = name; } }
「容器构件」:Company、Department
public class Company extends OrganizationComponent { private List<OrganizationComponent> components = new ArrayList<>(); public Company(String name) { super(name); } @Override protected void add(OrganizationComponent component) { components.add(component); } @Override protected void remove(OrganizationComponent component) { components.remove(component); } @Override protected void print() { System.out.println("======="+getName()+"======="); for (OrganizationComponent component : components) { component.print(); } } @Override public String getName() { return super.getName(); } }
public class Department extends OrganizationComponent { private List<OrganizationComponent> components = new ArrayList<>(); public Department(String name) { super(name); } @Override protected void add(OrganizationComponent component) { components.add(component); } @Override protected void remove(OrganizationComponent component) { components.remove(component); } @Override protected void print() { System.out.println("======="+getName()+"======="); for (OrganizationComponent component : components) { component.print(); } } @Override public String getName() { return super.getName(); } }
「叶子构件」:Group,叶子构件不没有子节点了,因此不须要添加、删除之类的方法
public class Group extends OrganizationComponent { public Group(String name) { super(name); } @Override protected void print() { System.out.println(getName()); } @Override public String getName() { return super.getName(); } }
「测试类」:Client
public class Client { @Test public void test01(){ OrganizationComponent company = new Company("阿里巴巴"); OrganizationComponent department1 = new Department("市场部"); OrganizationComponent department2 = new Department("技术部"); OrganizationComponent group1 = new Group("市场一组"); OrganizationComponent group2 = new Group("市场二组"); OrganizationComponent group3 = new Group("技术一组"); OrganizationComponent group4 = new Group("技术二组"); //添加部门 company.add(department1); company.add(department2); //添加小组 department1.add(group1); department1.add(group2); department2.add(group3); department2.add(group4); //打印结果 company.print(); } }
「运行结果」
=======阿里巴巴======= =======市场部======= 市场一组 市场二组 =======技术部======= 技术一组 技术二组
在 Java(jdk 1.8为例) 的集合类 HashMap 中,抽象构件是 Map,容器构件是 HashMap,叶子构件是 Node
进入源码能够看见,在 Map 中定义了许多公共方法
HashMap 实现了 Map,并对一些方法重写,并且 HashMap 中有一个静态内部类 Node,它就充当了叶子构件的角色,Node 中去除了 put、putAll 等方法,下面也没有子结点了
使用:
@Test public void test02(){ Map<String, String> map = new HashMap<>(); map.put("k1", "v1"); map.put("k2", "v2"); System.out.println(map); }
当咱们 put 一个键值对的时候,在 HashMap 内部会调用 putVal 方法,将键值对封装为 Node。
一、简化客户端操做。客户端只须要面对一致的对象而不用考虑总体部分或者节点叶子的问题。
二、具备较强的扩展性。当咱们要更改组合对象时,咱们只须要调整内部的层次关系,客户端不用作出任何改动。
三、方便建立出复杂的层次结构。客户端不用理会组合里面的组成细节,容易添加节点或者叶子从而建立出复杂的树形结构。
四、须要遍历组织机构,或者处理的对象具备树形结构时,很是适合使用组合模式。
五、要求较高的抽象性。若是节点和叶子有不少差别性的话,好比不少方法和属性都不同,不适合使用组合模式。
装饰者模式属于结构型模式,它能够动态的将新功能附加到对象上,同时又不改变其结构。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(OCP)。
装饰者和被装饰者有相同的超类型,由于装饰者和被装饰者必须是同样的类型,利用继承是为了达到类型的匹配,而不是利用继承获取行为
在咖啡店客人想点一杯加两份糖一份牛奶的摩卡咖啡,各个商品的价格以下,咱们须要根据用户点的咖啡、加的配料,动态的计算价格
商品 | 价格 |
---|---|
拿铁咖啡(LatteCoffee) | 4.5 |
摩卡咖啡(MochaCoffe) | 5.5 |
糖(Sugar) | 1.0 |
牛奶(Milk) | 2.0 |
「实体类」 Coffee
public abstract class Coffee{ public String des = "咖啡"; //描述 private float price = 0.0f; //价格 protected abstract float cost(); //计算费用 //省略getter setter方法 }
「被装饰者」LatteCoffee
public class LatteCoffee extends Coffee{ public LatteCoffee() { setDes("拿铁咖啡"); setPrice(4.5f); } @Override protected float cost() { return getPrice(); } }
「被装饰者」MochaCoffee
public class MochaCoffee extends Coffee { public MochaCoffee() { setDes("摩卡咖啡"); setPrice(5.5f); } @Override protected float cost() { return getPrice(); } }
「抽象装饰者」Decorator
public class Decorator extends Coffee { private Coffee coffee; public Decorator(Coffee drink) { this.coffee = drink; } @Override protected float cost() { return getPrice() + coffee.cost(); } @Override public String getDes() { return coffee.getDes() + "加" + super.getDes(); } }
「具体装饰者」SugarDecorator
public class SugarDecorator extends Decorator{ public SugarDecorator(Coffee coffee) { super(coffee); setDes("糖"); setPrice(1.0f); } }
「具体装饰者」MilkDecorator
public class MilkDecorator extends Decorator{ public MilkDecorator(Coffee coffee) { super(coffee); setDes("牛奶"); setPrice(2.0f); } }
「测试类」Client
public class Client { /** * 点一杯 加两份糖一份牛奶的摩卡咖啡 */ @Test public void test01() { Coffee order = new MochaCoffee(); System.out.println(order.getDes() + ",价格:" + order.cost()); //加两份糖 order = new SugarDecorator(new SugarDecorator(order)); System.out.println(order.getDes() + ",价格:" + order.cost()); //加一份牛奶 order = new MilkDecorator(order); System.out.println(order.getDes() + ",价格:" + order.cost()); } }
「结果」result
摩卡咖啡,价格:5.5 摩卡咖啡加糖加糖,价格:7.5 摩卡咖啡加糖加糖加牛奶,价格:9.5
在上图所示的关系中
具体使用以下:
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("G:\\a.txt"));
一、利用继承设计子类,只能在编译时静态决定,而且全部子类都会继承相同的行为;利用组合扩展对象,就能够在运行时动态的进行扩展。
二、装饰者和被装饰者对象有相同的超类型,因此在任何须要原始对象(被装饰者)的场合,均可以用装饰过的对象代替原始对象。
三、能够用一个或多个装饰者包装一个对象(被装饰者)。
四、装饰者能够在所委托的装饰者行为以前或以后加上本身的行为,以达到特定的目的。
五、被装饰者能够在任什么时候候被装饰,因此能够在运行时动态地、不限量地用你喜欢的装饰者来装饰对象。
六、装饰者会致使出现不少小对象,若是过分使用,会让程序变得复杂。
外观模式(Facade Pattern):外部与一个子系统的通讯必须经过一个统一的外观对象进行,它为子系统中的一组接口提供一个统一的高层接口,使子系统更容易被使用。
外观模式又称为门面模式,它是一种对象结构型模式。
一、Client(客户端):调用者
二、Facade(外观类):即上述所讲的高层接口
三、SubSystem(子系统):被调用者
想要使用电脑,你只须要按一下开机键(客户端),电脑的各个部件(子系统)就开始工做了,你不须要关心硬盘如何启动的,CPU怎么运转的等等,一切都交给内部程序(外观类)处理。
编写简单的程序模拟一下
「SubSystem」:电脑的几个部件 CPU、内存、硬盘
public class Cpu { //使用「单例模式--饿汉式」建立对象 private static Cpu instance = new Cpu(); private Cpu() { } public static Cpu getInstance() { return instance; } public void start() { System.out.println("CPU启动"); } public void stop() { System.out.println("CPU中止工做"); } }
public class Memory { private static Memory instance = new Memory(); private Memory() { } public static Memory getInstance() { return instance; } public void start() { System.out.println("内存启动"); } public void stop() { System.out.println("内存中止工做"); } }
public class HardDisk { private static HardDisk instance = new HardDisk(); private HardDisk() { } public static HardDisk getInstance() { return instance; } public void start() { System.out.println("硬盘启动"); } public void stop() { System.out.println("硬盘中止工做"); } }
「Facade」:电脑,统一管理开机关机中硬件的启动与中止
public class Computer { private Cpu cpu; private Memory memory; private HardDisk hardDisk; public Computer() { this.cpu = Cpu.getInstance(); this.memory = Memory.getInstance(); this.hardDisk = HardDisk.getInstance(); } /** * 开机 */ public void boot(){ cpu.start(); memory.start(); hardDisk.start(); } /** * 关机 */ public void shutdown(){ cpu.stop(); memory.stop(); hardDisk.stop(); } }
「Client」:电源键,可控制开机、关机
public class Client { Computer computer = new Computer(); @Test public void boot(){ computer.boot(); } @Test public void shutdown(){ computer.shutdown(); } }
优势:
缺点:
享元模式(Flyweight Pattern)也叫蝇量模式,运用共享技术有效地支持大量细粒度对象的复用。经常使用于系统底层开发,解决系统性能问题。例如数据库链接池,里面都是建立好的链接对象,若是有咱们须要的,直接拿来用,避免从新建立,能够解决重复对象对内存形成浪费的问题。
享元模式提出了细粒度和共享对象,这里就涉及了内部状态和外部状态的概念,便可以把对象的信息分为两个部分:内部状态和外部状态
内部状态(Intrinsic State):能够共享的相同内容
外部状态(Extrinsic State):须要外部环境来设置的不能共享的内容
举个栗子,围棋理论上有 361 个位置能够放棋子,每盘棋可能会产生两三百个棋子对象,因为内存有限,一台服务器很难支持更多玩家进行围棋对战,若是用享元模式来处理棋子,将棋子的颜色(黑与白)做为内部状态,棋子的位置(不肯定)做为外部状态,就能够将棋子对象减小到两个实例(黑棋、白棋),这样就能够很好的解决内存开销问题。
一个开发团队接了这样的项目,客户但愿作一个产品展现网站,但网站须要有多种发布形式,每一个用户能够以新闻形式发布、以博客形式发布、以微信公众号形式发布...
「抽象享元类」
public abstract class AbstractWebsite { public abstract void publish(User user); }
「非共享具体享元类」
public class User { private String name; public User(String name) { this.name = name; } public String getName() { return name; } }
「具体享元类」
public class ConcreteWebsite extends AbstractWebsite { /** * 发布类型 */ private String type = ""; public ConcreteWebsite(String type) { this.type = type; } /** * 发布 */ @Override public void publish(User user) { System.out.println("用户「"+user.getName()+"」发布的网站形式为「" + type+"」"); } }
「享元工厂类」
public class WebsiteFactory { /** * 以 HashMap 做为对象池 */ private Map<String, ConcreteWebsite> pool = new HashMap<>(); /** * 从对象池中返回指定类型的对象,没有则建立 */ public AbstractWebsite getWebsite(String type) { if (!pool.containsKey(type)) { pool.put(type, new ConcreteWebsite(type)); } return pool.get(type); } /** * 计算对象池中对象的个数 */ public int count() { return pool.size(); } }
「测试类」
public class Client { @Test public void test(){ WebsiteFactory factory = new WebsiteFactory(); AbstractWebsite website1 = factory.getWebsite("新闻"); website1.publish(new User("张三")); website1.publish(new User("李四")); AbstractWebsite website2 = factory.getWebsite("博客"); website2.publish(new User("王五")); website2.publish(new User("赵六")); AbstractWebsite website3 = factory.getWebsite("公众号"); website3.publish(new User("陈七")); website3.publish(new User("胡八")); System.out.println("对象的个数:" + factory.count()); } }
「运行结果」
用户「张三」发布的网站形式为「新闻」 用户「李四」发布的网站形式为「新闻」 用户「王五」发布的网站形式为「博客」 用户「赵六」发布的网站形式为「博客」 用户「陈七」发布的网站形式为「公众号」 用户「胡八」发布的网站形式为「公众号」 对象的个数:3
首先咱们看一段代码,运行结果是什么?
public class IntegerSource { public static void main(String[] args) { Integer v1 = 127; Integer v2 = 127; System.out.println("v1等于v2? " + (v1 == v2)); Integer v3 = 128; Integer v4 = 128; System.out.println("v3等于v4? " + (v3 == v4)); } }
v1等于v2? true v3等于v4? false
分析:查看 Integer 源码,找到 valueOf
方法,能够看到,若是 i
在某个范围内,就不会产生新的对象,直接从缓存数组中获取,点进 IntegerCache
里就会发现 low = -128
high = 127
,所以,咱们能够理解为这个数组就是「内部状态」
public static Integer valueOf(int i) { //low = -128 , high = 127 if (i >= IntegerCache.low && i <= IntegerCache.high) //IntegerCache.cache是一个常量数组:static final Integer cache[]; return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
优势:
能够极大减小内存中对象的数量,使得相同对象或类似对象在内存中只保存一份。
享元模式的外部状态相对独立,并且不会影响其内部状态,从而使得享元对象能够在不一样的环境中被共享。
缺点:
适用场景:
一个系统有大量相同或者类似的对象,因为这类对象的大量使用,形成内存的大量耗费。
对象的大部分状态均可之外部化,能够将这些外部状态传入对象中。
使用享元模式须要维护一个存储享元对象的享元池,而这须要耗费资源,所以,应当在屡次重复使用享元对象时才值得使用享元模式。
代理模式提供了对目标对象额外的访问方式,即经过代理对象访问目标对象,这样能够在不修改原目标对象的前提下,提供额外的功能操做,扩展目标对象的功能。
代理模式分为三类:
要求目标对象和代理对象实现同一个接口,调用的时候调用代理对象的方法,从而达到加强的效果
优势:
能够在不修改目标对象的前提下,加强目标对象方法的功能(全部代理模式均可以实现,所以不推荐使用此方法)
缺点:
① 冗余。目标对象和代理对象实现同一个接口,会产生过多的代理类。
② 不易维护。当接口方法增长,目标对象与代理对象都要进行修改。
场景:厂家生产了商品,可是没有足够的精力、人力去销售,这时候就须要一个代理商帮他售卖,可是代理商须要从中抽取 20% 的利润。
公共接口
public interface IProducer { void sale(float money); }
被代理对象
public class Producer implements IProducer { @Override public void sale(float money) { System.out.println("卖出产品,厂家得到" + money + "元"); } }
代理对象
public class ProxyProducer implements IProducer{ private IProducer producer; public ProxyProducer(IProducer producer) { this.producer = producer; } @Override public void sale(float money) { producer.sale(money * 0.8f); } }
测试类
public class Client { @Test public void test(){ IProducer producer = new Producer(); ProxyProducer proxyProducer = new ProxyProducer(producer); proxyProducer.sale(1000f); } }
运行结果
卖出产品,厂家得到800.0元
动态代理也称:JDK 代理、接口代理,须要目标对象实现接口,不然不能用动态代理,利用 JDK 的 API(java.lang.reflect.Proxy),动态地在内存中构建代理对象。
静态代理和动态代理的区别:
以静态代理的情景为例,咱们只须要修改代理对象的代码,代理对象不须要实现公共接口了。
public class ProxyProducer { /** * 维护一个目标对象 */ private Object target; public ProxyProducer(Object target) { this.target = target; } public Object getProxyInstance() { return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { /** * 执行被代理对象的任何接口方法都会通过这里 * @param proxy 代理对象的引用 * @param method 当前执行的方法 * @param args 当前执行方法的参数 * @return 和被代理对象具备相同的返回值 * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //代理过程当中执行一些方法 float money = (float) args[0] * 0.8f; //反射机制调用目标对象的方法 Object invoke = method.invoke(target, money); return invoke; } }); } }
Cglib 代理也叫子类代理,目标对象不须要实现任何接口,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。
Cglib 是一个强大的高性能的代码生成包,它能够在运行期间扩展 Java 类与实现 Java 接口,它普遍地被许多 AOP 的框架使用,例如 Spring AOP,用于实现方法拦截。
Cglib 包底层实经过使用字节码处理框架 ASM 来转换字节码并生成新的类。
在 AOP 编程中选择哪一种代理模式?
使用以前须要导入相关 jar 包,可去 maven 仓库下载
被代理对象,无需实现接口
public class Producer { public void sale(float money) { System.out.println("卖出产品,厂家得到" + money + "元"); } }
代理对象
public class ProxyProducer implements MethodInterceptor { /** * 维护一个目标对象 */ private Object target; public ProxyProducer(Object target) { this.target = target; } /** * 为目标对象生成代理对象 */ public Object getProxyInstance(){ //建立一个工具类 Enhancer enhancer = new Enhancer(); //设置父类 enhancer.setSuperclass(target.getClass()); //设置回调函数 enhancer.setCallback(this); //建立子类对象(代理对象) return enhancer.create(); } /** * 会拦截被代理对象的全部方法 * @param obj 加强对象 * @param method 被代理对象的方法 * @param args 被代理对象方法的参数 * @param methodProxy 代理对象 */ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("obj:" + obj.getClass()); Object returnValue = null; float money = (float) args[0] * 0.8f; if("sale".equals(method.getName())){ returnValue = method.invoke(target, money); } return returnValue; } }
测试类
public class Client { @Test public void test() { Producer producer = new Producer(); Producer proxyInstance = (Producer) new ProxyProducer(producer).getProxyInstance(); proxyInstance.sale(1000f); } }
🎉 以上全部代码和笔记都可在 个人GitHub 获取