Dynamic Proxy,动态代理,你就可以想成是代购。你要法国的商品,你自己买不到,但是可以通过国内的代购公司买到。生产法国商品的原公司,就像是原对象(original object),而国内的代购公司就像是代理商(proxy),我们通过代理商(proxy)就能够拿到法国商品(调用original object的method),并且还可以对原来的法国商品进行“强化”,比如快递服务,售后服务等等(对original object的method进行前置和后置增强)。
JDK动态代理,需要有一个Handler,这个Handler,必须实现InvocationHandler接口,同时我们要实现接口中的invoke方法。因为代理产生后,我们就要拿到原公司的产品(调用original object的method),找谁调用,就找InvocationHandler的实现类。
一个JDK动态代理的案例:
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkProxyDemo {
interface If {
void originalMethod(String s);
}
static class Original implements If {
public void originalMethod(String s) {
System.out.println(s);
}
}
static class Handler implements InvocationHandler {
private final If original;
public Handler(If original) {
this.original = original;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException {
System.out.println("BEFORE");
method.invoke(original, args);
System.out.println("AFTER");
return null;
}
}
public static void main(String[] args){
Original original = new Original();
Handler handler = new Handler(original);
If f = (If) Proxy.newProxyInstance(If.class.getClassLoader(),
new Class[] { If.class },
handler);
f.originalMethod("Hello");
}
}
Handler中包含了真实对象,在调用真实对象的方法的同时,进行一些增强:
System.out.println("BEFORE");
method.invoke(original, args);
System.out.println("AFTER");
注意产生代理的代码If f = (If) Proxy.newProxyInstance(If.class.getClassLoader(), new Class[] { If.class }, handler);
我们用Proxy的静态方法来产生代理,这是类似于工厂模式的产生代理的方式,产生的代理叫做$Proxy0, $Proxy1等等。
至于传入的参数,我们需要传入一个类加载器,一个Class数组,这个Class数组中,我们要放接口的Class对象,这些接口,就是原对象实现的接口,这决定了我们调用原对象的方法有多少、是什麽。
至于Proxy Pattern,你可以想象成给班主任教课本费。课本费当然不是直接交给班主任的,而是先交给班长,再由班长转交给班主任。这里班长有两个作用,第一个,他收集课本费,第二个,他可以检查你交的对不对。你交的不对(比如交少了),拿回去重新交过。
现在举一个办公室网络权限的问题。并不是所有的员工都能上所有的网站的,这是根据的你的身份来决定的。
首先,我们要有一个接口,定义一些方法:
package proxy_pattern;
public interface OfficeInternetAccess {
public void grantInternetAccess();
}
然后是一个实现类,这个类里面规定的员工可以有足够的权限:
package proxy_pattern;
public class RealInternetAccess implements OfficeInternetAccess {
private String employeeName;
public RealInternetAccess(String empName) {
this.employeeName = empName;
}
@Override
public void grantInternetAccess() {
System.out.println("Internet Access granted for employee: "+ employeeName);
}
}
然后就是代理类,他的功能就相当于是班长:
package proxy_pattern;
public class ProxyInternetAccess implements OfficeInternetAccess {
private String employeeName;
private RealInternetAccess realaccess;
public ProxyInternetAccess(String employeeName) {
this.employeeName = employeeName;
}
@Override
public void grantInternetAccess() {
if (getRole(employeeName) > 4) {
realaccess = new RealInternetAccess(employeeName);
realaccess.grantInternetAccess();
} else {
System.out.println("No Internet access granted. Your job level is below 5");
}
}
public int getRole(String emplName) {
// Check role from the database based on Name and designation
// return job level or job designation.
return 9;
}
}
注意,他也实现了接口。不过这个代理类中实现的方法就更严格了,他要检查你的职位高低,低于某个阶段就没有足够的权限。
当我们要加载一些大的配置对象时,比如JDBC或者SessionFactory,这时,我们就应该加载一次就行了,并不是每次连接都要加载,所以我们要找一个代理来完成。
接口:
package proxy_pattern;
public interface ExpensiveObject {
void process();
}
实际实现对象:
package proxy_pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ExpensiveObjectImpl implements ExpensiveObject {
private static Logger LOG = LoggerFactory.getLogger(ExpensiveObjectImpl.class);
public ExpensiveObjectImpl() {
heavyInitialConfiguration();
}
@Override
public void process() {
LOG.info("processing complete.");
}
private void heavyInitialConfiguration() {
LOG.info("Loading initial configuration...");
}
}
代理对象:
package proxy_pattern;
public class ExpensiveObjectProxy implements ExpensiveObject {
private static ExpensiveObject object;
@Override
public void process() {
if(object == null) {
object = new ExpensiveObjectImpl();
}
object.process();
}
}
测试类:
package test;
import proxy_pattern.ExpensiveObject;
import proxy_pattern.ExpensiveObjectProxy;
public class ProxyPatternTest {
public static void main(String[] args) {
ExpensiveObject expensiveObject = new ExpensiveObjectProxy();
expensiveObject.process();
expensiveObject.process();
}
}
这不仅使得配置文件只加载一次,而且隐藏了内部的具体操作,我们只需调用代理对象的process方法就行了,具体是怎么加载的我们不用关心。
另外,这个代理类中的方法process实在是太像单例模式了。对,就是几乎一样,他们的不同在于目的。单例模式的目的是一个类只有一个对象,而我们代理模式的目的是简化加载过程或者做一些安全检查之类的。