思路

我们使用@Aroud,@Before,@After来确定是在这个类的方法的什么时候进行处理属于是类级别的注释,使用其他的注解(方法级别)来定义要做的事 即如下的样子

@Component
@Around("aroundInvocationHandler") //指定什么时候调用AOP
public class OriginBean {

@Value("${customer.name}")
public String name;

@Polite //指定AOP要干的事
public String hello() {
return "Hello, " + name + ".";
}

public String morning() {
return "Morning, " + name + ".";
}
}

AOP的本质实际上也就是代理,和我们前文所处理的BeanPostProcessor区别在于,BeanPostProcessor在Bean生命周期的固定阶段执行,而AOP在方法调用时动态执行。BeanPostProcessor - 适合Bean级别的处理,AOP - 适合方法级别的横切关注点。

按照廖老师的说法,实现方法拦截的不外乎3种

  1. 编译期:在编译时,由编译器把切面调用编译进字节码,这种方式需要定义新的关键字并扩展编译器,AspectJ就扩展了Java编译器,使用关键字aspect来实现织入;
  2. 类加载器:在目标类被装载到JVM时,通过一个特殊的类加载器,对目标类的字节码重新“增强”;
  3. 运行期:目标对象和切面都是普通Java类,通过JVM的动态代理功能或者第三方库实现运行期动态织入。

最后一种相对简单,不涉及到任何的JVM底层:1.使用Java标准库的动态代理机制,不过仅支持对接口代理,无法对具体类实现代理。2.使用CGLIB或Javassist这些第三方库,通过动态生成字节码,可以对具体类实现代理。

我们仅实现简单的通过注解进行AOP的方式

实现ProxyResolver

动态代理需要两种bean:1.原始的bean,2.拦截器,拦截对应的bean自动调用拦截器实现代理功能
拦截器的接口我们使用java标准库自带的InvocationHandler

应为CGLIB已经不维护了,所以我们选用的是bytebuddy

实现ProxyResovler接口,官网有相关的使用方式

依赖引入

<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>LATEST</version>
</dependency>
public class ProxyResolver {
// ByteBuddy实例:
ByteBuddy byteBuddy = new ByteBuddy();

// 传入原始Bean、拦截器,返回代理后的实例:
public <T> T createProxy(T bean, InvocationHandler handler) {
// 目标Bean的Class类型:
Class<?> targetClass = bean.getClass();
// 动态创建Proxy的Class:
Class<?> proxyClass = this.byteBuddy
// 子类用默认无参数构造方法:
.subclass(targetClass, ConstructorStrategy.Default.DEFAULT_CONSTRUCTOR)
// 拦截所有public方法:
.method(ElementMatchers.isPublic()).intercept(InvocationHandlerAdapter.of(
// 新的拦截器实例:
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 将方法调用代理至原始Bean:
return handler.invoke(bean, method, args);
}
}))
// 生成字节码:
.make()
// 加载字节码:
.load(targetClass.getClassLoader()).getLoaded();
// 创建Proxy实例:
Object proxy;
try {
proxy = proxyClass.getConstructor().newInstance();
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
return (T) proxy;
}
}

注意InvocationHandler有两层:外层的invoke()传入的Object是Proxy实例,内层的invoke()将调用转发至原始Bean。

对于每个注解我们都要编写一个InvocationHandler

public class PoliteInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object bean, Method method, Object[] args) throws Throwable {
// 修改标记了@Polite的方法返回值:
if (method.getAnnotation(Polite.class) != null) {
String ret = (String) method.invoke(bean, args);
if (ret.endsWith(".")) {
ret = ret.substring(0, ret.length() - 1) + "!";
}
return ret;
}
return method.invoke(bean, args);
}
}

实现@Aroud注解

上一节实现了代理类的装配,也就相当于实现AOP的关键点——生成代理已经完全实现了。

注解设计:要有一个值来指定查找什么样子的拦截器。和上节实现Polite注解一样
我们要实现一个InvocationHandler来指定这个注解会做什么

@Component
public class AroundInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 拦截标记了@Polite的方法返回值:
if (method.getAnnotation(Polite.class) != null) {
String ret = (String) method.invoke(proxy, args);
if (ret.endsWith(".")) {
ret = ret.substring(0, ret.length() - 1) + "!";
}
return ret;
}
return method.invoke(proxy, args);
}
}

