这个系列是基于小傅哥的「手撸 Spring」学习的一个记录
目标
AOP 意为面向切面编程,通过预编译的方式和运行期间动态代理实现程序功能的统一维护。这句话可能不太好理解,可以先看下图: 从图中可以看出, AOP 的作用就是在对某些具有共同特点的方法进行拦截,并执行方法拦截器中的功能,完成对原有方法的功能的扩展。 这一次首先是要实现一个简单的切面功能。
设计
首先从代理一个方法,来观察代理方法的过程。
public void test_proxy_method() {
Object targetObj = new UserService();
IUserService proxy = (IUserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), targetObj.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
Object value= method.invoke(targetObj, args);
long end = System.currentTimeMillis();
System.out.println("程序执行时间=" + (end-start) + "ms");
return value;
}
});
proxy.queryUserInfo();
}
在这个代码中,我们对需要 targetObj 接口的实现类进行了代理。在 AOP 的实现过程中,需要对这个过程进行进一步的抽象。
- 抽象出解析表达式的类:表达式中规定了给那些类的那些方法进行增强
- 抽象出代理类生成:可能有不同的代理生成策略,或者是使用 JDK 方式,或者是使用 Cglib 方式
- 抽象出对代理方法的自定义拦截方法:不同的拦截对象可能需要增强的功能不同,有些是统计代码运行时间,有的只是简单增加日志,所以不能把功能都一股脑卸载具体的 invoke 逻辑中。并且这部分是要提供给用户的。
接下来,就可以根据这些点来看一个简易的 AOP 是怎么完成的了。
实现
这个是完成简易 AOP 功能的类图,看起来很复杂,但是仔细解析一下,就发现其实并不是很难。
1. 表达式校验
接口定义
- Pointcut 接口:定义了获取 ClassFilter、MethodMatcher 的两个方法
- ClassFilter 接口:定义 matches(Class clazz) 方法,匹配类
- MethodMatcher 接口:定义 matches(Method method, Class clazz) 方法,匹配方法
public interface PointCut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
}
public interface ClassFilter {
boolean matches(Class<?> clazz);
}
public interface MethodMatcher {
boolean matches(Method method, Class<?> targetClass);
}
AspectJExpressionPointcut 实现类
AspectJExpressionPointcut 这个类继承了上述三个接口,完成了对具体表达式的解析和匹配规则,这里的匹配规则直接调用了 aspectjweaver 的实现。
public class AspectJExpressionPointcut implements PointCut, ClassFilter, MethodMatcher {
private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<>();
static {
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);
}
private final PointcutExpression pointcutExpression;
public AspectJExpressionPointcut(String expression) {
PointcutParser pointCutParse = PointcutParser.
getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(SUPPORTED_PRIMITIVES, this.getClass().getClassLoader());
pointcutExpression = pointCutParse.parsePointcutExpression(expression);
}
@Override
public boolean matches(Class<?> clazz) {
return pointcutExpression.couldMatchJoinPointsInType(clazz);
}
@Override
public boolean matches(Method method, Class<?> targetClass) {
return pointcutExpression.matchesMethodExecution(method).alwaysMatches();
}
@Override
public ClassFilter getClassFilter() {
return this;
}
@Override
public MethodMatcher getMethodMatcher() {
return this;
}
}
在这一步,其实已经完成了对方法的拦截,可以测试一下。
public void test_aop() throws NoSuchMethodException {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut("execution(* cn.wufu.springframework.bean.UserService.*(..))");
Class<UserService> clazz = UserService.class;
Method method = clazz.getDeclaredMethod("queryUserInfo");
System.out.println(pointcut.matches(clazz));
System.out.println(pointcut.matches(method, clazz));
}
2. 生成代理对象
AdvisedSupport 类
这个类主要是持有了被代理对象、方法拦截器、方法匹配器,这是完成代理的重要组成部分,这里将其包装到一个类中,方便后续使用。
自定义的拦截器就是注入到这个类中。
public class AdvisedSupport {
private TargetSource targetSource;
private MethodInterceptor methodInterceptor;
private MethodMatcher methodMatcher;
public MethodInterceptor getMethodInterceptor() {
return methodInterceptor;
}
}
public class TargetSource {
private final Object target;
public TargetSource(Object target) {
this.target = target;
}
public Class<?>[] getTargetClass() {
return this.target.getClass().getInterfaces();
}
public Object getTarget() {
return target;
}
}
代理实现
这里实现两种方式一种是 JDK 方式,一种是 Cglib 方式,所以首先定义一个接口 AopProxy。
public interface AopProxy {
Object getProxy();
}
public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {
private final AdvisedSupport advised;
public JdkDynamicAopProxy(AdvisedSupport advised) {
this.advised = advised;
}
@Override
public Object getProxy() {
return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
advised.getTargetSource().getTargetClass(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) {
MethodInterceptor methodInterceptor = advised.getMethodInterceptor();
return methodInterceptor.invoke(new ReflectiveMethodInvocation(advised.getTargetSource().getTarget(), method, args));
}
return method.invoke(advised.getTargetSource().getTarget(), args);
}
}
public class CglibAopProxy implements AopProxy{
private final AdvisedSupport advised;
public CglibAopProxy(AdvisedSupport advised) {
this.advised = advised;
}
@Override
public Object getProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(advised.getTargetSource().getTarget().getClass());
enhancer.setInterfaces(advised.getTargetSource().getTargetClass());
enhancer.setCallback(new DynamicAdvisedInterceptor(advised));
return enhancer.create();
}
private static class DynamicAdvisedInterceptor implements MethodInterceptor {
private final AdvisedSupport advised;
public DynamicAdvisedInterceptor(AdvisedSupport advised) {
this.advised = advised;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
CglibMethodInvocation methodInvocation = new CglibMethodInvocation(advised.getTargetSource().getTarget(), method, objects, methodProxy);
if (advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) {
return advised.getMethodInterceptor().invoke(methodInvocation);
}
return methodInvocation.proceed();
}
}
private static class CglibMethodInvocation extends ReflectiveMethodInvocation {
private final MethodProxy methodProxy;
public CglibMethodInvocation(Object target, Method method, Object[] arguments, MethodProxy methodProxy) {
super(target, method, arguments);
this.methodProxy = methodProxy;
}
@Override
public Object proceed() throws Throwable {
return this.methodProxy.invoke(this.target, this.arguments);
}
}
}
这两种方式只需要看他共性的部分:一个是 JDK 方式中的 invoke 方法,一个是 cglib 中的 DynamicAdvisedInterceptor 内部类的 intercept 方法,这两个方法中,如果匹配成功调用 methodInterceptor.invoke() 逻辑。
3. 测试
以下是准备的 IUserservice 接口、UserService 类以及自定义的拦截器 UserServiceInterceptor 。
public interface IUserService {
String queryUserInfo();
String register(String userName);
}
public class UserService implements IUserService{
public String queryUserInfo() {
return "wufu";
}
public String register(String userName) {
try {
Thread.sleep(new Random(1).nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
return "注册用户:" + userName + " success!";
}
}
public class UserServiceInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
long start = System.currentTimeMillis();
try {
return invocation.proceed();
} finally {
System.out.println("监控 - Begin By AOP");
System.out.println("方法名称:" + invocation.getMethod());
System.out.println("方法耗时:" + (System.currentTimeMillis() - start) + "ms");
System.out.println("监控 - End\r\n");
}
}
}
@Test
public void test_dynamic() {
IUserService userService = new UserService();
AdvisedSupport advisedSupport = new AdvisedSupport();
advisedSupport.setTargetSource(new TargetSource(userService));
advisedSupport.setMethodInterceptor(new UserServiceInterceptor());
advisedSupport.setMethodMatcher(new AspectJExpressionPointcut("execution(* cn.wufu.springframework.bean.IUserService.*(..))"));
IUserService proxy_jdk = (IUserService) new JdkDynamicAopProxy(advisedSupport).getProxy();
System.out.println("测试结果=" + proxy_jdk.queryUserInfo());
IUserService proxy_cglib = (IUserService) new CglibAopProxy((advisedSupport)).getProxy();
System.out.println("测试结果=" + proxy_cglib.register("wufu 02"));
}
总结
这个部分设计比代码更重要一些,主要关注一下 AOP 的代码结构。
|