原创

Spring利用BeanPostProcessor接口实现自定义AOP功能

文章源码:https://github.com/Sniper2016/SpringStudy

学完 Spring 的 ioc、aop 源码,又完成了自定义 xml 注解,是否觉得自己已经很牛逼了?本文带你使用 spring 的 BeanPostProcessor 接口手写一个 aop 框架。

1.必备知识

以下知识在前几篇文章已经详细说明了。

  • cgLib 动态代理
  • BeanPostProcessor 接口在框架中的调用时间
  • Aware 相关接口的作用
  • 通过注解使用 spring 的 aop 功能

2.编写流程:

  • 编写一个自定义注解 MyAspect,声明带有该注解的类都具有自定义切面功能
  • 编写一个通知类 AbstractAdvisor,检测需要被拦截的 bean 及方法
  • 编写自定义切面代理类 CustomizeAspectProxy,让我们自定义的切面和通知相关信息注入到 spring 中,使得切面相关数据生效
  • 编写测试方法,查看结果

3.实例

MyAspect

package cn.gameboys.diy.aop;
import org.springframework.stereotype.Component;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface MyAspect {
 String value() default "";

 String pointCut();
}

AbstractAdvisor

package cn.gameboys.diy.aop;

import java.lang.reflect.Method;
import java.util.regex.Pattern;

/**
 *
* @Description:
* @author: www.gameboys.cn
* @date:2020年6月22日 下午3:22:42
 */
public abstract class AbstractAdvisor implements Proceed {

 public static final int AFTER_ORDER = 0;

 public static final int AROUND_ORDER = 1;

 public static final int BEFORE_ORDER = 2;

 private Pattern pointCutPattern;

 protected String pointCut;

 protected Object aspectObject;

 protected Method aspectMethod;

 protected int order;

 public AbstractAdvisor(Object aspectObject, Method aspectMethod) {
  pointCut = aspectObject.getClass().getAnnotation(MyAspect.class).pointCut();
  pointCutPattern = Pattern.compile(pointCut);
  this.aspectObject = aspectObject;
  this.aspectMethod = aspectMethod;
 }

 public boolean isMatch(Class<?> matchClass, Method method) {
  return pointCutPattern.matcher(matchClass.getName() + "." + method.getName()).find();
 }

 public int getOrder() {
  return order;
 }
}

CustomizeAspectProxy

package cn.gameboys.diy.aop;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cglib.core.SpringNamingPolicy;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
 *
* @Description:
* @author: www.gameboys.cn
* @date:2020年6月22日 下午3:22:42
 */

public class CustomizeAspectProxy implements BeanPostProcessor, ApplicationContextAware {

 private ApplicationContext applicationContext;

 private List<AbstractAdvisor> advisorList;

 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  return bean;
 }

 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  buildAdvisor();
  Map<Method, List<AbstractAdvisor>> matchAdvisorMap = matchAdvisor(bean);
  if (matchAdvisorMap.isEmpty()) {
   return bean;
  } else {
   Enhancer enhancer = new Enhancer();
   enhancer.setSuperclass(bean.getClass());
   enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
   enhancer.setCallback(new MethodInterceptorImpl(matchAdvisorMap));
   return enhancer.create();
  }
 }

 private Map<Method, List<AbstractAdvisor>> matchAdvisor(Object bean) {
  Class<?> beanClass = bean.getClass();
  Method[] methods = beanClass.getMethods();
  if (methods == null) {
   return Collections.emptyMap();
  }
  Map<Method, List<AbstractAdvisor>> methodListMap = new HashMap<Method, List<AbstractAdvisor>>();
  for (Method method : methods) {
   for (AbstractAdvisor abstractAdvisor : advisorList) {
    if (!abstractAdvisor.isMatch(bean.getClass(), method)) {
     continue;
    }
    List<AbstractAdvisor> advisorList = methodListMap.get(method);
    if (advisorList == null) {
     advisorList = new LinkedList<AbstractAdvisor>();
     methodListMap.put(method, advisorList);
    }
    advisorList.add(abstractAdvisor);
   }
  }
  return methodListMap;
 }

 private void buildAdvisor() {
  if (advisorList != null) {
   return;
  }
  synchronized (this) {
   if (advisorList != null) {
    return;
   }
   String[] beanNames = applicationContext.getBeanDefinitionNames();
   advisorList = new ArrayList<AbstractAdvisor>();
   for (String beanName : beanNames) {
    Class<?> beanClass = applicationContext.getType(beanName);
    MyAspect myAspect = beanClass.getAnnotation(MyAspect.class);
    if (myAspect == null) {
     continue;
    }
    Method[] methods = beanClass.getDeclaredMethods();
    if (methods == null) {
     continue;
    }
    Object bean = applicationContext.getBean(beanName);
    List<AbstractAdvisor> beanAdvisorList = new ArrayList<AbstractAdvisor>(methods.length);
    for (Method method : methods) {
     if (method.getName().equals("before")) {
      beanAdvisorList.add(new MethodInvocation.BeforeAdvisor(bean, method));
     } else if (method.getName().equals("around")) {
      beanAdvisorList.add(new MethodInvocation.AroundAdvisor(bean, method));
     } else if (method.getName().equals("after")) {
      beanAdvisorList.add(new MethodInvocation.AfterAdvisor(bean, method));
     }
    }
    advisorList.addAll(beanAdvisorList);
   }
   Collections.sort(advisorList, new Comparator<AbstractAdvisor>() {
    public int compare(AbstractAdvisor o1, AbstractAdvisor o2) {
     return o1.getOrder() - o2.getOrder();
    }
   });
  }
 }

 public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  this.applicationContext = applicationContext;
 }
}

