设计模式之代理模式

二月份的时候看MyBatis书籍,知道它用到了代理模式,所以选择代理模式作为设计模式的开篇之作

1.为什么需要代理模式

网上搜索了好多关于WHY,但是大多都是一些所谓的来自生活的解释,感觉没有从代码的角度去解释。比如每个明星会有自己的经纪人,明星只要专注演戏唱歌,经纪人则负责安排出席活动啥的,这是一种代理模式。

近官方一点的是这样的:

官方版+OWN理解:

通过代理,可以控制如何访问真正的服务对象,提供额外服务,有机会通过重写一些类来满足特定需要。例如:MyBatis的Dao接口(有的叫Mapper),作为一个接口没有办法执行,它却可以直接通过接口调用xml中的方法,显然这是一种代理。具体后面会有更详细的介绍。

 

2.简介

代理,中介,给一个对象提供一个代理,由代理对象控制对原对象的引用。

代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。

3.分类

3.1 静待代理

先看一个例子:客户实际需要调用的是RealSubject的request()方法,现在用ProxySubject来代理RealSubject,同样达到目的,同时还加入了其他方法preRequest()等,提供了额外服务。

抽象角色:

真实角色:

代理角色:

客户调用:

但是这样的话,真实角色必须先存在,而且一个真实角色得对应一个代理角色,会造成类的冗余。如果事先不知道真实角色,这时候我们就需要使用动态代理了。

动态代理类的字节码是程序运行时由Java反射机制动态生成。

3.2 JDK动态代理

JDK的动态代理由JDK的java.lang.reflect.*包提供支持,需要完成这么几个步骤:

1)编写业务接口和服务类(实现类),这个是真正服务提供者,在JDK代理中接口是必须的

2)编写代理类,提供绑定和代理方法

JDK的代理最大的缺点就是需要提供接口,MyBatis的Dao(或称Mapper)就是一个接口,它采用的就是JDK的动态代理。

关键代码:下图代理类Proxy不去实现具体的某个业务接口,而是实现了JDK提供的InbocationHander类。在Proxy中我们不需要知道具体业务类(委托类),在运行之前,将委托类和代理类进行了解耦,在运行期通过private Object target才产生形成联系,然后在调用的时候通过getInstance确定谁是委托对象。

public class Proxy implements InvocationHandler {
	private Object target;

	// 绑定委托对象并返回一个代理类
	public Object GetInstance(Object target) {
		this.target = target;
		// 取得代理对象
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
	}

	@Override
	// 调用方法
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object result = null;
		System.out.println("before");
		// 执行方法
		result = method.invoke(target, args);
		System.out.println("after");
		return result;
	}
}

然后客户调用的时候才传入真正的具体业务类(委托类)

Proxy proxy = new Proxy();    
// 在这里进行真正的对象传入  
User user = (User)proxy.getInstance(new UserImpl());    
user.getUsers(); 

 

3.3 CGLIB动态代理

 

开源框架CGLIB是一种流行的动态代理,克服了JDK动态代理必须提供接口才可以使用的缺陷。

 

CGLIB是针对类来实现代理的,其实现原理:CGLIB的底层采用ASM字节码生成框架,使用字节码技术生成代理,比使用反射生成代理的效果要高,是对指定的目标类生成一个子类,并覆盖其中方法实现增强。但是也有一点点不足,因为采用的是继承,所以不能对final修饰的类进行代理。

步骤: 

1)建立一个普普通通的业务类

2)写CGLIB代理类,这里的不同是第一步中,我们不需要在建接口了,只是一个普普通通的java类。

public class CglibProxy implements MethodInterceptor {    
    // 委托对象,运行时定类型  
    private Object target;    
    
    // 创建代理对象   
    public Object getInstance(Object target) {    
        this.target = target;    
        Enhancer enhancer = new Enhancer();    
        enhancer.setSuperclass(this.target.getClass());    
        // 回调方法    
        enhancer.setCallback(this);    
        // 创建代理对象    
        return enhancer.create();    
    }    
    
    // 回调方法    
    @Override    
    public Object intercept(Object obj, Method method, Object[] args,    
            MethodProxy proxy) throws Throwable {    
        System.out.println("before");    
        Object result = proxy.invokeSuper(obj, args);    
        System.out.println("after");    
        return result  
    }    
} 

发现proxy.invokeSuper(obj, args)是关键,调用者只需要传入对象就行

CglibProxy cproxy = new CglibProxy();    
User user = (User)cproxy.getInstance(new User());    
user.getUsers(); 

 

4.应用

4.1 MyBatis-DAO的代理开发模式 

只需要定义Dao接口,由MyBatis产生Dao的接口的实现类,属于JDK动态代理,因为提供了接口

此外,在MyBatis中通常在延迟加载的时候才会用到CGLIB动态代理

步骤:

1)定义一个Dao接口,接口中

public interface DemoDao  {
	public List<User> getUserById(Long id) ;
}

2)对应的mapper.xml中添加相应的select

<select id="getUserById" parameterType="long" resultMap="BaseResultMap">
	select ...
</select>

3)service层可以直接通过注入dao接口,直接调用方法demoDao.getUserById(1L)

注:此处的demoDao其实就是MapperProxy<T>

4.2 Spring的AOP(面向切面编程)

面向对象编程是从【静态角度】考虑程序的结构,而面向切面编程是从【动态角度】考虑程序运行过程。
AOP底层,就是采用【动态代理】模式实现的。采用了两种代理:JDK动态代理和CGLIB动态代理。

5.面试

1)静态代理与动态代理区别
静态代理是代理类中需要引入真实类,所以事先必须知道真实类,并且代理类源代码是由程序员编写的,在程序运行前,它的.class文件就已经存在了。而动态代理是基于反射生成代理对象,所以代理类字节码是在程序运行时由Java反射机制动态生成。动态代理包括JDK动态代理和CGLIB动态代理(区别JDK动态代理必须提供接口才可以)。典型代表MyBatis的Dao(或称Mapper)就是一个接口,它采用的就是JDK的动态代理;Spring的AOP也采用了动态代理。

代理模式是一种结构型设计模式,它提供一个代理对象来代表另一个对象。在代理模式中,有一个被称为实际对象(Subject)和一个被称为代理对象(Proxy)的中介,代理对象持有实际对象的引用,并且可以控制对实际对象的访问。代理模式的主要目的是在不修改原始对象的情况下,为原始对象添加额外的逻辑处理。 代理模式分为多种型,如远程代理、虚拟代理、保护代理等,它们各自有不同的应用场景: - 远程代理:为远程对象提供一个本地代表。 - 虚拟代理:根据需要创建开销大的对象,通过虚拟代理控制访问这些对象的过程。 - 保护代理:控制对原始对象的访问权限,例如进行权限检查。 代理模式的优点包括: 1. 能够控制对真实对象的访问,并在访问前后添加额外的逻辑。 2. 可以通过代理对象实现延迟加载,即在实际需要时才创建真实对象。 3. 增强了对真实对象的封装,并且可以避免对真实对象的重复引用。 在C#中实现代理模式通常涉及以下步骤: 1. 定义一个接口或抽象,声明真实对象和代理对象需要实现的方法。 2. 实现真实对象的,按照接口或抽象的要求实现具体方法。 3. 实现代理,它同样实现接口或抽象,并在方法中持有真实对象的引用,通过调用真实对象的方法来执行所需的操作,同时可以添加额外的逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值