设计模式之代理模式

写在前面

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中的代理模式------静态代理以及分析静态代理的缺点

相关推荐
王嘉俊92510 分钟前
设计模式--适配器模式:优雅解决接口不兼容问题
java·设计模式·适配器模式
王嘉俊92512 分钟前
设计模式--组合模式:统一处理树形结构的优雅设计
java·设计模式·组合模式
rongqing201913 分钟前
Google 智能体设计模式:多智能体协作
设计模式
李广坤15 小时前
状态模式(State Pattern)
设计模式
李广坤16 小时前
观察者模式(Observer Pattern)
设计模式
李广坤17 小时前
中介者模式(Mediator Pattern)
设计模式
李广坤17 小时前
迭代器模式(Iterator Pattern)
设计模式
李广坤18 小时前
解释器模式(Interpreter Pattern)
设计模式
阿无,21 小时前
java23种设计模式之前言
设计模式
Asort1 天前
JavaScript设计模式(八):组合模式(Composite)——构建灵活可扩展的树形对象结构
前端·javascript·设计模式