MethodInvocation

package cn.gameboys.diy.aop;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.util.List;

/**
 *
* @Description:
* @author: www.gameboys.cn
* @date:2020年6月22日 下午3:22:42
 */
public class MethodInvocation implements Proceed {
 private List<AbstractAdvisor> advisorList;

 private Object sourceObject;

 private Method sourceMethod;

 private Object[] sourceParameters;

 private MethodProxy sourceMethodProxy;

 private int advisorIndex = -1;

 public MethodInvocation(Object o, Method method, Object[] objects, MethodProxy methodProxy,
   List<AbstractAdvisor> advisorList) {
  this.sourceObject = o;
  this.sourceMethod = method;
  this.sourceParameters = objects;
  this.sourceMethodProxy = methodProxy;
  this.advisorList = advisorList;
 }

 public Object proceed(MethodInvocation methodInvocation) throws Throwable {
  if (advisorIndex == advisorList.size() - 1) {
   return sourceMethodProxy.invokeSuper(sourceObject, sourceParameters);
  } else {
   advisorIndex += 1;
   return advisorList.get(advisorIndex).proceed(this);
  }
 }

 public static class AroundAdvisor extends AbstractAdvisor {

  public AroundAdvisor(Object aspectObject, Method aspectMethod) {
   super(aspectObject, aspectMethod);
   order = AbstractAdvisor.AROUND_ORDER;
  }

  public Object proceed(MethodInvocation methodInvocation) throws Throwable {
   Object[] param = { methodInvocation, methodInvocation.sourceParameters };
   return aspectMethod.invoke(aspectObject, param);
  }
 }

 public static class BeforeAdvisor extends AbstractAdvisor {

  public BeforeAdvisor(Object aspectObject, Method aspectMethod) {
   super(aspectObject, aspectMethod);
   order = AbstractAdvisor.BEFORE_ORDER;
  }

  public Object proceed(MethodInvocation methodInvocation) throws Throwable {
   Object[] param = { methodInvocation.sourceParameters };
   aspectMethod.invoke(aspectObject, param);
   return methodInvocation.proceed(methodInvocation);
  }
 }

 public static class AfterAdvisor extends AbstractAdvisor {

  public AfterAdvisor(Object aspectObject, Method aspectMethod) {
   super(aspectObject, aspectMethod);
   order = AbstractAdvisor.AFTER_ORDER;
  }

  public Object proceed(MethodInvocation methodInvocation) throws Throwable {
   methodInvocation.proceed(methodInvocation);
   Object[] param = { methodInvocation.sourceParameters };
   return aspectMethod.invoke(aspectObject, param);
  }
 }
}

MethodInterceptorImpl

package cn.gameboys.diy.aop;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

/**
 *
* @Description:
* @author: www.gameboys.cn
* @date:2020年6月22日 下午3:22:42
 */
public class MethodInterceptorImpl implements MethodInterceptor {
 private Map<Method, List<AbstractAdvisor>> advisorMap;

 public MethodInterceptorImpl(Map<Method, List<AbstractAdvisor>> advisorMap) {
  this.advisorMap = advisorMap;
 }

 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
  List<AbstractAdvisor> advisorList = advisorMap.get(method);
  if (advisorList == null) {
   return methodProxy.invokeSuper(o, objects);
  } else {
   MethodInvocation methodInvocation = new MethodInvocation(o, method, objects, methodProxy, advisorList);
   return methodInvocation.proceed(methodInvocation);
  }
 }
}

Proceed

package cn.gameboys.diy.aop;
/**
 *
* @Description:
* @author: www.gameboys.cn
* @date:2020年6月22日 下午3:22:42
 */
public interface Proceed {
 Object proceed(MethodInvocation methodInvocation) throws Throwable;
}

JavaConfig

package cn.gameboys.diy.aop;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
 *
* @Description:
* @author: www.gameboys.cn
* @date:2020年6月22日 下午3:22:42
 */
@Configuration
@ComponentScan("cn.gameboys.diy.aop")
public class JavaConfig {

 @MyAspect(pointCut = "cn.gameboys.diy.aop.TestBean.test")
 public static class MyAspectClass {

  void before(Object[] args) {
   System.out.println("aop before");
  }

  void after(Object[] args) {
   System.out.println("aop after");
  }

  void around(MethodInvocation methodInvocation, Object[] args) throws Throwable {
   System.out.println("aop around before");
   methodInvocation.proceed(methodInvocation);
   System.out.println("aop around after");
  }
 }

 @Bean
 public CustomizeAspectProxy getCustomizeAspectScan() {
  return new CustomizeAspectProxy();
 }


}

TestBean

package cn.gameboys.diy.aop;

import org.springframework.stereotype.Component;
/**
 *
* @Description:
* @author: www.gameboys.cn
* @date:2020年6月22日 下午3:22:42
 */
@Component
public class TestBean {
 public void test() {
  System.out.println("hello world");
 }
}

CustomizeAspectTest

package cn.gameboys.diy.aop;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 *
* @Description:
* @author: www.gameboys.cn
* @date:2020年6月22日 下午3:22:42
 */
public class CustomizeAspectTest {

 public static void main(String[] args) {
  AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
  annotationConfigApplicationContext.register(JavaConfig.class);
  annotationConfigApplicationContext.refresh();
  TestBean test = annotationConfigApplicationContext.getBean(TestBean.class);
  test.test();
 }

}

正文到此结束