设计模式-1、代理模式

什么是代理模式?

代理模式提供了对目标对象的另一个访问方式,即通过代理对象进行目标对象的访问。这样做的目的是在实现目标对象的功能之外,还能做一些额外的增强扩展。

为什么需要代理模式?

1、中介隔离对象:当客户类不愿意或者不能直接使用目标对象时,代理对象就作为中介者,将客户类的请求委托给目标对象。
2、开闭原则:当需要在实现目标对象的功能之外,做一些额外的增强扩展时。比如在方法执行前后打日志,在数据库插入方法前后加上事务等,可以在不修改目标对象代码的前提上做扩展。

静态代理

如图是静态代理的类图
在这里插入图片描述
这里可以看到一个RealSubject,在代理模式的设计中,会设计一个接口与目标对象一致的代理对象,在代理对象中持有对目标对象的引用,当客户端访问这个接口时,就会被代理对象拦截,在访问目标对象前后实现增强。
下面来写一个静态代理的案例代码:
创建服务类接口:
Subject:

public interface Subject {
	void request();
}

实现服务类接口:
RealObject:

public class RealObject implements Subject{

	public void request() {
		System.out.println("数据库操作");
		
	}

}

代理类:
Proxy:

public class Proxy implements Subject{
	
	private Subject real ;

	public void request() {
		System.out.println("打开事务");
		real.request();
		System.out.println("提交事务");
		
	}

	public Proxy(Subject real) {
		super();
		this.real = real;
	}

}

客户端:
Client:

public static void main(String[] args) {
		Subject subject = new Proxy(new RealObject());
		subject.request();

	}

输出:
打开事务
数据库操作
提交事务

静态代理的优缺点

优点:可以实现在不修改目标对象代码的前提下,进行扩展增强
缺点:当需要不同的增强时,需要编写不同的代理类,同时当服务接口修改时,对应的代理类和目标对象都需要修改,不方便维护。

为了解决静态代理的缺点,所以出现了动态代理。

动态代理

特点:在内存中根据要求动态创建代理对象。不需要实现接口

JDK动态代理

API:
代理类所在包:java.lang.reflect.Proxy
JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
loader:目标对象的类加载器
interfaces:目标对象实现的接口
h:执行目标对象的方法时,会触发执行,将目标对象的Method对象作为参数传入进来

动态代理类:

public class ProxyFactory {
	
	private Object target;

	public ProxyFactory(Object target) {
		super();
		this.target = target;
	}
	
	public Object getProxy() {
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
			
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				System.out.println("开始事务");
				Object returnValue = method.invoke(target, args);
				System.out.println("提交事务");
				return returnValue;
			}
		});
	}
	

}

客户端:

public static void main(String[] args) {
		Subject target = new RealObject();
		Subject proxy = (Subject) new ProxyFactory(target).getProxy();
		proxy.request();
	}

输出:
打开事务
数据库操作
提交事务

注意:JDK动态代理,默认会对所有的方法都进行相同的增强,如果想指定方法,或者不同方法实现不同的增强,可以在InvocationHandler编写代码

JDK代理的缺点

目标对象必须实现一个或者多个接口,无法对没有实现任何接口的对象进行代理。

因此出现了 CGLIB动态代理。

CGLIB动态代理

本质是生成目标对象的子类,通过方法的重写来实现增强。

API:
Enhancer工具类用来创建目标对象的子类
MethodInterceptor 设置目标对象的回调函数,即增强扩展的地方。

代理类:

public class ProxyFactory {
	
	private Object target;

	public ProxyFactory(Object target) {
		super();
		this.target = target;
	}
	
	public Object getProxy() {
		//1.工具类
        Enhancer en = new Enhancer();
        //2.设置父类
        en.setSuperclass(target.getClass());
        //3.设置回调函数
        en.setCallback(new MethodInterceptor() {

			public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
				System.out.println("打开事务");
				//据说用cglib的MethodProxy执行方法性能更佳
				//Object returnValue = arg3.invoke(target, arg2);
				Object returnValue = arg1.invoke(target, arg2);
				System.out.println("提交事务");
				return returnValue;
			}
		});
        //4.创建子类(代理对象)
        return en.create();
	}
	

}

客户端:

public static void main(String[] args) {
		Subject target = new RealObject();
		Subject proxy = (Subject) new ProxyFactory(target).getProxy();
		proxy.request();
	}

输出:
打开事务
数据库操作
提交事务

注意:
1、CGLIB出现的目的是为了给没有实现接口的类增强,但是他其实也能给接口增强,如上的代码其实就是给接口增强,不管有没有实现接口,对CGLIB是一样的都是通过生成目标对象子类的方式来实现。
2、因为要通过子类的方式实现增强,因此CGLIB不能给final类增强,会报错
3、增强的过程是通过方法的重写做到,因此CGLIB也不能给final方法增强,final方法不报错,只是没有效果罢了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值