java设计模式之代理模式

什么是代理模式

代理(Proxy)模式是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。

我们想象在一个法庭里:

目标类:实现基础的功能。相当于原告,知道大致案件的情况。

代理类:实现基于目标类加强和扩展的功能。相当于律师,对原告给出的证据进行加强。

业务接口:定义了目标方法。相当于证据,需要律师给与加强和扩展。

静态代理

静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。

静态代理是在运行前就已经写好了目标类和代理类的代理关系。静态代理类似于企业与企业的法律顾问间的关系。法律顾问与企业的代理关系,并不是在“官司”发生后才建立的,而是之前就确立好的一种关系

我们来举个例子:

最近,发生了一些事情,但是原告和被告争执不下,业务接口:

package com.lmm.service;

/**
 * @author lmm E-mail:violet_mmhh@163.com
 * @time 时间:2019年4月23日
 * @function 功能:实现主业务接口
 */
public interface ISomeService {
	// 目标方法
	String doFirst();

	// 目标方法
	void doSecond();
}

原告觉得doFirst可以给出doFirst返回的小写的love,目标类:

package com.lmm.service;

/**
 * @author lmm E-mail:violet_mmhh@163.com
 * @time 时间:2019年4月23日
 * @function 功能:实现目标类
 */
public class SomeServiceImpl implements ISomeService {

	public String doFirst() {
		System.out.println("执行doFirst方法");
		return "love";
	}

	public void doSecond() {
		System.out.println("执行doSecond方法");

	}

}

但是原告觉得应该是大写的LOVE,所以请过来律师进行帮忙(对目标类进行加强),但是律师不能直接否定原告提供的证据:应该是小写的love,更不可能更改法庭(测试类)的抉择,就是说代理类不能通过直接更改目标类或者测试结果来达到目的!代理类(用于增强目标类):

/**
 * 
 */
package com.lmm.proxy;

import com.lmm.service.ISomeService;
import com.lmm.service.SomeServiceImpl;

/**
 * @author lmm E-mail:violet_mmhh@163.com
 * @time 时间:2019年4月23日
 * @function 功能:实现代理类
 */
/**
 * @author Administrator
 * 
 */
public class SomeServiceProxy implements ISomeService {
	private ISomeService target;

	public SomeServiceProxy() {
		target = new SomeServiceImpl();
	}

	public String doFirst() {
		// 代理类调用和增强目标类
		// 所以先创建目标对象
		// target = new SomeServiceImpl();
		// 代理类调用目标方法
		String result = target.doFirst();
		// 增强就发生在这里
		return result.toUpperCase();
	}
	public void doSecond() {
		// 此处无增强
		target.doSecond();
	}

}

这个时候律师(代理类)就给原告(目标类)进行了证据的加强,今天开庭审理,法庭上(测试类):

package com.lmm.test;

import com.lmm.proxy.SomeServiceProxy;
import com.lmm.service.ISomeService;

/**
 * @author lmm E-mail:violet_mmhh@163.com
 * @time 时间:2019年4月23日
 * @function 功能:
 */
public class MyTest {

	public static void main(String[] args) {
		ISomeService service = new SomeServiceProxy();
		String result = service.doFirst();
		System.out.println("result=" + result);
		service.doSecond();
	}
}

法庭(测试类)采取了律师(代理类)的加强后的证据,最终胜诉(输出了大写的LOVE):

我们来看一下其调用顺序:

JDK动态代理

jdk动态代理的原理其实和通过代理类来进行静态代理一样。不一样的就是在需要的时候才使用JDK的Proxy动态代理,要求目标类实现接口,就像这个律师比较厉害,在开庭之前没有和原告进行沟通(即没有产生代理关系),在法庭上才产生了代理关系。【即删除上述代理类SomeServiceProxy,直接将测试类改为:】

package com.lmm.test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import com.lmm.service.ISomeService;
import com.lmm.service.SomeServiceImpl;

/**
 * @author lmm E-mail:violet_mmhh@163.com
 * @time 时间:2019年4月23日
 * @function 功能:
 */
public class MyTest {

