代理模式
为其他对象提供一个代理以控制对某个对象的访问。
动态代理
利用反射机制在运行时创建代理类。
举个例子,首先产生一个接口:
public interface ISomeInterface {
String doSomething(String arg);
}
被代理的实际对象(接口的实现类):
public class TargetClass implements ISomeInterface {
public TargetClass() {
}
@Override
public String doSomething(String arg) {
System.out.println(arg);
return arg;
}
public String dealOne(String arg) {
System.out.println("[" + arg + "]");
return null;
}
}
上述的被代理类除了实现接口的方法doSomething(),还另外写了一个方法dealOne()。
JDK代理模式
public class JDKProxy {
public JDKProxy() {
}
@SuppressWarnings("unchecked")
public static <T> T getProxy(T target) {
Class<?> klass = target.getClass();
ClassLoader classLoader = klass.getClassLoader();
Class<?>[] interfaces = klass.getInterfaces();
return (T) Proxy.newProxyInstance(classLoader, interfaces,
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前置拦截!");
Object result = method.invoke(target, args);
System.out.println("后置拦截!");
return result;
}
});
}
}
InvocationHandler接口中的抽象方法:
public Object invoke(Object proxy, Method method, Object[] args)
第一个参数proxy一般是指代理类,method是被代理的方法,如上例中的doSomething(),args为该方法的参数数组。
InvocationHandler是一个接口,里面只有一个抽象方法invoke,这里通过匿名内部类的方式产生InvocationHandler接口的实现类,利用invoke方法提供的参数method,args以及被代理对象target,通过反射机制执行被代理类中的方法。在方法执行之前,我们分别输出了“前置拦截”和“后置拦截”,这里可以看到AOP的影子,AOP 功能模块就是采用动态代理的机制来实现切面编程。
测试一下:
public class JDKTest {
public static void main(String[] args) {
TargetClass target = new TargetClass();
ISomeInterface targetClass = JDKProxy.getProxy(target);
String str = targetClass.doSomething("abcd");
System.out.println(str);
}
}
执行结果:
前置拦截!
abcd
后置拦截!
abcd
从上图可以看出,代理对象只能调用接口方法,我们之前写的dealOne()方法不能被调用。
通过一个JDKProxy类将核心过程进行封装,使用时只需调用getProxy()方法,就可产生代理对象。
JDK代理模式:
1.被代理的类,必须有接口;
2.产生的代理对象,其类型是接口类型的派生类类型;
3.代理对象只能调用接口方法;
CGLIB代理模式
public class CGLIBProxy {
public CGLIBProxy() {
}
@SuppressWarnings("unchecked")
public static <T> T getProxy(T target) {
Class<?> klass = target.getClass();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(klass);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("前置拦截!");
Object result = method.invoke(target, args);
System.out.println("后置拦截!");
return result;
}
});
return (T) enhancer.create();
}
}
测试一下:
public class CGLIBTest {
public static void main(String[] args) {
TargetClass targetClass = new TargetClass();
TargetClass target = CGLIBProxy.getProxy(targetClass);
target.doSomething("abcd");
target.dealOne("1234");
}
}
运行结果:
前置拦截!
abcd
后置拦截!
前置拦截!
[1234]
后置拦截!
从上图可以看出,cglib代理除了可以调用接口方法外,还可以调用dealOne方法。因为CGLIB代理的原理是会创建一个被代理类的子类对象,所以,被代理类中的可继承的方法都可以被调用。如果被代理类存在不能继承的方法,即final修饰的方法,那就无法调用,当然如果被代理类本身是final修饰的类,那就不能被代理。
CGLIB代理模式:
1.被代理的类,不必须实现接口;
2.CGLib代理的原理是,会创建一个被代理类的子类对象;
3.代理对象可以调用除了final修饰的其它所有方法。
最后,我们可以做一个工具类,将JDK代理和CGLIB代理整合起来。
public class MecProxy {
private static final int JDK_PROXY = 0;
private static final int CGLIB_PROXY = 1;
private int proxyType = JDK_PROXY;
private Intercepter intercepter;
private INewInstanceMaker maker;
public MecProxy() {
}
public void setProxyType(int proxyType) {
if (proxyType != JDK_PROXY && proxyType != CGLIB_PROXY) {
this.proxyType = JDK_PROXY;
}
this.proxyType = proxyType;
}
public void setIntercepter(Intercepter intercepter) {
this.intercepter = intercepter;
}
public void setMaker(INewInstanceMaker maker) {
this.maker = maker;
}
@SuppressWarnings("unchecked")
public <T> T getProxy(Class<?> klass) {
Object target = null;
if (maker == null) {
try {
target = klass.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} else {
target = maker.getNewInstance(klass);
}
return (T) getProxy(target);
}
public <T> T getProxy(T target) {
if (proxyType == JDK_PROXY) {
JDKProxy jdkProxy = new JDKProxy();
jdkProxy.setIntercepter(intercepter);
jdkProxy.setMaker(maker);
return jdkProxy.getProxy(target);
}
CGLIBProxy cglibProxy = new CGLIBProxy();
cglibProxy.setIntercepter(intercepter);
cglibProxy.setMaker(maker);
return cglibProxy.getProxy(target);
}
}
Intercepter是一个接口,拦截器,可以在调用被代理对象的方法中设置进去,也就是在上述JDKProxy或者CGLIBProxy中增加Intercepte类型成员,在method.invoke(…)之前完成设置即可.
public interface Intercepter {
boolean before();
Object after(Object result);
}
INewInstanceMaker也是一个接口,创建被代理类对象,因为有的类对象不是直接newInstance获得的.
public interface INewInstanceMaker {
Object getNewInstance(Class<?> klass);
}
相应地,对JDKProxy和CGLIBProxy两个类可以做些修改补充.