java静态代理与动态代理

        假设有一个User类,里面有一个获取用户信息的方法,接口和实现如下:

//接口定义
public interface IUserService {
	void getUserInfo();
}

//接口实现
public class UserService implements IUserService{

	@Override
	public void getUserInfo() {
        //这个等待是后面测试用的,可忽略
		try {
			Thread.sleep(1000);
		} catch (Exception e) {
		}
		System.out.println("获取User信息");
	}

}

        当我们正常调用时,只需要实现这个类调用即可。这里并没有使用代理。

IUserService user=new UserService();
user.getUserInfo();

        假如现在有一个新需求,需要统计一下这个方法的运行时间。最简单的办法当然是直接在getUserInfo这个方法中增加时间统计代码即可,但是如果是如下情况,该怎么办(不仅指这个小需求):

1、这个类被封在了一个jar包中(静态代理)

2、不允许修改源代码,保持原代码的既有功能不变(静态代理,装饰器模式)

3、旧版代码和新版代码结构差异太大(静态代理,适配器模式)

4、对所有代码增加时间统计(动态代理)

        括号里已标注了对应的解决方案。其实在学习设计模式的时候,就感觉装饰器模式和适配器模式很类似,学完代理模式以后,感觉三个类似。。。

        从网上找了代理模式的含义:其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。

        个人理解:在不改变源文件的情况下,通过对原有功能的重写(适配器)、修改(装饰器)、增强(AOP)等方式实现新的目的。

        从上面列出的四点就可以看出,代理模式基本上就是要新写一个类来代替这个类执行新功能了。还是从代码看起。

public class UserServiceProxy implements IUserService{

	private IUserService user;
	
	public UserServiceProxy(IUserService user) {
		this.user=user;
	}
	@Override
	public void getUserInfo() {
		System.out.println("begin");
		long begin=System.currentTimeMillis();
		user.getUserInfo();
		System.out.println("end");
		System.out.println("一共运行了:"+(System.currentTimeMillis()-begin)+"ms");
		
	}

}

        只需要新建一个代理类实现同一个接口,把原始类传进来作为成员变量即可,调用方法如下:

IUserService user=new UserServiceProxy(new UserService());
user.getUserInfo();

        这样就实现了不改变源文件的情况下,增强了原有功能。

        其实从这里就能看出来,为什么说代理模式和装饰器模式很像,JDK中一个很著名的装饰器模式,就是IO流的处理了,我们经常是这么用的,比如new BufferedReader(new FileReader(file));就是一层一层的封装,每次的封装都是支持前一层操作,然后再增加新的功能,这行代码就是对文件读取支持的基础上,增加了缓冲区。

        但是这种方式很笨重,如果再来一个Company类,需要打印时间,那么我们就需要再写一个CompanyProxy类去实现,一定程度上,增加了代码冗余,并且效果还不如直接修改源代码的好。所以就有了动态代理的概念,即在运行时动态获取代理类对象,并对该对象进行增强。

        JDK动态代理的实现很简单,实现InvocationHandler 接口,并重写invoke方法,里面的核心语句是 method.invoke(target, args);这句话就是在调用源代码的方法。然后我们就可以在这句话前后,增加我们想要的内容,比如计算代码执行时间等。

public class InvocationHandlerTest<T> implements InvocationHandler {
	T target;
	public InvocationHandlerTest(T target) {
		this.target=target;
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("begin");
		long begin=System.currentTimeMillis();
		Object result = method.invoke(target, args);
		System.out.println("一共运行了:"+(System.currentTimeMillis()-begin)+"ms");
		System.out.println("end");
		return result;
	}
}

        调用方法如下:

IUserService user=new UserService();
InvocationHandlerTest<IUserService> userInvocationHandler = new InvocationHandlerTest<IUserService>(user);
IUserService userProxy=(IUserService)Proxy.newProxyInstance(IUserService.class.getClassLoader(), user.getClass().getInterfaces(), userInvocationHandler);
userProxy.getUserInfo();

        核心是Proxy.newProxyInstance(IUserService.class.getClassLoader(), user.getClass().getInterfaces(), userInvocationHandler);三个参数分别是被代理对象的classloader,接口和代理对象的invocation。

        这样就实现了一个动态代理,我们只需要修改这三个参数,就可以将所有的类进行动态代理了。这里要注意的是,jdk的动态代理必须要求有接口,因为它是基于接口实现的动态代理。如果想要不依赖接口实现动态代理,那么可以采用cglib技术实现。

        至于Proxy.newProxyInstance是怎么实现动态代理的,容我读几天源码再写吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值