我是靠谱客的博主 无奈月光,最近开发中收集的这篇文章主要介绍Java设计模式(Design Patterns)前言,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

前言

设计模式 (Design pattern) 是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。

设计模式的分类

在这里插入图片描述

总体来说设计模式分为三类:

  • 创建型模式:单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式。
  • 结构型模式:适配器模式、桥接模式、组合模式、装饰模式、外观模式、享元模式、代理模式。
  • 行为型模式:访问者模式、模板模式、策略模式、状态模式、观察者模式、备忘录模式、中介者模式、迭代器模式、解释器模式、命令模式、责任链模式。

设计模式的六大原则

① 开闭原则(Open Close Principle)

开闭原则就是说对扩展开放,对修改关闭。

② 里氏代换原则(Liskov Substitution Principle)

里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。

③ 依赖倒转原则(Dependence Inversion Principle)

针对接口编程,依赖于抽象而不依赖于具体

④ 接口隔离原则(Interface Segregation Principle)

使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思。

⑤ 迪米特法则(Demeter Principle)

个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

⑥ 合成复用原则(Composite Reuse Principle)

原则是尽量使用合成/聚合的方式,而不是使用继承。

单例模式 (Singleton)

单例模式分为饿汉模式和懒汉模式,单例模式的效果就是保证某个类只有唯一实例。

这样的模式有几个好处:

  1. 某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。
  2. 省去了new操作符,降低了系统内存的使用频率,减轻GC压力。

(1)饿汉单例模式

public class Hungry_Person {
private static Hungry_Person single = new Hungry_Person();
private Hungry_Person() {}
public static Hungry_Person getInstance() {
return single;
}
}

饿汉单例模式测试结果,两个对象相等

Hungry_Person obj1 = Hungry_Person.getInstance();
Hungry_Person obj2 = Hungry_Person.getInstance();
System.out.println(obj1 == obj2); // true

(2)懒汉单例模式

public class Lazy_Person {
private static Lazy_Person single;
private Lazy_Person() {}
public static Lazy_Person getInstance() {
if (single == null) {
single = new Lazy_Person();
}
return single;
}
}

懒汉单例模式测试结果,两个对象相等

Lazy_Person obj1 = Lazy_Person.getInstance();
Lazy_Person obj2 = Lazy_Person.getInstance();
System.out.println(obj1 == obj2);// true

但是懒汉单例模式在多线程下可能出现的线程安全问题,如下:

Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
Lazy_Person.getInstance();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
Lazy_Person.getInstance();
}
});
t1.start();
t2.start();

在多线程种会出现创建两个不同对象的情况,这就是线程安全问题,可以给懒汉模式在创建对象的时候加 synchronized

public class Lazy_Person {
private static Lazy_Person single;
private Lazy_Person() {}
public static Lazy_Person getInstance() {
if (single == null) {
synchronized (Lazy_Person.class) {
if (single == null) {
single = new Lazy_Person();
}
}
}
return single;
}
}

第一个判定条件,是否需要加锁,第二个判定条件是否需要对像实例化,这就解决了懒汉模式下的线程安全问题。

工厂方法模式 (Factory Method)

工厂方法模式分为三种:普通工厂模式、多个工厂方法模式、静态工厂方法模式。

(1)普通工厂模式

就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。

首先,创建两者的共同接口

public interface ISender {
void send();
}

然后分别创建具体的实现类

public class MailSender implements ISender {
@Override
public void send() {
System.out.println("mail sender");
}
}
public class SmsSender implements ISender {
@Override
public void send() {
System.out.println("sms sender");
}
}

最后是建立工厂类

public class SendFactory {
public ISender produce(String type) {
if ("mail".equals(type)) {
return new MailSender ();
} else if ("sms".equals(type)) {
return new SmsSender ();
} else {
System.out.println("请输入正确的类型!");
return null;
}
}
}

(2)多个工厂方法模式

是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。

public class SendFactory {
public ISender produceMail(){
return new MailSender();
}
public ISender produceSms(){
return new SmsSender();
}
}

(3)静态工厂方法模式

将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。

public class SendFactory {
public static ISender produceMail(){
return new MailSender();
}
public static ISender produceSms(){
return new SmsSender();
}
}

总体来说,工厂模式适合凡是出现了大量的产品需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。在以上的三种模式中,第一种如果传入的字符串有误,不能正确创建对象,第三种相对于第二种,不需要实例化工厂类,所以,大多数情况下,我们会选用第三种——静态工厂方法模式。

抽象工厂模式 (Abstract Factory)

工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?就用到抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。

首先,创建两者的共同接口

