1. 简介
代理设计模式,用于无侵入性地增强方法功能。代理模式是指,目标类的方法执行,需要委托给代理类执行,代理类可以在执行目标方法前/后,处理一些其它事情,这样就可以起到增强目标方法的作用,例如,需要在目标方法前获取运行时间。
代理又分为静态代理和动态代理,静态代理的代理类二进制文件是在编译时生成的,然而静态代理的代理类二进制文件是在运行时生成并加载到虚拟机环境的。
下图是典型的静态代理模式类图。
举个栗子。假如有个UserService接口,它拥有一个保存用户的方法。UserServiceImpl类实现了保存用户的方法,此时有个需求是统计保存用户这个方法的运行时间,通过静态代理的方式实现该功能如下:
清单:UserService.java
public interface UserService {
void saveUser();
}
清单:UserServiceImpl.java
public class UserServiceImpl implements UserService {
public void saveUser() {
System.out.println("用户数据已经保存");
}
}
清单:方法增强处理器
//方法增强处理器
public interface MethodEnhanceHandler {
/**
* 抛出异常时调用
*/
void doThrowing(Exception e);
/**
* 目标方法执行前调用
*/
void doBefore();
/**
* 目标方法执行后调用
*/
void doAfter();
}
清单:代理类
public class UserServiceProxy implements UserService {
private final MethodEnhanceHandler methodEnhanceHandler;
private final UserService target;
public UserServiceProxy(UserService target, MethodEnhanceHandler methodEnhanceHandler) {
this.target = target;
this.methodEnhanceHandler = methodEnhanceHandler;
}
@Override
public void saveUser() {
try {
if (methodEnhanceHandler != null) {
methodEnhanceHandler.doBefore();
}
target.saveUser();
} catch (Exception e) {
if (methodEnhanceHandler != null) {
methodEnhanceHandler.doThrowing(e);
}
} finally {
if (methodEnhanceHandler != null) {
methodEnhanceHandler.doAfter();
}
}
}
}
清单:client
public class Main {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
UserService proxy = new UserServiceProxy(userService, new MethodEnhanceHandler() {
public void doThrowing(Exception e) {
System.out.println("方法出错了");
}
public void doBefore() {
System.out.println("记录方法运行前的时间戳");
}
public void doAfter() {
System.out.println("记录方法运行后的时间戳");
}
});
proxy.saveUser();
}
}
上面是流行的AOP切面编程的一种实现方式。如果要按照上述的方式使用代理模式,那么真实角色必须是实现已经存在的,并将其作为代理对象的内部属性。但是实际使用时,一个真实角色必须对应一个代理角色,但如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色,该如何使用代理呢?这个问题可以通过Java的动态代理类来解决。
在JDK中,提供了一种实现动态代理的方案,但速度慢,而且,它是基于接口的,在下个章节中,我们通过一个例子来感性认识。在开源界提供了几种动态代理库,其中著名并广泛使用的有两种,一种是cglib,在spring中使用它作为动态代理的实现方案,它的底层基于ASM实现;另一种是javassist。
为了让大家可以感性认识JDK/CGLIB/JAVASSIST三种动态代理方案的使用,下面提个小需求,就是实现一个简单的AOP。
2. AOP的简单实现
2.1 需求
有个Person类,它拥有医生的技能(会诊),和办公室工作人员的技能(打印文件),然后利用前面提供的方法增强器,在方法执行前、执行后、抛异常时,执行相应的方法处理。
清单:方法增强处理器
//方法增强处理器
public interface MethodEnhanceHandler {
/**
* 抛出异常时调用
*/
void doThrowing(Exception e);
/**
* 目标方法执行前调用
*/
void doBefore();
/**
* 目标方法执行后调用
*/
void doAfter();
}
清单
public interface DoctorSkill {
void consult();
}
清单:
public interface OfficerSkill {
void print(String fileName);
}
清单:
public class Person implements DoctorSkill, OfficerSkill {
private String name;
@Override
public void consult() {
System.out.println("会诊病人");
}
@Override
public void print(String fileName) {
System.out.println("打印文件:" + fileName);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2.2 基于JDK
清单:
public class JDKDynamicProxy {
public static Object createProxy(Object target, MethodEnhanceHandler methodEnhanceHandler) {
return Proxy.newProxyInstance(JDKDynamicProxy.class.getClassLoader(),
target.getClass().getInterfaces(),
new InternalInvocationHandler(target, methodEnhanceHandler));
}
private static class InternalInvocationHandler implements InvocationHandler {
private MethodEnhanceHandler methodEnhanceHandler;
private Object target;
public InternalInvocationHandler(Object target, MethodEnhanceHandler methodEnhanceHandler) {
this.target = target;
this.methodEnhanceHandler = methodEnhanceHandler;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object retVal = null;
try {
if (methodEnhanceHandler != null) {
methodEnhanceHandler.doBefore();
}
retVal = method.invoke(target, args);
} catch (Exception e) {
if (methodEnhanceHandler != null) {
methodEnhanceHandler.doThrowing(e);
}
} finally {
if (methodEnhanceHandler != null) {
methodEnhanceHandler.doAfter();
}
}
return retVal;
}
}
}
清单:
public class Main {
public static void main(String[] args) {
Person person = new Person();
// DoctorSkill proxy = (DoctorSkill) JDKDynamicProxy.createProxy(person, new MyMethodEnhanceHandler());
// proxy.consult(); //会诊
OfficerSkill proxy = (OfficerSkill) JDKDynamicProxy.createProxy(person, new MyMethodEnhanceHandler());
proxy.print("the cat"); //打印文件
}
private static class MyMethodEnhanceHandler implements MethodEnhanceHandler {
public void doThrowing(Exception e) {
System.out.println("AOP invoked throw a exception");
}
public void doBefore() {
System.out.println("AOP before method invoked");
}
public void doAfter() {
System.out.println("AOP after method invoked");
}
}
}
在客户端Main.java 代码中,基于JDk的动态代理,是无法同时调用医生的技能和办公室的技能的,因为代理对象是基于接口实现的。
2.3 基于CGLIB
清单:代理
package com.github.thinwonton.java.showcase.proxy.cglib;
import com.github.thinwonton.java.showcase.proxy.MethodEnhanceHandler;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibDynamicProxy implements MethodInterceptor {
private MethodEnhanceHandler methodEnhanceHandler;
private CglibDynamicProxy(MethodEnhanceHandler methodEnhanceHandler) {
this.methodEnhanceHandler = methodEnhanceHandler;
}
public static Object createProxy(final Class target, MethodEnhanceHandler methodEnhanceHandler) {
//cglib 中加强器,用来创建动态代理
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target);
CglibDynamicProxy cglibDynamicProxy = new CglibDynamicProxy(methodEnhanceHandler);
enhancer.setCallback(cglibDynamicProxy);
return enhancer.create();
}
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Object retVal = null;
try {
if (methodEnhanceHandler != null) {
methodEnhanceHandler.doBefore();
}
retVal = proxy.invokeSuper(obj, args);
} catch (Exception e) {
if (methodEnhanceHandler != null) {
methodEnhanceHandler.doThrowing(e);
}
} finally {
if (methodEnhanceHandler != null) {
methodEnhanceHandler.doAfter();
}
}
return retVal;
}
}
清单:客户端
public class Main {
public static void main(String[] args) {
Person proxy = (Person) CglibDynamicProxy.createProxy(Person.class, new MyMethodEnhanceHandler());
proxy.consult();
proxy.print("the dog");
}
private static class MyMethodEnhanceHandler implements MethodEnhanceHandler {
public void doThrowing(Exception e) {
System.out.println("AOP invoked throw a exception");
}
public void doBefore() {
System.out.println("AOP before method invoked");
}
public void doAfter() {
System.out.println("AOP after method invoked");
}
}
}
2.3 基于javassist
清单:代理
package com.github.thinwonton.java.showcase.proxy.javassist;
import com.github.thinwonton.java.showcase.proxy.MethodEnhanceHandler;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;
import javassist.util.proxy.ProxyObject;
import java.lang.reflect.Method;
public class JavassistDynamicProxy {
public static Object createProxy(Class<?> target, MethodEnhanceHandler methodEnhanceHandler)
throws IllegalAccessException, InstantiationException {
ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(target);
// //设置过滤器,判断哪些方法调用需要被拦截
// factory.setFilter(new MethodFilter() {
// public boolean isHandled(Method m) {
// return true;
// }
// });
Class proxyClass = factory.createClass();
Object proxy = proxyClass.newInstance();
//设置拦截器
ProxyObject proxyObject = (ProxyObject) proxy;
proxyObject.setHandler(new InternalMethodHandler(methodEnhanceHandler));
return proxy;
}
private static class InternalMethodHandler implements MethodHandler {
private MethodEnhanceHandler methodEnhanceHandler;
public InternalMethodHandler(MethodEnhanceHandler methodEnhanceHandler) {
this.methodEnhanceHandler = methodEnhanceHandler;
}
public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
Object retVal = null;
try {
if (methodEnhanceHandler != null) {
methodEnhanceHandler.doBefore();
}
retVal = proceed.invoke(self, args);
} catch (Exception e) {
if (methodEnhanceHandler != null) {
methodEnhanceHandler.doThrowing(e);
}
} finally {
if (methodEnhanceHandler != null) {
methodEnhanceHandler.doAfter();
}
}
return retVal;
}
}
}
清单:客户端
public class Main {
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
Person proxy = (Person) JavassistDynamicProxy.createProxy(Person.class,new MyMethodEnhanceHandler());
proxy.consult();
proxy.print("the pig");
}
private static class MyMethodEnhanceHandler implements MethodEnhanceHandler {
public void doThrowing(Exception e) {
System.out.println("AOP invoked throw a exception");
}
public void doBefore() {
System.out.println("AOP before method invoked");
}
public void doAfter() {
System.out.println("AOP after method invoked");
}
}
}