Java代理

引言:

在一个系统中,假设我们需要对所有方法实施监控,允许在其之前或者之后执行一些通用的操作,比如打开关闭交易,验证用户权限,记录日志,我们应该怎么做呢?在每个方法体中都添加这些代码无疑会给我们带来巨大的工作量,即使完成了项目,实现了功能,以后的维护工作也绝对会令人头疼。借助AOP(Aspect Oriented Programming)我们可以解决以上问题,那么AOP的内部实现机制是什么呢?动态代理(Dynamic Proxy)!

首先自己码代码实现一下上面的功能,暂且叫它静态代理,算是抛砖引玉吧。

先定义一个接口Apple,其中包含方法wash()和eat(),代码如下:

package com.paxdata.proxy.itf;

public interface Apple {
void wash();
void eat();
}

提供一个实现类:

package com.paxdata.proxy.itf.impl;

import com.paxdata.proxy.itf.Apple;

public class AppleImpl implements Apple {

@Override
public void wash() {
System.out.println("Washing apple...");
}

@Override
public void eat() {
System.out.println("Eatting apple...");
}

}

下面我们自己实现一个代理类,其实并非真正意义上的代理,只是在我们自己的类里封装了一个原始类,在调用原始类地各个方法之前执行了我们需要额外去做的操作,比如上面提到的记录日志等,看代码:

package com.paxdata.proxy;

import com.paxdata.proxy.itf.Apple;

public class AppleStaticProxy implements Apple {
private Apple apple;

public AppleStaticProxy(Apple apple) {
this.apple = apple;
}

@Override
public void wash() {
System.out.println("Start to wash...");
apple.wash();
System.out.println("End to wash...");
}

@Override
public void eat() {
System.out.println("Start to eat...");
apple.eat();
System.out.println("End to eat...");
}

}
准备工作就绪,我们来用JUnit做下测试:

package com.paxdata.proxy.test;

import junit.framework.TestCase;

import com.paxdata.proxy.AppleStaticProxy;
import com.paxdata.proxy.itf.Apple;
import com.paxdata.proxy.itf.impl.AppleImpl;

public class TestAppleStaticProxy extends TestCase{
public void testAppleStaticProxy() {
Apple apple = new AppleImpl();
AppleStaticProxy appleProxy = new AppleStaticProxy(apple);
appleProxy.wash();
appleProxy.eat(); }

没错,运行结果如下:

Start to wash...
Washing apple...
End to wash...
Start to eat...
Eatting apple...
End to eat...

好啦,砖抛完了,不过有一个问题,大家试想一下,要是我们有很多方法,难道要修改每个方法来添加这些公用代码吗?当时不能。下来该JDK动态代理闪亮登场了。

JDK动态代理中包含一个类和一个接口:
InvocationHandler接口:
public interface InvocationHandler {
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable; 参数说明:
Object proxy:指被代理的对象。
Method method:要调用的方法
Object[] args:方法调用时所需要的参数

可以将InvocationHandler接口的子类想象成一个代理的最终操作类,替换掉ProxySubject。

Proxy类:
Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces,

InvocationHandler h) throws IllegalArgumentException ClassLoader loader:类加载器
Class<?>[] interfaces:得到全部的接口
InvocationHandler h:得到InvocationHandler接口的子类实例
在Proxy类中的newProxyInstance()方法中需要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器,在Java中主要有一下三种类加载器;
Booststrap ClassLoader:此加载器采用C++编写,一般开发中是看不到的;
Extendsion ClassLoader:用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类;
AppClassLoader:(默认)加载classpath指定的类,是最常使用的是一种加载器。

动态代理:
与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler接口提供了生成动态代理类的能力。

用JDK动态代理实现的动态代理类:

package com.paxdata.proxy;

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

public class AppleDynamicProxy implements InvocationHandler {
private Object target; public Object bind(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("Start to execute method " + method.getName());
result = method.invoke(target, args);
System.out.println("End to execute method " + method.getName());
return result;
}

}

在上面的类中,我们定义了一个Apple对象,然后通过bind方法可以将一个实现了该接口的类的对象赋给它(面向接口编程),通过Proxy.newProxyInstance()方法来返回代理对象给我们使用。当我们通过代理对象调用方法时,实际上是通过反射来调用原始对象的相应方法。

下面我们再用JUnit来测试一下:

package com.paxdata.proxy.test;

import junit.framework.TestCase;

import com.paxdata.proxy.AppleDynamicProxy;
import com.paxdata.proxy.itf.Apple;
import com.paxdata.proxy.itf.impl.AppleImpl;

public class TestAppleDynamicProxy extends TestCase {
public void testAppleDynamicProxy(){
AppleImpl appleImpl = new AppleImpl();
Apple apple = (Apple) new AppleDynamicProxy().bind(appleImpl);
apple.wash();
apple.eat(); }

结果想来大家已经知道了,结果就是下面这样:

Start to execute method wash
Washing apple...
End to execute method wash
Start to execute method eat
Eatting apple...
End to execute method eat
 

有了JDK动态代理的基础,大家可以去啃AspectJ或其他AOP框架啦,偶也~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值