AOP是什么?如何实现AOP?
动态代理
@Test
public void test_proxy_class() {
IUserService userService = (IUserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{IUserService.class}, (proxy, method, args) -> "你被代理了!");
String result = userService.queryUserInfo();
System.out.println("测试结果:" + result);
}
测试结果
测试结果:你被代理了!
Proxy.newProxyInstance
方法。如何给符合规则的方法做代理?怎么做完代理方法的案例后,把类的职责拆分出来?
Proxy
,其实代理方法主要是使用到方法拦截器类处理方法的调用 MethodInterceptor#invoke
,而不是直接使用 invoke
方法中的入参 Method method
进行 method.invoke(targetObj, args)
。org.aspectj.weaver.tools.PointcutParser
处理拦截表达式 execution(* com.lino.springframework.test.bean.IUserService.*(..))
,有了方法代理和处理拦截,就可以设计出一个 AOP 的雏形了。
<dependency>
<groupId>aopalliancegroupId>
<artifactId>aopallianceartifactId>
<version>1.0version>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.7version>
dependency>
spring-step-11
|-src
|-main
| |-java
| |-com.lino.springframework
| |-aop
| | |-aspectj
| | | |-AspectJExpressionPointcut.java
| | |-framework
| | | |-AopProxy.java
| | | |-Cglib2AopProxy.java
| | | |-JdkDynamicAopProxy.java
| | | |-ReflectiveMethodInvocation.java
| | |-AdvisedSupport.java
| | |-ClassFilter.java
| | |-MethodMatcher.java
| | |-Pointcut.java
| | |-TargetSource.java
| |-beans
| | |-factory
| | | |-config
| | | | |-AutowireCapableBeanFactory.java
| | | | |-BeanDefinition.java
| | | | |-BeanFactoryPostProcessor.java
| | | | |-BeanPostProcessor.java
| | | | |-BeanReference.java
| | | | |-ConfigurableBeanFactory.java
| | | | |-SingletonBeanRegistry.java
| | | |-support
| | | | |-AbstractAutowireCapableBeanFactory.java
| | | | |-AbstractBeabDefinitionReader.java
| | | | |-AbstractBeabFactory.java
| | | | |-BeabDefinitionReader.java
| | | | |-BeanDefinitionRegistry.java
| | | | |-CglibSubclassingInstantiationStrategy.java
| | | | |-DefaultListableBeanFactory.java
| | | | |-DefaultSingletonBeanRegistry.java
| | | | |-DisposableBeanAdapter.java
| | | | |-FactoryBeanRegistrySupport.java
| | | | |-InstantiationStrategy.java
| | | | |-SimpleInstantiationStrategy.java
| | | |-support
| | | | |-XMLBeanDefinitionReader.java
| | | |-Aware.java
| | | |-BeanClassLoaderAware.java
| | | |-BeanFactory.java
| | | |-BeanFactoryAware.java
| | | |-BeanNameAware.java
| | | |-ConfigurableListableBeanFactory.java
| | | |-DisposableBean.java
| | | |-FactoryBean.java
| | | |-HierarcgicalBeanFactory.java
| | | |-InitializingBean.java
| | | |-ListableBeanFactory.java
| | |-BeansException.java
| | |-PropertyValue.java
| | |-PropertyValues.java
| |-context
| | |-event
| | | |-AbstractApplicationEventMulticaster.java
| | | |-ApplicationContextEvent.java
| | | |-ApplicationEventMulticaster.java
| | | |-ContextclosedEvent.java
| | | |-ContextRefreshedEvent.java
| | | |-SimpleApplicationEventMulticaster.java
| | |-support
| | | |-AbstractApplicationContext.java
| | | |-AbstractRefreshableApplicationContext.java
| | | |-AbstractXmlApplicationContext.java
| | | |-ApplicationContextAwareProcessor.java
| | | |-ClassPathXmlApplicationContext.java
| | |-ApplicationContext.java
| | |-ApplicationContextAware.java
| | |-ApplicationEvent.java
| | |-ApplicationEventPublisher.java
| | |-ApplicationListener.java
| | |-ConfigurableApplicationContext.java
| |-core.io
| | |-ClassPathResource.java
| | |-DefaultResourceLoader.java
| | |-FileSystemResource.java
| | |-Resource.java
| | |-ResourceLoader.java
| | |-UrlResource.java
| |-util
| | |-ClassUtils.java
|-test
|-java
|-com.lino.springframework.test
|-bean
| |-IUserService.java
| |-UserService.java
| |-UserServiceInterceptor.java
|-ApiTest.java
|-resources
|-spring.xml
AspectJExpressionPointcut
的核心功能主要依赖于 aspectj
组件并处理 Pointcut、ClassFilter、MethodMatcher
接口实现,专门用于处理类和方法的匹配过滤操作。AopProxy
是代理的抽象对象,它的实现主要是基于 JDK 的代理和 Cglib 代理。ClassFilter.java
package com.lino.springframework.aop;
/**
* @description: 类匹配接口
*/
public interface ClassFilter {
/**
* 匹配类
*
* @param clazz 类类型
* @return 是否匹配类
*/
boolean matches(Class<?> clazz);
}
MethodMatcher.java
package com.lino.springframework.aop;
import java.lang.reflect.Method;
/**
* @description: 匹配方法接口
*/
public interface MethodMatcher {
/**
* 匹配方法
*
* @param method 匹配方法
* @param targetClass 类
* @return 是否匹配方法
*/
boolean matches(Method method, Class<?> targetClass);
}
Pointcut.java
package com.lino.springframework.aop;
/**
* @description: 切点表达式接口
*/
public interface Pointcut {
/**
* 获取切点中类匹配类
*
* @return 类匹配类
*/
ClassFilter getClassFilter();
/**
* 获取切点中匹配方法
*
* @return 匹配方法
*/
MethodMatcher getMethodMatcher();
}
ClassFilter、MethodMatcher
两个类,这两个接口获取都是切点表达式提供的内容。AspectJExpressionPointcut.java
package com.lino.springframework.aop.aspectj;
import com.lino.springframework.aop.ClassFilter;
import com.lino.springframework.aop.MethodMatcher;
import com.lino.springframework.aop.Pointcut;
import org.aspectj.weaver.tools.PointcutExpression;
import org.aspectj.weaver.tools.PointcutParser;
import org.aspectj.weaver.tools.PointcutPrimitive;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
/**
* @description: 切点表达式实现类
*/
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 pointcutParser = PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(SUPPORTED_PRIMITIVES, this.getClass().getClassLoader());
pointcutExpression = pointcutParser.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;
}
}
Pointcut, ClassFilter, MethodMatcher
,三个接口定义方法,同时这个类主要是对 aspectj
包提供的表达式校验方法使用。matches
:
pointcutExpression.couldMatchJoinPointsInType(clazz)
pointcutExpression.matchesMethodExecution(method).alwaysMatches()
TargetSource.java
package com.lino.springframework.aop;
/**
* @description: 被代理的目标对象
*/
public class TargetSource {
private final Object target;
public TargetSource(Object target) {
this.target = target;
}
/**
* 获取目标对象列表
*
* @return 目标对象列表
*/
public Class<?>[] getTargetClass() {
return this.target.getClass().getInterfaces();
}
/**
* 获取目标对象
*
* @return 目标对象
*/
public Object getTarget() {
return this.target;
}
}
AdvisedSupport.java
package com.lino.springframework.aop;
import org.aopalliance.intercept.MethodInterceptor;
/**
* @description: 包装切面通知信息
*/
public class AdvisedSupport {
/**
* 被代理的目标对象
*/
private TargetSource targetSource;
/**
* 方法拦截器
*/
private MethodInterceptor methodInterceptor;
/**
* 方法匹配器(检查目标方法是否符合通知条件)
*/
private MethodMatcher methodMatcher;
public TargetSource getTargetSource() {
return targetSource;
}
public void setTargetSource(TargetSource targetSource) {
this.targetSource = targetSource;
}
public MethodInterceptor getMethodInterceptor() {
return methodInterceptor;
}
public void setMethodInterceptor(MethodInterceptor methodInterceptor) {
this.methodInterceptor = methodInterceptor;
}
public MethodMatcher getMethodMatcher() {
return methodMatcher;
}
public void setMethodMatcher(MethodMatcher methodMatcher) {
this.methodMatcher = methodMatcher;
}
}
AdvisedSupport
:主要用于把代理、拦截、匹配的各项属性包装到一个类中,方便在 Proxy
实现类进行使用。TargetSource
:是一个目标对象,在目标对象类中提供 Object 入参属性,以及获取目标类 TargetClass 信息。MethodInterceptor
:是一个具体拦截方法实现类,由用户自己实现 MethodInterceptor#invoke
方法,做具体的实现。MethodMatcher
:是一个匹配方法,这个对象由 AspectJExpressionPointcut
提供服务。ReflectiveMethodInvocation.java
package com.lino.springframework.aop.framework;
import org.aopalliance.intercept.MethodInvocation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
/**
* @description: 反射方法调用
* @author: lingjian
* @createDate: 2022/12/2 9:44
*/
public class ReflectiveMethodInvocation implements MethodInvocation {
/**
* 目标对象
*/
protected final Object target;
/**
* 方法
*/
protected final Method method;
/**
* 入参
*/
protected final Object[] arguments;
public ReflectiveMethodInvocation(Object target, Method method, Object[] arguments) {
this.target = target;
this.method = method;
this.arguments = arguments;
}
@Override
public Method getMethod() {
return method;
}
@Override
public Object[] getArguments() {
return arguments;
}
@Override
public Object proceed() throws Throwable {
return method.invoke(target, arguments);
}
@Override
public Object getThis() {
return target;
}
@Override
public AccessibleObject getStaticPart() {
return method;
}
}
AopProxy.java
package com.lino.springframework.aop.framework;
/**
* @description: AOP代理接口
*/
public interface AopProxy {
/**
* 获取代理对象
*
* @return 代理对象
*/
Object getProxy();
}
JdkDynamicAopProxy.java
package com.lino.springframework.aop.framework;
import com.lino.springframework.aop.AdvisedSupport;
import org.aopalliance.intercept.MethodInterceptor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @description: JDK 动态代理
*/
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);
}
}
AopProxy
、InvocationHandler
,这样就可以把代理对象 getProxy
和反射调用方法 invoke
分开处理了。getProxy
方法中的是代理一个对象的操作,需要提供入参 ClassLoader
、AdvisedSupport
和当前类 this
,因为这个类提供了 invoke
方法。invoke
方法中主要处理匹配的方法后,使得用户自己提供的方法拦截实现,做反射调用 methodInterceptor.invoke
。ReflectiveMethodInvocation
,其实它就是一个入参的包装信息,提供了入参对象:目标对象、方法、入参。Cglib2AopProxy.java
package com.lino.springframework.aop.framework;
import com.lino.springframework.aop.AdvisedSupport;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @description: Cglib代理
*/
public class Cglib2AopProxy implements AopProxy {
private final AdvisedSupport advised;
public Cglib2AopProxy(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);
}
}
}
Enhancer
代理的类可以在运行期间为接口使用底层 ASM 字节码增强技术处理对象的代理对象生成,因此被代理类不需要实现任何接口。Enhancer#setCallback
中处理,用户自己的新增的拦截处理。
DynamicAdvisedInterceptor#intercept
匹配方法后做了相应的反射操作。IUserService.java
package com.lino.springframework.test.bean;
/**
* @description: 用户接口
*/
public interface IUserService {
/**
* 查询用户信息
*
* @return 用户信息
*/
String queryUserInfo();
/**
* 注册用户
*
* @param userName 用户名
* @return 用户信息
*/
String register(String userName);
}
UserService.java
package com.lino.springframework.test.bean;
import java.util.Random;
/**
* @description: 用户接口实现类
*/
public class UserService implements IUserService {
@Override
public String queryUserInfo() {
try {
Thread.sleep(new Random(1).nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
return "张三,100001,杭州";
}
@Override
public String register(String userName) {
try {
Thread.sleep(new Random(1).nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
return "注册用户:" + userName + " success!";
}
}
UserServiceInterceptor.java
package com.lino.springframework.test.bean;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
* @description: 用户拦截器
*/
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");
}
}
}
MethodInterceptor
接口的 invoke
方法,使用方式于 Spring AOP 类似,也是包装 invocation.proceed()
放行,并在 finally
中添加监控信息。ApiTest.java
@Test
public void test_proxy_method() {
// 目标对象
Object targetObj = new UserService();
// AOP代理
IUserService proxy = (IUserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), targetObj.getClass().getInterfaces(), new InvocationHandler() {
// 方法匹配器
MethodMatcher methodMatcher = new AspectJExpressionPointcut("execution(* com.lino.springframework.test.bean.IUserService.*(..))");
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (methodMatcher.matches(method, targetObj.getClass())) {
// 方法拦截器
MethodInterceptor methodInterceptor = invocation -> {
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");
}
};
return methodInterceptor.invoke(new ReflectiveMethodInvocation(targetObj, method, args));
}
return method.invoke(targetObj, args);
}
});
String result = proxy.queryUserInfo();
System.out.println("测试结果:" + result);
}
UserService
当成目标对象,对类中的所有方法进行拦截添加监控信息打印处理。Proxy.newProxyInstance
,有方法的匹配 MethodMatcher
,有反射的调用 invoke(Object proxy, Method method, Object[] args)
,也有用户自己拦截方法后的操作。测试结果
监控 - Begin By AOP
方法名称:public abstract java.lang.String com.lino.springframework.test.bean.IUserService.queryUserInfo()
方法耗时:86ms
监控 - End
测试结果:张三,100001,杭州
UserService#queryUserInfo
方法进行了拦截监控操作。拆解案例
ReflectiveMethodInvocation
的使用,它目前已经是实现 MethodInvocation
接口的一个包装后的类。
ApiTest.java
@Test
public void test_aop() throws NoSuchMethodException {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut("execution(* com.lino.springframework.test.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));
}
测试结果
true
true
ApiTest.java
@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(* com.lino.springframework.test.bean.IUserService.*(..))"));
// 代理对象(JDKDynamicAopProxy)
IUserService proxy_jdk = (IUserService) new JdkDynamicAopProxy(advisedSupport).getProxy();
// 测试调用
System.out.println("测试结果:" + proxy_jdk.queryUserInfo());
// 代理对象(Cglib2AopProxy)
IUserService proxy_cglib = (IUserService) new Cglib2AopProxy(advisedSupport).getProxy();
// 测试调用
System.out.println("测试结果:" + proxy_cglib.register("小零"));
}
AdvisedSupport
:包装了目标对象、用户自己实现的拦截方法以及方法匹配表达式。JdkDynamicAopProxy
、Cglib2AopProxy
,两个不同方式实现的代理类,看看是否可以成功拦截方法。测试结果
监控 - Begin By AOP
方法名称:public abstract java.lang.String com.lino.springframework.test.bean.IUserService.queryUserInfo()
方法耗时:90ms
监控 - End
测试结果:张三,100001,杭州
监控 - Begin By AOP
方法名称:public java.lang.String com.lino.springframework.test.bean.UserService.register(java.lang.String)
方法耗时:99ms
监控 - End
测试结果:注册用户:小零 success!
Proxy#newProxyInstance
、MethodInterceptor#invoke
的使用验证切面核心原理以及再把功能拆解到 Spring 框架实现中。