public interface IProvider {
public ISender produce();
}

具体两个工厂类的实现

public class SendMailFactory implements IProvider {
@Override
public ISender produce(){
return new MailSender();
}
}
public class SendSmsFactory implements IProvider{
@Override
public ISender produce() {
return new SmsSender();
}
}

建造者模式 (Builder)

工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,建造者模式将很多功能集成到一个类里,这个类可以创造出比较复杂的东西。

所以与工程模式的区别就是:工厂模式关注的是创建单个产品,而建造者模式则关注创建符合对象,多个部分。因此,是选择工厂模式还是建造者模式,依实际情况而定。

public class SenderBuilder {
private List<ISender> list = new ArrayList<>();
public void produceMailSender(int count) {
for (int i = 0; i < count; i++) {
list.add(new MailSender());
}
}
public void produceSmsSender(int count) {
for (int i = 0; i < count; i++) {
list.add(new SmsSender());
}
}
}

原型模式 (Prototype)

该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。

public class Prototype implements Cloneable {
public Object clone() throws CloneNotSupportedException {
Prototype proto = (Prototype) super.clone();
return proto;
}
}

一个原型类,只需要实现Cloneable接口,super.clone()调用的是Object的clone()方法,而在Object类中,clone()是native的。

了解对象深、浅复制的概念:

  • 浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。
  • 深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。
public class Prototype implements Cloneable, Serializable {
private static final long serialVersionUID = 1L;
/* 浅复制 */
public Object clone() throws CloneNotSupportedException {
Prototype proto = (Prototype) super.clone();
return proto;
}
/* 深复制 */
public Object deepClone() throws IOException, ClassNotFoundException, IOException {
/* 写入当前对象的二进制流 */
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
/* 读出二进制流产生的新对象 */
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
}

适配器模式 (Adapter)

适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。

主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。

(1)类的适配器模式

public interface Targetable {
/* 与原类中的方法相同 */
public void method1();
/* 新类的方法 */
public void method2();
}
public class Source {
public void method1() {
System.out.println("this is original method!");
}
}
public class Adapter extends Source implements Targetable {
@Override
public void method2() {
System.out.println("this is the targetable method!");
}
}
public class AdapterTest {
public static void main(String[] args) {
Targetable target = new Adapter();
target.method1();
target.method2();
}
}
输出结果是:
this is original method!
this is the targetable method!

Adapter类继承Source类,实现Targetable接口,这样Targetable接口的实现类就具有了Source类的功能。

(2)对象的适配器模式

基本思路和类的适配器模式相同,只是将Adapter类作修改,这次不继承Source类,而是持有Source类的实例,以达到解决兼容性的问题。

public class Adapter implements Targetable {
private Source source;
public Adapter(Source source){
super();
this.source = source;
}
@Override
public void method2() {
System.out.println("this is the targetable method!");
}
@Override
public void method1() {
source.method1();
}
}
public class AdapterTest {
public static void main(String[] args) {
Source source = new Source();
Targetable target = new Adapter(source);
target.method1();
target.method2();
}
}

(3)接口的适配器模式

有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的, 有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法, 而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。

public interface ISourceable {
public void method1();
public void method2();
public void method3();
}
public abstract class BaseAdapter implements ISourceable{
public void method1() {
}
public void method2() {
}
public void method3() {
}
}
public class ListAdapter extends BaseAdapter {
// 不用全部实现方法,只需要实现自己需要的就可以了
public void method3(){
System.out.println("the sourceable interface's first Sub1!");
}
}

总结一下三种适配器模式的应用场景:

