设计模式
UML
http://www.uml.org.cn/oobject/201211231.asp
Iterator模式
迭代器模式——一个一个遍历
在for循环中,将i
的作用抽象化、通用化后形成的模式,在设计模式中称为Iterator
模式。
Iterator
模式用于在数据集合中按照顺序遍历集合。
登场角色
- Iterator(迭代器):该角色负责按顺序逐个遍历元素的接口(API)。
- ConcreteIterator(具体的迭代器):该角色负责实现Iterator角色所定义的接口(API)。
- Aggregate(集合):该角色负责定义创建Iterator角色的接口(API)。
- ConcreteAggregate(具体的集合):该角色负责实现Aggregate角色所定义的接口(API)。
代码
public interface Aggregate<T> {
Iterator<T> iterator();
}
public class BookShelf implements Aggregate<Book> {
private List<Book> books = new ArrayList<>();
private Integer last = 0;
public Book getBookAt(Integer index) {
return books.get(index);
}
public void appendBook(Book book) {
books.add(book);
last++;
}
public Integer getLength(){
return last;
}
@Override
public Iterator<Book> iterator() {
return new BookShelfIterator(this);
}
}
public class BookShelfIterator implements Iterator<Book> {
private BookShelf bookShelf;
private Integer index;
public BookShelfIterator(BookShelf bookShelf) {
this.bookShelf = bookShelf;
index = 0;
}
@Override
public boolean hasNext() {
Integer last = bookShelf.getLength();
return index < last;
}
@Override
public Book next() {
return bookShelf.getBookAt(index++);
}
}
@Data
public class Book {
private String name;
}
使用原因
使用Iterator的hasNext()和next()方法,没有调用实体类的方法,循环遍历不依赖于类的实现。
相关的设计模式
Visitor
模式:Iterator
模式是从集合中一个一个取出元素进行遍历,但是并没有在Iterator
接口中声明对取出的元素进行何种处理。Visitor
模式则是在遍历元素集合的过程中,对元素进行相同的处理。在遍历集合的过程中对元素进行固定的处理是常用的需求。Visitor
模式正是为了应对这种需求而出现的。在访问元素集合的过程中对元素进行相同的处理,这种模式就是Visitor
模式。Composite
模式:Composite
模式是具有递归结构的模式,在其中使用Iterator
模式比较困难。Factory Method
模式:在iterator
方法中生成Iterator
的实例时可能会使用Factory Method
模式。
Adapter模式
适配器模式——加个“适配器”以便于复用
在程序中,经常会存在现有的程序无法直接使用,需要做适当的变换之后才能使用的情况。这种用于填补“现有的程序”和“所需的程序”之间差异的设计模式就是Adapter模式。
Adapter
模式也被称为Wrapper
模式。Wrapper
有“包装器”的意思,就像用精美的包装纸将普通商品包装成礼物那样,替我们把某样东西包起来,使其能够用于其他用途的东西就被称为“包装器”或是“适配器”。
Adapter
模式有以下两种:
- 类适配器模式(使用继承的适配器)
- 对象适配器模式(使用委托的适配器)
登场角色
Target
(对象):该角色负责定义所需的方法。Client
(请求者):该角色负责使用Target
角色所定义的方法进行具体处理。Adaptee
(被适配):持有既定方法的角色。Adapter
(适配):使用Adaptee
角色的方法来满足Target
角色的需求,这是Adapter
模式的目的,也是Adapter
角色的作用。
使用场景
Adapter模式对现有的类进行适配,生成新的类。通过该模式可以很方便地创建需要的方法群。当出现bug时,由于我们很明确地知道bug不在现有类(Adaptee
角色中),所以只需调查Adapter
即可,代码的排查就会变得非常简单。
使用Adapter
模式可以在完全不改变现有代码的前提下使现有代码适配于新的接口(API)。此外,在Adapter模式中,并非一定需要现成的代码。只要知道现有类的功能,就可以编写出新的类。
相关的设计模式
Bridge
模式:Adapter
模式用于连接接口(API)不同的类,而Bridge
模式则用于连接类的功能层次结构与实现层次结构。Decorator
模式:Adapter
模式用于填补不同接口(API)之间的缝隙,而Decorator
模式则是在不改变接口(API)的前提下增加功能。
类适配器模式
代码
public interface Print {
void printWeak();
void printStrong();
}
public class Banner {
private String str;
public Banner(String str) {
this.str = str;
}
/**
* 用括号包围
*/
public void showWithParen() {
System.out.println("(" + str + ")");
}
/**
* 用星号包围
*/
public void showWithAster() {
System.out.println("*" + str + "*");
}
}
public class PrintBanner extends Banner implements Print {
public PrintBanner(String str) {
super(str);
}
@Override
public void printWeak() {
showWithParen();
}
@Override
public void printStrong() {
showWithAster();
}
}
对象适配器模式
代码
public abstract class Print {
public abstract void printWeak();
public abstract void printStrong();
}
public class Banner {
private String str;
public Banner(String str) {
this.str = str;
}
/**
* 用括号包围
*/
public void showWithParen() {
System.out.println("(" + str + ")");
}
/**
* 用星号包围
*/
public void showWithAster() {
System.out.println("*" + str + "*");
}
}
public class PrintBanner extends Print {
private Banner banner;
public PrintBanner(String str) {
this.banner = new Banner(str);
}
@Override
public void printWeak() {
banner.showWithParen();
}
@Override
public void printStrong() {
banner.showWithAster();
}
}
Template Method模式
模板方法模式——将具体处理交给子类
带有模板功能的模式,组成模板的方法被定义在父类中。由于这些方法是抽象方法,所以只查看父类的代码是无法知道这些方法最终会进行何种具体处理的,唯一能知道的就是父类是如何调用这些方法的。
实现上述这些抽象方法的是子类。在子类中实现了抽象方法也就决定了具体的处理。也就是说,只要在不同的子类中实现不同的具体处理,当父类的模板方法被调用时程序的行为也会不同。但是,不论子类中的具体实现如何,处理的流程都会按照父类中所定义的那样进行。
向这样在父类中定义处理流程的框架,在子类中实现具体处理的模式就称为Template Method
模式。
在父类中定义处理的流程,在子类中实现具体处理内容。
登场角色
AbtractClass
(抽象类):AbstractClass
角色不仅负责实现模板方法,还负责声明在模板方法中所使用到的抽象方法。这些抽象方法由子类ConcreteClass
角色负责实现。ConcreteClass
(具体类):该角色负责具体实现AbstractClass
角色中定义的抽象方法。
注意
由于在父类的模板方法中编写了算法,因此无需在每个子类中再编写算法。
在Template Method
模式中,父类和子类是紧密联系、共同工作的。因此,在子类中实现父类中声明的抽象方法时,必须要理解这些抽象方法被调用的时机。在看不到父类源码的情况下,想要编写出子类是非常困难的。
使用父类类型的变量保存子类实例的优点是,即使没有用instanceof等指定子类的种类,程序也能正常工作。
无论在父类类型的变量中保存哪个子类的实例,程序都可以正常工作,这种原则称为里氏替换原则(LSP)。LSP并非仅限于Template Method模式。它是通用的继承原则。
相关的设计模式
Factory Method
模式:是将Template Method
模式用于生成实例的一个典型例子。Strategy
模式:在Template Method
模式中,可以使用继承改变程序的行为。这是因为Template Method
模式在父类中定义程序行为的框架,在子类中决定具体的处理。于此相对的是Strategy
模式,它可以使用委托改变程序的行为。与Template Method
模式中改变部分程序行为不同的是,Strategy
模式用于替换整个算法。
代码
public abstract class AbstractDisplay {
public abstract void open();
public abstract void print();
public abstract void close();
public final void display() {
open();
for (int i = 0; i < 5; i++) {
print();
}
close();
}
}
public class CharDisplay extends AbstractDisplay {
private char ch;
public CharDisplay(char ch) {
this.ch = ch;
}
@Override
public void open() {
System.out.print("<<");
}
@Override
public void print() {
System.out.print(ch);
}
@Override
public void close() {
System.out.println(">>");
}
}
public class StringDisplay extends AbstractDisplay {
private String str;
private Integer width;
public StringDisplay(String str) {
this.str = str;
width = str.getBytes().length;
}
@Override
public void open() {
printLine();
}
@Override
public void print() {
System.out.println("|" + str + "|");
}
@Override
public void close() {
printLine();
}
private void printLine() {
System.out.print("+");
for (int i = 0; i < width; i++) {
System.out.print("-");
}
System.out.println("+");
}
}
Factory Method模式
工厂方法模式——将实例的生成交给子类
在Template Method
模式中,我们在父类中规定处理的流程,在子类中实现具体的处理,如果将该模式用于生成实例,就是Factory Method
模式。
用Template Method
模式来构建生成实例的工厂,这就是Factory Method
模式。
在Factory Method
模式中,父类决定实例的生成方式,但并不决定所要生成的具体的类,具体的处理全部交给子类负责。这样就可以将生成实例的框架(framework)和实际负责生成实例的类解耦。
登场角色
product
(产品):属于框架方,是一个抽象类。定义了在Factory Method
模式中生成的那些实例所持有的接口(API),但具体的处理则由子类ConcreteProduct
角色决定。Creator
(创建者):属于框架方,负责生成Product
角色的抽象类,但具体的处理则由子类ConcreteCreator
角色决定。Creator
对实际负责生成实例的ConcreteCreator
一无所知,唯一知道的是,只要调用Product
角色和生成实例的方法,就可以生成Product
的实例。不用new
关键字来生成实例,而是调用生成实例的专用方法来生成实例,这样就可以防止父类与其他具体类耦合。ConcreteProduct
(具体的产品):属于具体加工方,决定了具体的产品。ConcreteCreator
(具体的创建者):属于具体加工方,负责生成具体的产品。
生成实例——三种实现
- 指定其为抽象方法:一旦将
createProduct
指定为抽象方法后,子类就必须实现该方法。如果子类不实现该方法,编译器将会报告编译错误。 - 为其实现默认处理:这时使用new关键字创建出实例,因此不能将Product类定义为抽象类。
- 在其中抛出异常:
createProduct
方法的默认处理为抛出异常,这样一来,如果未在子类中实现该方法,程序就会运行时报错,不过需要另外编写FactoryMethodRuntimeException
异常类。
代码
framework包
public abstract class Product {
public abstract void use();
}
public abstract class Factory {
public final Product create(String owner) {
Product product = createProduct(owner);
registerProduct(product);
return product;
}
protected abstract Product createProduct(String owner);
protected abstract void registerProduct(Product product);
}
idcard包
public class IdCard extends Product {
@Getter
private String owner;
IdCard(String owner) {
this.owner = owner;
}
@Override
public void use() {
System.out.println("owner:" + owner);
}
}
public class IdCardFactory extends Factory {
@Getter
private List<String> owners = new ArrayList<>();
@Override
protected Product createProduct(String owner) {
return new IdCard(owner);
}
@Override
protected void registerProduct(Product product) {
owners.add(((IdCard) product).getOwner());
}
}
相关的设计模式
Template Method
模式:Factory Method
模式是Template Method
的典型应用。Singleton
模式:在多数情况下我们可以将Singleton
模式用于扮演Creator
角色(或是ConcreteCreator
角色)的类。这是因为在程序中没有必要存在多个Creator
角色(或是ConcreteCreator
角色)的实例。Composite
模式:有时可以将Composite
模式用于Product
角色(或是ConcreteProduct
角色)。Iterator
模式:在Iterator
模式中使用iterator
方法生成Iterator
的实例时会使用Factory Method
模式。
Singleton模式
单例模式——只有一个实例
确保只生成一个实例的模式被称为Singleton
模式。Singleton
是指只含有一个元素的集合。
登场角色
饿汉式
/**
* 饿汉式
*/
public class HungrySingleton {
private static HungrySingleton instance = new HungrySingleton();
private HungrySingleton() {
System.out.println("生成了一个实例");
}
public static HungrySingleton getInstance() {
return instance;
}
}
懒汉式
public class LazySingleton {
private static LazySingleton instance = null;
private static final ReentrantLock lock = new ReentrantLock();
private LazySingleton() {
System.out.println("生成了一个实例");
}
public static LazySingleton getInstance() {
if (instance == null) {
lock.lock();
try {
if (instance == null) {
instance = new LazySingleton();
}
} finally {
lock.unlock();
}
}
return instance;
}
}
相关的设计模式
AbstractFactory
模式Bulder
模式Facade
模式Prototype
模式
Prototype模式
原型模式——通过复制生成实例
在以下情况下,我们就不能根据类来生成实例,而要根据现有的实例来生成新的实例:
- 对象种类繁多,无法将它们整合到一个类中
- 难以根据类生成实例
- 想解耦框架与生成的实例时
代码
/**
* 复制功能的接口
*/
public interface Product extends Cloneable {
void use(String s);
Product createClone();
}
/**
* 使用Product接口来复制实例
*/
public class Manager {
private Map<String, Product> showCase = new HashMap<>();
public void register(String name, Product proto) {
showCase.put(name, proto);
}
public Product create(String name) {
Product product = showCase.get(name);
return product.createClone();
}
}
public class MessageBox implements Product {
private char decoChar;
public MessageBox(char decoChar) {
this.decoChar = decoChar;
}
@Override
public void use(String s) {
int length = s.getBytes().length;
for (int i = 0; i < length + 4; i++) {
System.out.print(decoChar);
}
System.out.println();
System.out.println(decoChar + " " + s + " " + decoChar);
for (int i = 0; i < length + 4; i++) {
System.out.print(decoChar);
}
System.out.println();
}
@Override
public Product createClone() {
Product clone = null;
try {
clone = (Product) clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
public class UnderlinePen implements Product {
private char ulChar;
public UnderlinePen(char ulChar) {
this.ulChar = ulChar;
}
@Override
public void use(String s) {
int length = s.getBytes().length;
System.out.println("\"" + s + "\"");
for (int i = 0; i < length; i++) {
System.out.print(ulChar);
}
System.out.println();
}
@Override
public Product createClone() {
Product clone = null;
try {
clone = (Product) clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
登场角色
Prototype
(原型):负责定义用于复制现有实例来生成新实例的方法。ConcretePrototype
(具体的原型):负责实现复制现有实例并生成新实例的方法。Client
(使用者):负责使用复制实例的方法生成新的实例。
相关的设计模式
Flyweight
模式:使用Prototype
模式可以生成一个与当前实例的状态完全相同的实例。而使用Flyweight
模式可以在不同的地方使用同一个实例。Memento
模式:使用Prototype
模式可以生成一个与当前实例的状态完全相同的实例。而使用Memento
模式可以保存当前实例的状态,以实现快照和撤销功能。Composite
模式以及Decorator
模式:经常使用Composite'
模式和Decorator
模式时,需要能够动态地创建复杂结构的实例。这时可以使用Prototype
模式。Command
模式:想要复制Command
模式中出现的命令时,可以使用Prototype
模式。
注意
clone方法进行的是浅复制
clone
方法所进行的复制只是将被复制实例的字段值直接复制到新的实例中。换言之,它并没有考虑字段中所保存的实例的内容。例如,当字段中保存的是数组时,如果使用clone
方法进行复制,则只会复制该数组的引用,并不会一一复制数组中的元素。
像上面这样的字段对字段的复制(field-to-field-copy)被称为浅复制(shallow copy)。clone
方法所进行的复制就是浅复制。
当使用clone
方法进行浅复制无法满足需求时,类的设计者可以实现重写clone方法,实现自己需要的复制功能(重写clone方法时,别忘了使用super.clone()
来调用父类的clone
方法)。
需要注意的是,clone
方法只会进行复制,并不会调用被复制实例的构造函数。此外,对于在生成实例时需要进行特殊的初始化处理的类,需要自己去实现clone方法,在其内部进行这些初始化处理。
Builder模式
Abstract Factory模式
抽象工厂模式——将关键零件组装成产品
将抽象零件组合成为抽象产品的工厂。
代码
factory包
/**
* 抽象的零件:Item
*/
public abstract class Item {
/**
* 标题
*/
protected String caption;
public Item(String caption) {
this.caption = caption;
}
public abstract String makeHTML();
}
/**
* 抽象的零件:Link
*/
public abstract class Link extends Item {
protected String url;
public Link(String caption, String url) {
super(caption);
this.url = url;
}
}
/**
* 抽象的零件:Tray
* 表示一个含有多个Link和Tray类的容器
*/
public abstract class Tray extends Item {
protected ArrayList<Item> tray = new ArrayList();
public Tray(String caption) {
super(caption);
}
public void add(Item item) {
tray.add(item);
}
}
/**
* 抽象的产品:Page
* 抽象地表示HTML页面的类
*/
public abstract class Page {
protected String title;
protected String author;
protected ArrayList<Item> content = new ArrayList();
public Page(String title, String author) {
this.title = title;
this.author = author;
}
public void add(Item item) {
content.add(item);
}
public void output() {
try {
String filename = title + ".html";
Writer writer = new FileWriter(filename);
writer.write(makeHTML());
writer.close();
System.out.println(filename + "编写完成!");
} catch (IOException e) {
e.printStackTrace();
}
}
public abstract String makeHTML();
}
/**
* 抽象的工厂:Factory
*/
public abstract class Factory {
public static Factory getFactory(String classname) {
Factory factory = null;
try {
factory = (Factory) Class.forName(classname).newInstance();
} catch (ClassNotFoundException e) {
System.out.println("没有找到" + classname + "类");
} catch (Exception e) {
e.printStackTrace();
}
return factory;
}
public abstract Link createLink(String caption, String url);
public abstract Tray createTray(String caption);
public abstract Page createPage(String title, String author);
}
listfactory
/**
* 具体的零件
*/
public class ListLink extends Link {
public ListLink(String caption, String url) {
super(caption, url);
}
@Override
public String makeHTML() {
return "<li><a href=\"" + url + "\">" + caption + "</a></li>";
}
}
/**
* 具体的零件
*/
public class ListTray extends Tray {
public ListTray(String caption) {
super(caption);
}
@Override
public String makeHTML() {
StringBuffer buffer = new StringBuffer();
buffer.append("<li>");
buffer.append(caption);
buffer.append("<ul>");
Iterator<Item> iterator = tray.iterator();
while (iterator.hasNext()) {
Item item = iterator.next();
buffer.append(item.makeHTML());
}
buffer.append("</ul>");
buffer.append("</li>");
return buffer.toString();
}
}
/**
* 具体的产品
*/
public class ListPage extends Page {
public ListPage(String title, String author) {
super(title, author);
}
@Override
public String makeHTML() {
StringBuffer buffer = new StringBuffer();
buffer.append("<html><head><title>" + title + "</title></head>");
buffer.append("<body>");
buffer.append("<h1>" + title + "</h1>");
buffer.append("<ul>");
Iterator<Item> iterator = content.iterator();
while (iterator.hasNext()) {
Item item = iterator.next();
buffer.append(item.makeHTML());
}
buffer.append("</ul>");
buffer.append("<hr><address>" + author + "</address>");
buffer.append("</body>");
buffer.append("</html>");
return buffer.toString();
}
}
/**
* 具体的工厂
*/
public class ListFactory extends Factory {
@Override
public Link createLink(String caption, String url) {
return new ListLink(caption, url);
}
@Override
public Tray createTray(String caption) {
return new ListTray(caption);
}
@Override
public Page createPage(String title, String author) {
return new ListPage(title, author);
}
}
登场角色
AbstractProduct
(抽象产品):负责定义AbstractFactory
角色所生成的抽象零件和产品的接口。AbstractFactory
(抽象工厂):负责定义用于生成抽象产品的接口(API)。Client
(委托者):仅会调用AbstractFactory
角色和AbstractProduct
角色的接口(API)来进行工作,对于具体的零件、产品和工厂一无所知。ConcreteProduct
(具体产品):负责实现AbstractProduct
角色的接口(API)。ConcreteFactory
(具体工厂):负责实现AbstractFactory
角色的接口(API)。
相关的设计模式
Builer
模式:Abstract Factory
模式通过调用抽象产品的接口(API)来组装抽象产品,生成具有复杂结构的实例。Builder
模式则是分阶段地制作复杂实例。Factory Method
模式:有时Abstract Factory
模式中零件和产品的生成会使用到Factory Method
模式。Composite
模式:有时Abstract Factory
模式在制作产品时会使用Composite
模式。Singleton
模式:有时Abstract Factory
模式中的具体工厂会使用Singleton
模式。
Bridge模式
Strategy模式
Composite模式
Decorator模式
Visitor模式
Chain of Responsibility模式
Facade模式
Mediator模式
Observer模式
Memento模式
State模式
Flyweight模式
Proxy模式
代理模式——只在必要时生成实例
代码
public interface Printable {
void setPrinterName(String name);// 设置名字
String getPrinterName();// 获取名字
void print(String str);// 显示文字(打印输出)
}
public class Printer implements Printable {
private String name;
public Printer() {
heavyJob("正在生成Printer实例");
}
public Printer(String name) {
this.name = name;
heavyJob("正在生成Printer实例(" + name + ")");
}
@Override
public void setPrinterName(String name) {
this.name = name;
}
@Override
public String getPrinterName() {
return name;
}
@Override
public void print(String str) {
System.out.println("===" + name + "===");
System.out.println(str);
}
private void heavyJob(String msg) {
System.out.print(msg);
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print(".");
}
System.out.println("结束");
}
}
public class PrinterProxy implements Printable {
private String name;
private Printer real;
public PrinterProxy() {
}
public PrinterProxy(String name) {
this.name = name;
}
@Override
public synchronized void setPrinterName(String name) {
if (real != null) {
real.setPrinterName(name);
}
this.name = name;
}
@Override
public String getPrinterName() {
return name;
}
@Override
public void print(String str) {
realize();
real.print(str);
}
private synchronized void realize() {
if (real == null)
real = new Printer(name);
}
}
登场角色
Subject
(主体):定义了使Proxy
和RealSubject
角色之间具有一致性的接口。由于存在Subject
角色,所以Client
角色不必在意它所使用的究竟是proxy
还是RealSubject
。Proxy
(代理人):Proxy
角色会尽量处理来自Client
角色的请求。只有当自己不能处理时,它才会将工作交给RealSubject
角色。Proxy
角色只有在必要时才会生成RealSubject
角色。Proxy
角色实现了在Subject
角色中定义的接口(API)。RealSubject
(实际的主体):“本人”RealSubject
角色会在“代理人”Proxy
角色无法胜任工作时出场。它与Proxy
角色一样,也实现了在Subject
角色中定义的接口(API)。Client
(请求者):使用Proxy
模式的角色。
各种Proxy模式
Virtual Proxy
(虚拟代理):只有当真正需要实例时,才生成和初始化实例。Remote Proxy
(远程代理):Remote Proxy
可以让我们完全不必在意RealSubject
角色是否在远程网络上,可以如同它在自己身边一样(透明性地)调用它的方法。Java的RMI(RemoteMethodInvocation:远程方法调用)就相当于Remote Proxy
。Access Proxy
:Access Proxy
用于在调用RealSubject
角色的功能时设置访问限制。例如,这种代理可以只允许指定的用户调用方法,而当其他用户调用方法时则报错。
相关的设计模式
Adapter
模式:Adapter
模式适配了两种具有不同接口(API)的对象,以使它们可以一同工作。而在Proxy
模式中,Proxy
角色与RealSubject
角色的接口(API)是相同的(透明性)。Decorator
模式:Decorator
模式与Proxy
模式在实现上很相似,不过它们的使用目的不同。Decorator
模式的目的在于增加新的功能。而在Proxy
模式中,与增加功能相比,它更注重通过设置代理人的方式来减轻本人的工作负担。
为什么要使用代理模式
- 中介隔离作用:在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。
- 开闭原则,增加功能:代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要在修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是通过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要修改已经封装好的委托类。
有哪几种代理模式
如果按照代理创建的时期来进行分类的话,可以分为两种:静态代理、动态代理。
- 静态代理是由程序员创建或特定工具自动生成源代码,再对其编译。在程序员运行之前,代理类.class文件就已经被创建了。
- 动态代理是在程序运行时通过反射机制动态创建的。
JDK动态代理
在动态代理中我们不再需要再手动创建代理类,只需要编写一个动态处理器就可以了。真正的代理对象由JDK在运行时为我们动态创建。
第一步:创建服务类接口
第二步:实现服务接口
第三步:编写动态处理器
public class DynamicProxyHandler implements InvocationHandler {
private Object object;
public DynamicProxyHandler(final Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(object, args);
return result;
}
}
第四步:编写测试类
public static void main(String[] args) {
Printer printer = new Printer();
Printable proxy = (Printable) Proxy.newProxyInstance(Printable.class.getClassLoader(),
new Class[]{Printable.class},
new DynamicProxyHandler(printer));
proxy.setPrinterName("打印机");
proxy.print("Hello World");
}
CGLib动态代理(CGLib Proxy)
JDK
实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,就需要CGLib
了。CGLib
采用了底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final
修饰的类进行代理。JDK
动态代理与CGLib
动态代理均是实现Spring AOP
的基础。
CGLib子类代理实现方法:
- 引入
cglib
的jar文件,asm的jar文件 - 代理的类不能为
final
- 目标业务对象的方法如果为
final
/static
,那么就不会被拦截,即不会执行目标对象额外的业务方法。