设计模式之代理模式

写在前面

1:介绍

1.1:什么时候使用代理模式

当我们有对象因为安全性,不能直接对外暴露,或者是需要对对象的操作本身记录日志等信息时就可以考虑使用代理模式,

1.2:UML类图

享元设计模式,包含如下元素:

1:被代理类
    需要被代理的类
2:代理类
    代理类
3:客户端类
    使用代理类执行操作的类

UML图如下:

另外,代理又分为静态代理和动态代理,静态代理就是在编译器已经确定的代理方式,即是硬编码到程序中的,这种方式不灵活,动态代理是在运行期动态确定的代理方式,灵活度高。动态代理主要有jdk动态代理(需要有接口),asm,cglib(基于asm,简化操作)。来看下。

2:实例

源码

2.1:场景

数据库操作场景,需要在service方法调用前开启事务,方法调用后提交事务。

2.2:静态代理

  • 定义PersonService接口
java 复制代码
public interface PersonService {
	public void savePerson();
}
  • 定义PersonServiceImpl
java 复制代码
public class PersonServiceImpl implements PersonService {
	@Override
	public void savePerson() {
		System.out.println("保存人了");
	}
}
  • 定义事务类
java 复制代码
public class Transaction {
	public void beginTransaction(){
		System.out.println("开启事务 ");
	}
	public void commit(){
		System.out.println("提交事务");
	}
}
  • 定义代理类
java 复制代码
public class PersonServiceProxy implements PersonService {
	
	//目标类
	private PersonService personService;
	
	//增强类
	private Transaction transaction;
	
	//利用构造函数将目标类和增强类注入
	public PersonServiceProxy(PersonService personService,Transaction transaction) {
		this.personService = personService;
		this.transaction = transaction;
	}
	
	@Override
	public void savePerson() {
		// 这是每个类都需要的操作
		transaction.beginTransaction();

		personService.savePerson();

		// 这是每个类都需要的操作
		transaction.commit();
	}
}
  • 测试
java 复制代码
@Test
public void serviceStatic() {
    new PersonServiceProxy(new PersonServiceImpl(), new Transaction()).savePerson();
}

开启事务 
保存人了
提交事务

假定我们现在又有个AnimalService如下:

java 复制代码
public interface AnimalService {
	public void saveAnimal();
}

public class AnimalServiceImpl implements AnimalService {
    @Override
    public void saveAnimal() {
        System.out.println("保存动物了");
    }
}

则需要重新定义一个AnimalService的代理类来为其添加事务,如下:

java 复制代码
public class AnimalServiceProxy implements AnimalService {

	//目标类
	private AnimalService animalService;

	//增强类
	private Transaction transaction;

	//利用构造函数将目标类和增强类注入
	public AnimalServiceProxy(AnimalService animalService, Transaction transaction) {
		this.animalService = animalService;
		this.transaction = transaction;
	}
	
	@Override
	public void saveAnimal() {
		// 这是每个类都需要的操作(PersonService写过一遍了,这里又要再写一遍,好痛苦!!!)
		transaction.beginTransaction();
		animalService.saveAnimal();
		// 这是每个类都需要的操作(PersonService写过一遍了,这里又要再写一遍,好痛苦!!!)
		transaction.commit();
	}
}

测试:

java 复制代码
new AnimalServiceProxy(new AnimalServiceImpl(), new Transaction()).saveAnimal();

开启事务 
保存动物了
提交事务

如果我们有100个service,则同样的定义对应代理类的工作就要做100次,岂不疯了!!!但是,幸好我们有动态代理,接下来看下使用动态代理解决这个需要重复定义代理类的问题。

2.2:jdk动态代理

  • 定义InvocationHandler
java 复制代码
public class ServiceInvocationHandler implements InvocationHandler {
    // 被代理的对象
    private Object target;
    private Transaction transaction;
    public ServiceInvocationHandler(Object obj){
        this.target=obj;
        this.transaction = new Transaction();
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 使用静态代理重复的事务开启操作就移动到这里了(只需要定义一次)
        this.transaction.beginTransaction();
        method.invoke(this.target, args);
        // 使用静态代理重复的事务提交操作就移动到这里了(只需要定义一次)
        this.transaction.commit();
        return null;
    }
}
  • 测试代理PersonService
java 复制代码
@Test
public void serviceJdkProxy() {
    ((PersonService) ServiceProxyFactory.makeProxy(new PersonServiceImpl())).savePerson();
}

开启事务 
保存人了
提交事务

如果是增加了AnimalService只需要增加如下代码即可获取代理类:

java 复制代码
((AnimalService) ServiceProxyFactory.makeProxy(new AnimalServiceImpl())).saveAnimal();

开启事务 
保存动物了
提交事务

但是,jdk动态代理有一个缺点就是必须要有接口,这在一定程度上限制了灵活度,如果是我们么有接口的话,则可以考虑使用cjlib来生成动态代理,一起看下!

2.3:cglib动态代理

  • 定义MethodInterceptor子类
java 复制代码
public class ServiceMethodInterceptor implements MethodInterceptor {
    private Transaction transaction = new Transaction();
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        // 开启事务(只需要写一次)
        transaction.beginTransaction();
        methodProxy.invokeSuper(o, objects);
        // 提交事务(只需要写一次)
        transaction.commit();
        return null;
    }
}
  • 获取代理工厂类
java 复制代码
public class CjlibProxyFactory {
    public static Object getGcLibDynProxy(Object target){
        Enhancer enhancer=new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(new ServiceMethodInterceptor());
        Object targetProxy= enhancer.create();
        return targetProxy;
    }
}
  • 测试
java 复制代码
@Test
public void serviceCjlibProxy() {
    ((PersonServiceImpl) CjlibProxyFactory.getGcLibDynProxy(new PersonServiceImpl())).savePerson();
}

开启事务 
保存人了
提交事务

如果是增加了AnimalService则增加如下代码即可获取代理类:

java 复制代码
((AnimalServiceImpl) CjlibProxyFactory.getGcLibDynProxy(new AnimalServiceImpl())).saveAnimal();

开启事务 
保存动物了
提交事务

写在后面

参考文章列表

java都有哪些动态代理机制?

Java中的代理模式------静态代理以及分析静态代理的缺点

相关推荐
渊渟岳3 小时前
掌握设计模式--装饰模式
设计模式
zh路西法5 小时前
【C++决策和状态管理】从状态模式,有限状态机,行为树到决策树(二):从FSM开始的2D游戏角色操控底层源码编写
c++·游戏·unity·设计模式·状态模式
夏旭泽6 小时前
设计模式-备忘录模式
设计模式·备忘录模式
蓝染-惣右介6 小时前
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
java·设计模式
捕鲸叉10 小时前
C++软件设计模式之类型模式和对象型模式
开发语言·c++·设计模式
诸葛悠闲10 小时前
设计模式——组合模式
设计模式·组合模式
诸葛悠闲11 小时前
设计模式——装饰模式
设计模式
西岭千秋雪_11 小时前
设计模式の中介者&发布订阅&备忘录模式
java·观察者模式·设计模式·中介者模式·备忘录模式
捕鲸叉11 小时前
C++软件设计模式之代理(Proxy)模式
c++·设计模式