JAVA23种设计模式——代理模式

代理模式很好的诠释了开闭原则,即:对扩展开放,对修改关闭。
它可以在不修改原有代码的情况下对程序进行扩展,甚至说在不知道原有代码是什么的情况下进行扩展,对目标类的扩展是非侵入式的。
如果理解Spring的AOP,就能理解代理模式是什么样一个玩法了,不理解也没关系,接下来开始详细介绍。

代理模式分为两种,即 静态/动态代理,AOP基本上就是基于动态代理来实现的。
一、静态代理
静态代理比较简单,就不再举例子,

  1. 首先需要一个接口 **IUserService **
  2. 然后有一个接口的实现类**UserService **
  3. 现在要对这个实现类进行代理,就有一个代理类**UserServiceProxy **,这个代理类也需要实现IUserService接口
//接口
public interface IUserService {
	public void addUser();
}
//需要被代理的目标类
public class UserService implements IUserService {
	@Override
	public void addUser() {
		System.out.println("-----添加一条user信息-----");
	}
}
//代理类
public class UserServiceProxy implements IUserService {
	private IUserService target;
	public UserServiceProxy(IUserService target){
		this.target = target;
	}
	@Override
	public void addUser() {
		System.out.println("------添加user信息之前先校验数据是否安全------");
		target.addUser();
		System.out.println("------添加user信息成功,将信息反馈回客户端------");
	}
}
//调用
public class Test {
	public static void main(String[] args){
		UserService target = new UserService();
		UserServiceProxy showProxy = new UserServiceProxy(target);
		showProxy.addUser();
	}
}

怎么样,是不是感觉就是一个精简版的AOP,实际上springAop是用的动态代理来实现的
优点:逻辑简单
缺点:会产生大量代理类,当接口改变时,目标类和代理类都要更改,前期写时简单,后期维护复杂。

接下来是动态代理,动态代理避免了接口改变之后目标类和代理类都要更改的情况,不管代理的目标方法怎么改变,代理类都可以正常代理,实现了解耦。

二、动态代理
动态代理又分两种,一种是JDK代理,一种是Cglib代理,JDK代理和静态代理一样需要有一个接口可以被实现,
JDK代理和Cglib代理的区别在于当被代理的对象并没有任何接口可以实现的时候,就需要用Cglib代理了。

1.JDK代理
JDK代理需要用到Proxy类,代理类所在包:java.lang.reflect.Proxy,需要使用其内的静态newProxyInstance方法。

完整方法为: static Object newProxyInstance( ClassLoader loader, Class<?>[]interfaces, InvocationHandler h )
里面共有三个参数,目标对象的类加载器、目标对象实现的接口类型、事件处理器(动态生成目标的代理对象)
大部分的写法都是固定的,只有我们自己的业务逻辑需要按实际需求更改

  1. 首先也是需要有一个接口
  2. 然后也是要有一个被代理的接口实现类,这前两步和静态代理是一样的
  3. 接着就需要在代理类里使用newProxyInstance方法来动态生成代理对象
//和静态代理一样,接口
public interface IUserService {
	public void addUser();
}
//和静态代理一样,接口实现类,也是被代理的目标
public class UserService implements IUserService {
	@Override
	public void addUser() {
		System.out.println("-----添加一条user信息-----");
	}
}
//第三步是重点,目标代理类,这里面的写法流程都是固定的,就是一个模板
public class UserServiceProxy {
	private IUserService target;
	public UserServiceProxy(IUserService target){//和第二步一样,把被代理的对象传进来
		this.target = target;
	}
	public Object getInstance(){
		return Proxy.newProxyInstance(
				target.getClass().getClassLoader(), //参数1,目标对象的类加载器,写法固定
				target.getClass().getInterfaces(),//参数2,目标对象实现的接口类型,写法固定
				new InvocationHandler() {//参数3,事件处理器,写法固定,需要实现InvocationHandler接口,通常用匿名内部类直接实现,也可以单独写一个方法实现InvocationHandler接口,然后将实现类对象作为参数
					@Override
					public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
						System.out.println("------添加user信息之前先校验数据是否安全------");//1只有这里的业务逻辑需要更改
                        //执行目标对象方法
                        Object returnValue = method.invoke(target, args);//这里实现了动态代理,对具体方法进行了解耦,无需知道目标有什么方法。
                        System.out.println("------添加user信息成功,将信息反馈回客户端------");//2只有这里的业务逻辑需要更改
                        return returnValue;
					}
				}
		);
	}
}

缺点:当被代理的目标只是一个普通类,并没有实现接口时,就无法使用JDK代理,JDK动态代理只针对接口的实现类

2.Cglib代理
Cglib代理,也叫做子类代理,采用动态创建子类的方法,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。也因此对于final方法,无法进行代理。CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。
需要引入Cglib的jar包,但是Spring-core包中已经包含了Cglib,这里我引入的是Spring-core-3.2.13.jar

//没有实现接口的普通目标类
public class UserService{
	public void addUser() {
		System.out.println("-----添加一条user信息-----");
	}
}
//实现了MethodInterceptor的代理类
public class UserServiceProxy implements MethodInterceptor{
	private UserService target;
	Enhancer en = new Enhancer();//帮我们动态创建代理的工具类
	public UserServiceProxy(UserService target){//依旧是获取被代理的目标
		this.target = target;
		
	}
	public Object getProxyInstance(){
        en.setSuperclass(target.getClass());//设置要代理的目标类,写法固定
        en.setCallback(this);//设置回调函数,写法固定
        return en.create();//创建代理对象,写法固定
    }
	@Override
	public Object intercept(Object obj, Method method, Object[] objs,
			MethodProxy proxy) throws Throwable {
		System.out.println("------添加user信息之前先校验数据是否安全------");//1只有这里是需要我们根据实际需求修改业务逻辑
        //执行目标对象方法
		Object targetObj = proxy.invokeSuper(obj, objs);//实现动态代理
        System.out.println("------添加user信息成功,将信息反馈回客户端------");//2只有这里是需要我们根据实际需求修改业务逻辑
		return targetObj;
	}
}
//调用
public class Test {
	public static void main(String[] args){
		UserService target = new UserService();
		UserService showProxy = (UserService)new UserServiceProxy(target).getProxyInstance();
		showProxy.addUser();
	}
}

总结:相比于静态代理的每个目标类对应一个代理类的繁重,动态代理可以把所有需要代理的操作集中到一个代理类中,使用模板的代理流程,只需要在代理类里针对不同的业务逻辑封装不同的方法来调用,大大减少了代码量,也提高了代码的重用性。
当目标对象有接口时用JDK代理,当目标对象没有接口时使用Cglib(子类)代理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值