在IOC容器中装配AOP,AOP的装配是在实例化完成后通过BeanPostProcessor实现的,所以我们需要实现一个AroundProxyBeanPostProcessor。使得在bean实例化完成之后能对@Aroud的类进行装配

public class AroundProxyBeanPostProcessor implements BeanPostProcessor {

Map<String, Object> originBeans = new HashMap<>();

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Class<?> beanClass = bean.getClass();
// 检测@Around注解:
Around anno = beanClass.getAnnotation(Around.class);
if (anno != null) {
String handlerName;
try {
handlerName = (String) anno.annotationType().getMethod("value").invoke(anno);
} catch (ReflectiveOperationException e) {
throw new AopConfigException();
}
Object proxy = createProxy(beanClass, bean, handlerName);
originBeans.put(beanName, bean);
return proxy;
} else {
return bean;
}
}

Object createProxy(Class<?> beanClass, Object bean, String handlerName) {
ConfigurableApplicationContext ctx = (ConfigurableApplicationContext) ApplicationContextUtils.getRequiredApplicationContext();
BeanDefinition def = ctx.findBeanDefinition(handlerName);
if (def == null) {
throw new AopConfigException();
}
Object handlerBean = def.getInstance();
if (handlerBean == null) {
handlerBean = ctx.createBeanAsEarlySingleton(def);
}
if (handlerBean instanceof InvocationHandler handler) {
return ProxyResolver.getInstance().createProxy(bean, handler);
} else {
throw new AopConfigException();
}
}

@Override
public Object postProcessOnSetProperty(Object bean, String beanName) {
Object origin = this.originBeans.get(beanName);
return origin != null ? origin : bean;
}
}

对于Before和After,Around其实已经包含了,我们只需要使用Adapter模式提供两个拦截器模版,实现这两个模板就好

public abstract class BeforeInvocationHandlerAdapter implements InvocationHandler {

public abstract void before(Object proxy, Method method, Object[] args);

@Override
public final Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before(proxy, method, args);
return method.invoke(proxy, args);
}
}
public abstract class AfterInvocationHandlerAdapter implements InvocationHandler {
// after允许修改方法返回值:
public abstract Object after(Object proxy, Object returnValue, Method method, Object[] args);

@Override
public final Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object ret = method.invoke(proxy, args);
return after(proxy, ret, method, args);
}
}

对于类@Aroud注解的拓展

比如说spring中的@Transactional,其逻辑也是AOP所做的,那我们就需要一个TransactionProxyBeanPostProcessor。

所以我们可以使用泛型来增强复用。用泛型代码处理Annotation,先抽象出一个AnnotationProxyBeanPostProcessor

public abstract class AnnotationProxyBeanPostProcessor<A extends Annotation> implements BeanPostProcessor {

Map<String, Object> originBeans = new HashMap<>();
Class<A> annotationClass;

public AnnotationProxyBeanPostProcessor() {
this.annotationClass = getParameterizedType();
}
...
}

后续如果想添加注解,就只需要如下即可了

public class TransactionalProxyBeanPostProcessor extends AnnotationProxyBeanPostProcessor<Transactional> {
}

总结

首先我们先对方法的调用实现了拦截,使得一个对象的所有public方法被设置了拦截器后返回新的代理对象。即在这一步之后,该类的所有方法调用的时候都会去经过实现的拦截器。

搞定了最难的方法拦截之后,接下来的就是对@Aroud要做的事,拦截器的实现,以及何时调用ProxyResolver生成代理的确定了。

接着我们会实现一个实现了InvocationHandler的拦截器,在其中invoke写入逻辑,比如在方法识别到什么注解的时候会干什么事

此时我们有了原始bean,实现了拦截器,这是就可以在IOC中装配AOP了,AOP的装配使用BeanPostProcessor自动装配,所以我们会实现一个BeanPostProcessor,做的事情就是查找是否有Aroud注解,如果有就返回实现Aroud值中拦截器的代理类,这个时候,这个代理类每次调用类的时候就会收到方法拦截器的拦截了。在廖老师的例子里并没有很好的体现Aroud的样式,实际上就是在拦截器的invoke前后添加就是一个环绕了,同理前后也是

生成代理和bean创建中的BeanPostProcessor有关