  • 类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。
  • 对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Adapter类,持有原类的一个实例,在Adapter类的方法中,调用实例的方法就行。
  • 接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Adapter,实现所有方法,我们写别的类的时候,继承抽象类即可。

桥接模式 (Bridge)

桥接模式就是把事物和其具体实现分开,使他们可以各自独立的变化。

桥接的用意是:将抽象化与实现化解耦,使得二者可以独立变化,像常用的JDBC的的DriverManager一样,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不用动,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接就行了。

public abstract class Bridge {
private ISourceable source;
public void method() {
source.method();
}
public ISourceable getSource() {
return source;
}
public void setSource(ISourceable source) {
this.source = source;
}
}
class MyBridge extends Bridge {
public void method() {
if (getSource() == null) {
return;
}
getSource().method();
}
public static void main(String[] args) {
Bridge bridge = new MyBridge();
/*调用第一个对象*/
ISourceable source1 = new SourceSub1();
bridge.setSource(source1);
bridge.method();
/*调用第二个对象*/
ISourceable source2 = new SourceSub2();
bridge.setSource(source2);
bridge.method();
}
}

这样,就通过对Bridge类的调用,实现了对接口ISourceable的实现类SourceSub1和SourceSub2的调用。

组合模式 (Composite)

组合模式有时又叫 部分—整体 模式在处理类似树形结构的问题时比较方便。

public class TreeNode {
private String name;
private TreeNode parent;
private Vector<TreeNode> children = new Vector<TreeNode>();
public TreeNode(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public TreeNode getParent() {
return parent;
}
public void setParent(TreeNode parent) {
this.parent = parent;
}
//添加子节点
public void add(TreeNode node) {
children.add(node);
}
//删除子节点
public void remove(TreeNode node) {
children.remove(node);
}
//获取子节点
public Enumeration<TreeNode> getChildren() {
return children.elements();
}
@Override
public String toString() {
return "TreeNode{" + "name='" + name + ''' + ", parent=" + parent + ", children=" + children + '}';
}
}
class Tree {
TreeNode root;
public Tree(String name) {
root = new TreeNode(name);
}
@Override
public String toString() {
return "Tree{" + "root=" + root + '}';
}
public static void main(String[] args) {
Tree tree = new Tree("A");
TreeNode nodeB = new TreeNode("B");
TreeNode nodeC = new TreeNode("C");
nodeB.add(nodeC);
tree.root.add(nodeB);
System.out.println(tree.toString());
// Tree{root=TreeNode{name='A', parent=null, children=[TreeNode{name='B', parent=null, children=[TreeNode{name='C', parent=null, children=[]}]}]}}
}
}

使用场景:将多个对象组合在一起进行操作,常用于表示树形结构中,例如二叉树,数等。

装饰模式 (Decorator)

装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。

Source类是被装饰类,Decorator类是一个装饰类,可以为Source类动态的添加一些功能,如下:

public interface ISourceable {
public void method();
}
public class Source implements ISourceable {
@Override
public void method() {
System.out.println("the original method!");
}
}
public class Decorator implements ISourceable {
private ISourceable source;
public Decorator(ISourceable source){
super();
this.source = source;
}
@Override
public void method() {
System.out.println("before decorator!");
source.method();
System.out.println("after decorator!");
}
}
public class DecoratorTest {
public static void main(String[] args) {
ISourceable source = new Source();
ISourceable obj = new Decorator(source);
obj.method();
}
}

外观模式是为了解决类与类之间的依赖关系的,像spring一样,可以将类和类之间的关系配置到配置文件中,而外观模式就是将他们的关系放在一个Facade类中,降低了类类之间的耦合度,该模式中没有涉及到接口。

下面以计算机启动过程为例:

public class CPU {
public void startup(){
System.out.println("cpu startup!");
}
public void shutdown(){
System.out.println("cpu shutdown!");
}
}
public class Memory {
public void startup(){
System.out.println("memory startup!");
}
public void shutdown(){
System.out.println("memory shutdown!");
}
}
public class Disk {
public void startup(){
System.out.println("disk startup!");
}
public void shutdown(){
System.out.println("disk shutdown!");
}
}
public class Computer {
private CPU cpu;
private Memory memory;
private Disk disk;
public Computer(){
cpu = new CPU();
memory = new Memory();
disk = new Disk();
}
public void startup(){
System.out.println("start the computer!");
cpu.startup();
memory.startup();
disk.startup();
System.out.println("start computer finished!");
}
public void shutdown(){
System.out.println("begin to close the computer!");
cpu.shutdown();
memory.shutdown();
disk.shutdown();
System.out.println("computer closed!");
}
}
public class User {
public static void main(String[] args) {
Computer computer = new Computer();
computer.startup();
computer.shutdown();
}
}

如果我们没有Computer类,那么CPU、Memory、Disk他们之间将会相互持有实例,产生关系,这样会造成严重的依赖,修改一个类,可能会带来其他类的修改,这不是我们想要看到的,有了Computer类,他们之间的关系被放在了Computer类里,这样就起到了解耦的作用,这就是外观模式!

享元模式 (Flyweight)

享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。

class ConnectionPool {
private Vector<Connection> pool;
/*公有属性*/
private String url = "jdbc:mysql://localhost:3306/test";
private String username = "root";
private String password = "root";
private String driverClassName = "com.mysql.jdbc.Driver";
private int poolSize = 100;
Connection conn;
/*构造方法,做一些初始化工作*/
private ConnectionPool() {
pool = new Vector<>(poolSize);
for (int i = 0; i < poolSize; i++) {
try {
Class.forName(driverClassName);
conn = DriverManager.getConnection(url, username, password);
pool.add(conn);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/* 返回连接到连接池 */
public synchronized void release() {
pool.add(conn);
}
/* 返回连接池中的一个数据库连接 */
public synchronized Connection getConnection() {
if (pool.size() > 0) {
Connection conn = pool.get(0);
pool.remove(conn);
return conn;
} else {
return null;
}
}
}

通过连接池的管理,实现了数据库连接的共享,不需要每一次都重新创建连接,节省了数据库重新创建的开销,提升了系统的性能!

代理模式 (Proxy)

代理按照代理的创建时期,可以分为两种:

  • 静态代理:由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。
  • 动态代理:在程序运行时运用反射机制动态创建而成。

(1)静态代理

  1. 新建了一个静态的代理类,把原有的业务和新的业务耦合在一起。
  2. 静态代理类要实现业务的接口,因为要保证业务的完整性。
  3. StaticProxy是静态代理类,此类的对象是代理对象。
  4. 静态代理类在编译期间就已经确认原有的业务和新的业务的耦合模型。
  5. 优点是执行效率高,缺点是开发效率低(有多少个业务类就有多少个静态代理类)。
import other.TransactionManager;
import service.UserService;
// 静态代理类组合新老功能
public class StaticProxy implements UserService {
// 业务原有的功能
private UserService userService;
// 新的业务功能
private TransactionManager tm;
public void setUserService(UserService userService) {
this.userService = userService;
}
public void setTm(TransactionManager tm) {
this.tm = tm;
}
@Override
public Boolean addUser(User user) {
try {
tm.begin();
// 调用老的业务功能
userService.addUser(user);
tm.commit();
} catch (Exception e) {
tm.rollback();
e.printStackTrace();
}
return null;
}
}

(2)动态代理

动态代理简单来说就是在程序执行过程中,创建代理对象,通过代理对象执行方法,给目标类的方法增加额外的功能,也叫做功能增强。分为 jdk动态代理 和 cglib动态代理。

动态代理的优缺点:

  • 优点:代理使客户端不需要知道实现类是什么,怎么做的,而客户端只需知道代理即可(解耦)。
  • 缺点:代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了

动态代理类只能代理接口(不支持抽象类),代理类都需要实现InvocationHandler类,实现invoke方法。该invoke方法就是调用被代理接口的所有方法时需要调用的,该invoke方法返回的值是被代理接口的一个实现类

① jdk动态代理

jdk 动态代理要求目标类必须有接口,jdk 代理类是目标类的兄弟类。

  1. 首先需要有一个目标类,在目标类的基础上通过动态代理实现功能增强。
  2. 创建InvocationHandler接口的实现类,在这个类中实现invoke方法,在invoke方法中实现给目标类的方法增强功能。
  3. 通过JDK中的Proxy创建代理对象,通过代理对象调用目标类中的方法,实现功能增强。

1.创建一个接口

public interface UserDao {
public Integer addUser(User user);
}

2.创建接口的实现类也就是目标类

public class UserDaoImpl implements UserDao {
@Override
public Integer addUser(User user) {
System.out.println("UserDaoImpl.addUser()");
return 1;
}
}

3.创建需要耦合的新功能类

public class TransactionManager {
public void begin() {
System.out.println("开始业务");
}
public void commit() {
System.out.println("提交业务");
}
public void rollback() {
System.out.println("回滚业务");
}
}

4.创建InvocationHandler接口的实现类,在此方法中耦合老的业务和新的业务功能

public class TransactionHandler implements InvocationHandler {
//目标对象
private Object targetObject;
//有参构造
public TransactionHandler(Object targetObject) {
this.targetObject = targetObject;
}
/**
*
@param proxy
代理对象
*
@param method
目标方法 老的业务方法
*
@param args
老的业务方法的参数
*
@return
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnValue = null;
// 实例化新的业务功能对象
TransactionManager tm = new TransactionManager();
try {
//执行新的业务功能
tm.begin();
//执行老的业务功能,用反射的方式
returnValue = method.invoke(targetObject, args);
//执行新的业务功能
tm.commit();
} catch (Exception e) {
//执行新的业务功能
tm.rollback();
e.printStackTrace();
}
return returnValue;
}
}

5.通过Proxy创建代理对象调用目标方法

@Test
public void testMethod3() {
// 实例化老的业务对象(目标类对象)
UserDao targetObject = new UserDaoImpl();
// 创建InvocationHandler对象
InvocationHandler handler = new TransactionHandler(targetObject);
/**
* jdk类库中有一个类Proxy,其中有一个静态方法newProxyInstance,此方法反回一个代理对象
* 参数一:类加载器,为了定位类路径
* 参数二:目标对象的所有接口数组
* 参数三:是一个类的对象,此类必须实现自InvocationHandler接口,在InvocationHandler接口的接口方法中耦合老业务和新业务功能
*/
// 创建Proxy代理对象
Object proxyObject = Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(), handler);
// 代理对象强制转换成接口类型
UserDao UserDao = (UserDao)proxyObject;
// 用代理对象调用目标方法,用代理对象调用目标方法事实上执行的是InvocationHandler接口方法
UserDao.addUser(new User());
}

6.结果

开始业务
UserDaoImpl.addUser()
提交业务

② cglib动态代理

cglib是第三方的工具 jar 包提供的用来生成cglib代理对象,cglib要求目标类有无接口皆可,但必须要求目标类不能是 final 类,因为cglib的代理类是目标类的子类。

  1. 首先需要有一个目标类,在目标类的基础上通过动态代理实现功能增强。
  2. 创建MethodInterceptor接口的实现类,在这个类中实现intercept方法,在intercept方法中实现给目标类的方法增强功能。
  3. 通过Enhancer的create创建代理对象,通过代理对象调用目标类中的方法,实现功能增强。

1.创建一个接口

public interface UserService {
public Boolean addUser(User user);
}

2.创建接口的实现类也就是目标类

public class UserServiceImpl implements UserService {
@Override
public Boolean addUser(User user) {
System.out.println("UserServiceImpl.addUser()");
return true;
}
}

3.创建需要耦合的新功能类

public class TransactionManager {
public void begin() {
System.out.println("开始业务");
}
public void commit() {
System.out.println("提交业务");
}
public void rollback() {
System.out.println("回滚业务");
}
}

4.创建MethodInterceptor接口的实现类,在此方法中耦合老的业务和新的业务功能

public class TransactionHandler implements MethodInterceptor {
//目标对象
private Object targetObject;
//有参构造
public TransactionHandler(Object targetObject) {
this.targetObject = targetObject;
}
/**
* @param proxy
cglib的代理对象
* @param method
代理对象调用的目标方法,Method是java原生反射的类型
* @param args
目标方法的参数
* @param methodProxy	代理对象调用的目标方法,MethodProxy是cglib封装过
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodproxy) throws Throwable {
Object returnValue = null;
TransactionManager tm = new TransactionManager();
try {
// 执行新的业务功能
tm.begin();
// 用原生的反射调用老的业务方法
// returnValue = method.invoke(targetObject, args);
// 用cglib封装类来调用老的业务方法,其底层还是原生的反射api
returnValue = methodproxy.invoke(targetObject, args);
// 执行新的业务功能
tm.commit();
} catch (Exception e) {
// 执行新的业务功能
tm.rollback();
e.printStackTrace();
}
return returnValue;
}

5.通过CGLIBProxy创建代理对象调用目标方法

@Test
public void testMethod3() {
// 实例化老的业务对象(目标类对象)
UserService targetObject = new UserServiceImpl();
// 创建MethodInterceptor对象
MethodInterceptor handler = new TransactionInterceptor(targetObject);
// Enhancer
Enhancer enhancer = new Enhancer();
// 设置代理类的父类是谁,目标类是代理类的父类
enhancer.setSuperclass(targetObject.getClass());
// 必须重写MethodInterceptor接口中的intercept方法,耦合老和新的业务
enhancer.setCallback(handler);
// 创建CGLIBProxy代理对象
Object proxyObject = enhancer.create();
// 代理对象强制转换成接口类型
UserService UserService = (UserService)proxyObject;
// 用代理对象调用目标方法,用代理对象调用目标方法事实上执行的是InvocationHandler接口方法
UserService.addUser(new User());
}

6.结果

开始业务
UserServiceImpl.addUser()
提交业务

jdk动态代理 和 cglib动态代理的区别

  1. jdk动态代理目标业务类必须有接口,cglib动态代理业务类有无接口皆可。
  2. jdk动态代理必须实现InvocationHandler接口,cglib动态代理必须实现MethodInterceptor接口。
  3. jdk动态代理代理类和目标业务类是兄弟关系,因为隶属于同一个接口,cglib动态代理代理类和目标业务类是父子关系,业务类是父类,业务类不能是final类,代理类是子类。
  4. jdk动态代理创建代理类快,执行代理类慢,cglib动态代理创建代理类慢,执行代理类快。

最后

以上就是无奈月光为你收集整理的Java设计模式(Design Patterns)前言的全部内容,希望文章能够帮你解决Java设计模式(Design Patterns)前言所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(37)

评论列表共有 0 条评论

立即
投稿
返回
顶部