	public static void main(String[] args) {
		final ISomeService target = new SomeServiceImpl();
		/*
		 * 三个参数分别为 1.目标类的类加载器 2.目标类所实现的所有接口 3.
		 */
		//使用JDK的Proxy动态代理,要求目标类实现接口
		//因为其底层原理,其与静态代理相同
		ISomeService service = (ISomeService) Proxy.newProxyInstance(target
				.getClass().getClassLoader(),
				target.getClass().getInterfaces(), new InvocationHandler() {// 接口中的方法体
					// proxy:代理对象
					// method:目标方法
					// args:目标方法的参数列表
					public Object invoke(Object proxy, Method method,
							Object[] args) throws Throwable {
						// 调用目标方法
						// 内部类使用外部类的成员变量,外部类变量必须加final,因为外部类不能变化,变化将导致内部类计算错误
						Object result = method.invoke(target, args);
						if (result != null) {
							result = ((String) result).toUpperCase();
						}
						return result;
					}
				});
		String result = service.doFirst();
		System.out.println("result=" + result);
		service.doSecond();
	}
}

运行结果和静态代理相同。

但是JDK只能针对实现了接口的类做动态代理,而不能对没有实现接口的类做动态代理,所以cgLib横空出世!CGLib(Code Generation Library)是一个强大、高性能的Code生成类库,它可以在程序运行期间动态扩展类或接口,它的底层是使用java字节码操作框架ASM实现。

CGLIB动态代理

  • 需要引入cglib的jar文件,由于Spring的核心包中已经包括了Cglib功能,所以也可以直接引入spring-core-3.2.5.jar
  • 目标类不能为final,因为final类不能被继承
  • 目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法
  • CGLIB动态代理的生成原理为生成目标列的子类

1.定义目标类

// 目标类
public class SomeService {

	public String doFirst() {
		System.out.println("执行doFirst()方法");
		return "love";
	}

	public void doSecond() {
		System.out.println("执行doSecond()方法");
	}

}

2.创建代理类工厂,该类要实现MethodInterceptor接口,该类中完成三样工作

  1. 声明目标类的成员变量,并创建以目标类对象为参数的构造器。用于接收目标对象。
  2. 定义代理的生成方法,用于创建代理对象。代理对象即目标类的子类。
  3. 定义回调接口方法。对目标类的增强这在这里完成。
     
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import com.lmm.service.SomeService;

public class MyCglibFactory implements MethodInterceptor {

	private SomeService target;

	public MyCglibFactory() {
		target = new SomeService();
	}

	public SomeService myCglibCreator() {
		// 创建增强器对象
		Enhancer enhancer = new Enhancer();
		// 指定目标类,即父类
		enhancer.setSuperclass(SomeService.class);
		// 设置回调接口对象
		enhancer.setCallback(this);

		return (SomeService) enhancer.create();
	}

	// 回调方法
	/*obj:代理对象
	 * method:代理对象的方法,即增强过的业务方法
	 * args:方法参数
	 * proxy代理对象方法的代理对象
	 * */
	@Override
	public Object intercept(Object obj, Method method, Object[] args,
			MethodProxy proxy) throws Throwable {
		// 调用目标方法
		Object result = method.invoke(target, args);
		if (result != null) {
			result = ((String) result).toUpperCase();
		}
		return result;
	}

}

3.定义测试类

import com.bjpowernode.factory.MyCglibFactory;
import com.bjpowernode.service.SomeService;

public class MyTest {

	public static void main(String[] args) {
		SomeService service = new MyCglibFactory().myCglibCreator();
		String result = service.doFirst();
		System.out.println("result = " + result);
		service.doSecond(); 
	}

}

方法回调设计模式

在Java中,就是类A调用类B中的某个方法b,然后类B又在某个时候反过来调用类A中的某个方法a,对于A来说,这个a方法便叫做回调方法。Java的接口提供了一种很好的方式来实现方法回调。这个方式就是定义一个简单的接口,在接口之中定义一个我们希望回调的方法。这个接口称为回调接口

在前面的例子中,我们定义的MyCglibFactory类就相当于前面所说的A类,而 Enhancer类则是B类。A类中调用了 Enhancer类的 setCallback(this)方法,并将回调对象this作为实参传递给了 Enhancer类。 Enhancer类在后续执行过程中,会调用A类中的intercept)方法,这个 intercept()方法就是回调方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值