设计模式之代理模式

写在前面

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

相关推荐
Zane19943 天前
一个 new 就能创建对象,为什么还要拆出单例、工厂、建造者、原型四种模式?
设计模式
怕浪猫4 天前
一行命令复刻爆款视频,我把 Hypit 从安装跑到了出片
人工智能·设计模式·程序员
Zane19944 天前
函数式编程里的函数,其实不是你天天写的那个函数——三大编程范式的边界在哪
设计模式
Zane19945 天前
策略模式现在该不该上?一次讲清楚过度设计和设计不足怎么找平衡
设计模式
她说..5 天前
常见设计模式-模板方法模式
java·spring·设计模式·springboot
xiaofeiyang1505 天前
第六章 · 桥接 — 三支毛笔,画出九种颜色
设计模式
IPdodo_6 天前
住宅 IP 与数据中心 IP 到底差在哪:用 RDAP、BGP 和多源证据做工程判定
网络·网络安全·代理模式·ip
Shadow(⊙o⊙)6 天前
OTOL设计模式 One Thread One Loop
服务器·网络·设计模式
sarasuki7 天前
如何让LLM 能在半夜偷偷打开网易云呢?
人工智能·设计模式·agent
小王师傅667 天前
【设计模式】装饰模式(四):框架源码实战——从 Java I/O 到 Spring 到 MyBatis
java·设计模式