手写Spring:第12章-基于JDK、Cglib实现AOP切面

文章目录

  • 一、目标:基于JDK、Cglib实现AOP切面
  • 二、设计:基于JDK、Cglib实现AOP切面
  • 三、实现:基于JDK、Cglib实现AOP切面
    • 3.0 引入依赖
    • 3.1 工程结构
    • 3.2 AOP切点表达式和使用以及基于JDK和CGLIB的动态代理类图
    • 3.3 切点表达式
      • 3.3.1 类匹配接口
      • 3.3.2 匹配方法接口
      • 3.3.3 切点表达式接口
      • 3.3.4 实现切点表达式类
    • 3.4 包装切面通知信息
      • 3.4.1 被代理的目标对象
      • 3.4.2 包装切面通知消息
    • 3.5 代理抽象实现(JDK&Cglib)
      • 3.5.1 反射方法调用
      • 3.5.2 AOP代理接口
      • 3.5.3 JDK 动态代理
      • 3.5.4 Cglib代理
  • 四、测试:基于JDK、Cglib实现AOP切面
    • 4.1 添加测试配置
      • 4.1.1 用户接口
      • 4.1.2 用户接口实现类
      • 4.1.3 自定义用户拦截方法
    • 4.2 单元测试
      • 4.2.1 代理方法测试
      • 4.2.2 匹配方法测试
      • 4.2.3 单元测试
  • 五、总结:基于JDK、Cglib实现AOP切面

一、目标:基于JDK、Cglib实现AOP切面

💡 AOP是什么?如何实现AOP?

  • AOP:面向切面编程,通过预编译的方式和运行期间动态代理实现程序功能的统一维护。
    • 其实 AOP 也是 OOP 的延续,在 Spring 框架中是一个非常重要的内容。
    • 使用 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 方法。
  • 那么有了一个基本的思路后,解析来就考虑怎么给方法做代理,而不是代理类?另外怎么去代理所有符合某些规则的所有类中方法?
  • 如果可以代理掉所有类的方法,就可以做一个方法拦截器,给所有被代理的方法添加一些自定义处理。如:打印日志、记录耗时、监控异常等。

二、设计:基于JDK、Cglib实现AOP切面

💡 如何给符合规则的方法做代理?怎么做完代理方法的案例后,把类的职责拆分出来?

  • 这两个功能点的实现,都是以切面的思想进行设计和开发。

在这里插入图片描述

  • 就像你在使用 Spring AOP 一样,只处理一些需要被拦截的方法。在拦截方法后,执行你对方法的扩展操作。
  • 需要先实现一个可以代理方法的 Proxy,其实代理方法主要是使用到方法拦截器类处理方法的调用 MethodInterceptor#invoke,而不是直接使用 invoke 方法中的入参 Method method 进行 method.invoke(targetObj, args)
  • 除了以上的核心功能实现,还需要使用到 org.aspectj.weaver.tools.PointcutParser 处理拦截表达式 execution(* com.lino.springframework.test.bean.IUserService.*(..)),有了方法代理和处理拦截,就可以设计出一个 AOP 的雏形了。

三、实现:基于JDK、Cglib实现AOP切面

3.0 引入依赖

<!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
<dependency><groupId>aopalliance</groupId><artifactId>aopalliance</artifactId><version>1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.7</version>
</dependency>

3.1 工程结构

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

3.2 AOP切点表达式和使用以及基于JDK和CGLIB的动态代理类图

在这里插入图片描述

  • 整个类图就是 AOP 实现核心逻辑的地方,上面部分是关于方法的匹配实现,下面从 AopProxy 开始是关于方法的代理操作。
  • AspectJExpressionPointcut 的核心功能主要依赖于 aspectj 组件并处理 Pointcut、ClassFilter、MethodMatcher 接口实现,专门用于处理类和方法的匹配过滤操作。
  • AopProxy 是代理的抽象对象,它的实现主要是基于 JDK 的代理和 Cglib 代理。

3.3 切点表达式

3.3.1 类匹配接口

ClassFilter.java

package com.lino.springframework.aop;/*** @description: 类匹配接口*/
public interface ClassFilter {/*** 匹配类** @param clazz 类类型* @return 是否匹配类*/boolean matches(Class<?> clazz);
}
  • 定义类匹配类,用于切点好到给定的接口和目标类

3.3.2 匹配方法接口

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);
}
  • 方法匹配,找到表达式范围内匹配下的目标类和方法。

3.3.3 切点表达式接口

Pointcut.java

package com.lino.springframework.aop;/*** @description: 切点表达式接口*/
public interface Pointcut {/*** 获取切点中类匹配类** @return 类匹配类*/ClassFilter getClassFilter();/*** 获取切点中匹配方法** @return 匹配方法*/MethodMatcher getMethodMatcher();
}
  • 切入点接口,定义用于获取 ClassFilter、MethodMatcher 两个类,这两个接口获取都是切点表达式提供的内容。

3.3.4 实现切点表达式类

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);}@Overridepublic boolean matches(Class<?> clazz) {return pointcutExpression.couldMatchJoinPointsInType(clazz);}@Overridepublic boolean matches(Method method, Class<?> targetClass) {return pointcutExpression.matchesMethodExecution(method).alwaysMatches();}@Overridepublic ClassFilter getClassFilter() {return this;}@Overridepublic MethodMatcher getMethodMatcher() {return this;}
}
  • 切点表达式实现了 Pointcut, ClassFilter, MethodMatcher,三个接口定义方法,同时这个类主要是对 aspectj 包提供的表达式校验方法使用。
  • 匹配 matches
    • pointcutExpression.couldMatchJoinPointsInType(clazz)
    • pointcutExpression.matchesMethodExecution(method).alwaysMatches()

3.4 包装切面通知信息

3.4.1 被代理的目标对象

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;}
}

3.4.2 包装切面通知消息

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 提供服务。

3.5 代理抽象实现(JDK&Cglib)

3.5.1 反射方法调用

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;}@Overridepublic Method getMethod() {return method;}@Overridepublic Object[] getArguments() {return arguments;}@Overridepublic Object proceed() throws Throwable {return method.invoke(target, arguments);}@Overridepublic Object getThis() {return target;}@Overridepublic AccessibleObject getStaticPart() {return method;}
}

3.5.2 AOP代理接口

AopProxy.java

package com.lino.springframework.aop.framework;/*** @description: AOP代理接口*/
public interface AopProxy {/*** 获取代理对象** @return 代理对象*/Object getProxy();
}
  • 定义一个标准接口,用于获取代理类。因为具体实现代理的方式可以有 JDK 方式,也可以是 Cglib 方式,所以定义接口会更加方便管理。

3.5.3 JDK 动态代理

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;}@Overridepublic Object getProxy() {return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), advised.getTargetSource().getTargetClass(), this);}@Overridepublic 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);}
}
  • 基于 JDK 实现的代理类,需要实现 AopProxyInvocationHandler,这样就可以把代理对象 getProxy 和反射调用方法 invoke 分开处理了。
  • getProxy 方法中的是代理一个对象的操作,需要提供入参 ClassLoaderAdvisedSupport 和当前类 this,因为这个类提供了 invoke 方法。
  • invoke 方法中主要处理匹配的方法后,使得用户自己提供的方法拦截实现,做反射调用 methodInterceptor.invoke
  • 这里还有一个 ReflectiveMethodInvocation,其实它就是一个入参的包装信息,提供了入参对象:目标对象、方法、入参。

3.5.4 Cglib代理

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;}@Overridepublic 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;}@Overridepublic 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;}@Overridepublic Object proceed() throws Throwable {return this.methodProxy.invoke(this.target, this.arguments);}}
}
  • 基于 Cglib 使用 Enhancer 代理的类可以在运行期间为接口使用底层 ASM 字节码增强技术处理对象的代理对象生成,因此被代理类不需要实现任何接口。
  • 关于扩展用户拦截方法,主要在 Enhancer#setCallback 中处理,用户自己的新增的拦截处理。
    • 这里可以看到 DynamicAdvisedInterceptor#intercept 匹配方法后做了相应的反射操作。

四、测试:基于JDK、Cglib实现AOP切面

4.1 添加测试配置

4.1.1 用户接口

IUserService.java

package com.lino.springframework.test.bean;/*** @description: 用户接口*/
public interface IUserService {/*** 查询用户信息** @return 用户信息*/String queryUserInfo();/*** 注册用户** @param userName 用户名* @return 用户信息*/String register(String userName);
}

4.1.2 用户接口实现类

UserService.java

package com.lino.springframework.test.bean;import java.util.Random;/*** @description: 用户接口实现类*/
public class UserService implements IUserService {@Overridepublic String queryUserInfo() {try {Thread.sleep(new Random(1).nextInt(100));} catch (InterruptedException e) {e.printStackTrace();}return "张三,100001,杭州";}@Overridepublic String register(String userName) {try {Thread.sleep(new Random(1).nextInt(100));} catch (InterruptedException e) {e.printStackTrace();}return "注册用户:" + userName + " success!";}
}
  • UserService 中提供了2个不同方法。查询和注册。

4.1.3 自定义用户拦截方法

UserServiceInterceptor.java

package com.lino.springframework.test.bean;import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;/*** @description: 用户拦截器*/
public class UserServiceInterceptor implements MethodInterceptor {@Overridepublic 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 中添加监控信息。

4.2 单元测试

4.2.1 代理方法测试

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.*(..))");@Overridepublic 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),也有用户自己拦截方法后的操作。
  • 这样看其实就和我们使用的 AOP 非常类似。

测试结果

监控 - Begin By AOP
方法名称:public abstract java.lang.String com.lino.springframework.test.bean.IUserService.queryUserInfo()
方法耗时:86ms
监控 - End测试结果:张三,100001,杭州
  • 从测试结果看,我们已经对 UserService#queryUserInfo 方法进行了拦截监控操作。

拆解案例

在这里插入图片描述

  • 拆解过程参考上图,把代理对象拆解出来,因为它可以是 JDK 的实现也可以是 Cglib 的处理。
  • 方法匹配器操作其实已经是一个单独的实现类了,不过我们还需要把传入的目标对象、方法匹配、拦截方法,都进行统一的包装,方便外部调用时进行一个入参透传。
  • 最后其实就是 ReflectiveMethodInvocation 的使用,它目前已经是实现 MethodInvocation 接口的一个包装后的类。
    • 参数信息包括:调用的对象、调用的方法、调用的入参。

4.2.2 匹配方法测试

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
  • 这是一个匹配方法的测试,可以看看拦截的方法与对应的对象是否匹配。

4.2.3 单元测试

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("小零"));
}
  • 整个案例测试了 AOP 在与 Spring 结合前的核心代码,包括什么是目标对象、怎么组装代理信息、如何调用代理对象等。
  • AdvisedSupport:包装了目标对象、用户自己实现的拦截方法以及方法匹配表达式。
  • 之后就是分别调用 JdkDynamicAopProxyCglib2AopProxy,两个不同方式实现的代理类,看看是否可以成功拦截方法。

测试结果

监控 - 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!
  • AOP 功能定义一样,我们可以通过这样的代理方式、方法匹配和拦截后,在对应的目标方法下,做了拦截操作进行监控信息打印。

五、总结:基于JDK、Cglib实现AOP切面

  • 本文对 Proxy#newProxyInstanceMethodInterceptor#invoke 的使用验证切面核心原理以及再把功能拆解到 Spring 框架实现中。
    • 可以看到一个貌似复杂的技术其实核心内容往往没有太多,但因为需要为了满足后续更多的扩展就需要进行职责解耦和包装,通过这样设计模式的使用,以此让调用方能更加简化,自身也可以不断按需扩展。
  • AOP 的功能实现目前还没有与 Spring 结合,只是对切面技术的一个具体实现,可以先学到:如何处理代理对象、过滤方法、拦截方法,以及使用 JDKCglib 代理的区别。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/125907.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Scala

Scala 介绍 Scala是一种多规范的编程语言&#xff0c;它结合了面向对象编程&#xff08;OOP&#xff09;和函数式编程&#xff08;FP&#xff09;的特征&#xff0c;Scala的名字源于”Scalable language“&#xff0c;意为”可伸缩语言“。2003年开发的&#xff0c;并在JVM&a…

LeetCode 48题: 旋转图像

题目 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]]…

激光切割机在船舶行业的的应用有哪些

我国享有世界工厂的美誉&#xff0c;是全球制造业的主力。然而&#xff0c;在船舶制造的关键技术领域&#xff0c;我国的研发投入不足&#xff0c;技术进步仍滞后&#xff0c;我国高端船舶制造的实力仍显不足。 在我国制造业全面复苏的当前背景下&#xff0c;“精准制作”正构成…

数据库 设计规范数据库设计样例

目录 5 数据库5.1 数据库命名规范5.2 数据库字段命名5.2.1 字段命名规范5.2.2 命名规范5.2.3 待优化命名示例5.2.4 字段类型规范5.2.5数据库中每个字段的规范描述 5.3表设计5.4 参考设计5.4.1 应用场景5.4.2 需求分析5.4.3 设计思路5.4.4 表结构设计5.4.5 缓存策略Q1 冗余设计和…

上手Spring

设置Maven镜像为阿里云 找到Maven的目录所在位置找到conf目录找到settings.xml文件 找到Maven的目录所在位置&#xff1a;去idea 的设置中 直接搜索Maven 找到conf目录 修改Maven本地仓库的地址 地址自定义 修改Maven的镜像为阿里云镜像 <mirror><id>nexus-aliy…

9.8day59

503. 下一个更大元素 II - 力扣&#xff08;LeetCode&#xff09; 知识点&#xff1a;单调栈 42. 接雨水 - 力扣&#xff08;LeetCode&#xff09;

opencv基础: 视频,摄像头读取与保存的常用方法

当然还可以从视频中抓取截图&#xff0c;所以现在聊一下常用的抓取视频截图的的方法。 VideoCapture 方法 cv2.VideoCapture();cv2.VideoCapture( device);cv2.VideoCapture(filename);上面有三种构造方法&#xff0c; 第一种是无法构造方法。 第二种参数device是一个数字。 …

【群智能算法改进】一种改进的鹈鹕优化算法 IPOA算法[2]【Matlab代码#58】

文章目录 【获取资源请见文章第5节&#xff1a;资源获取】1. 原始POA算法2. 改进后的IPOA算法2.1 随机对立学习种群初始化2.2 动态权重系数2.3 透镜成像折射方向学习 3. 部分代码展示4. 仿真结果展示5. 资源获取 【获取资源请见文章第5节&#xff1a;资源获取】 1. 原始POA算法…

企业架构LNMP学习笔记24

学习目标和内容&#xff1a; 1、能够描述高可用HA的作用 2、能够理解VIP的切换&#xff1a;虚拟IP。 3、能够描述keepalived作用&#xff1a;保持活跃。主备的服务器的关系。 4、能够理解主master和备backup服务器关系 5、能够实现主备服务器高可用配置&#xff1a;主服务…

Java——》线程间是如何通信的

推荐链接&#xff1a; 总结——》【Java】 总结——》【Mysql】 总结——》【Redis】 总结——》【Kafka】 总结——》【Spring】 总结——》【SpringBoot】 总结——》【MyBatis、MyBatis-Plus】 总结——》【Linux】 总结——》【MongoD…

鸿蒙系列-如何使用DevEco分析app的性能

如何使用DevEco分析app的性能 性能优化、启动优化、内存优化、FPS监测、性能分析&#x1f9d0; 在鸿蒙OpenHarmony开发过程中&#xff0c;开发者开发的代码&#xff08;Stage 模型&#xff09;通常以调用 ArkUI 框架的代码为主&#xff0c;主要优化的代码部分也在其中&#x…

信息安全保障

文章目录 目录 文章目录 一.信息安全的定义 信息安全的概念 狭义的信息安全概念&#xff1a; 广义的信息安全问题&#xff1a; 信息系统安全问题的根源&#xff1a; 威胁情报 威胁情报的作用&#xff1a; 信息安全的特征 二.信息系统的属性 三.信息安全的视角 国家视角下的信…

LlamaIndex:将个人数据添加到LLM

推荐&#xff1a;使用 NSDT场景编辑器 快速搭建3D应用场景 LlamaIndex是基于大型语言模型&#xff08;LLM&#xff09;的应用程序的数据框架。像 GPT-4 这样的 LLM 是在大量公共数据集上预先训练的&#xff0c;允许开箱即用的令人难以置信的自然语言处理能力。但是&#xff0c;…

HTML的段落中怎么样显示出标签要使用的尖括号<>?

很简单&#xff1a; 符号 < 用 < 替代&#xff1b; 符号 > 用 > 替代。 示例代码如下&#xff1a; <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>HTML中怎样打出尖括号</title> </head> <b…

AP3266 大功率同步降压恒流芯片 双路并用大电流LED车灯驱动线路 过EMC

产品描述 AP3266 是高效率、外围简单、内置功率管的同步降压恒流芯片&#xff0c;适用于4-40V输入的降压LED恒流驱动芯片。输出最大功率可达 40W&#xff0c;最大电流3.6A。AP3266 可通过调节 OVP 端口的分压电阻&#xff0c;设定输出空载电压 保护&#xff0c;避免高压 空载上…

【数据库事务日志碎片原理分析与方案】-深入解析篇.pdf

日志增长与 VLF 文件的个数 通过上面的相关内容的介绍&#xff0c;我们已经知道了日志文件自动的增长会到了一些问 题&#xff0c;而事实确实如此&#xff0c;下面&#xff0c;我们就来更加清楚的看看这些问题。 很显然&#xff0c;我们不希望日志文件任意的增长&#xff0c;…

外汇交易技巧分享:利用MT4交易平台进行精准的外汇技术分析

在外汇交易市场中&#xff0c;技术分析是一种重要的决策工具&#xff0c;能够帮助交易者预测价格走势和制定交易策略。而MT4交易平台作为一种功能强大、广泛应用的交易软件&#xff0c;为交易者提供了丰富的技术分析工具和功能。本文将与大家分享几个利用MT4交易平台(可在mtw.s…

Revit SDK 介绍:CreateAirHandler 创建户式风管机

前言 这个例子介绍如何通过 API 创建一个户式风管机族的内容&#xff0c;包含几何和接头。 内容 效果 核心逻辑 必须打开机械设备的族模板创建几何实体来表示风管机创建风机的接头 创建几何实体来表示风管机 例子中创建了多个拉伸&#xff0c;下面仅截取一段代码&#xff…

vue2 vuex

一、Vuex 概述 Vuex 是一个 Vue 的 状态管理工具&#xff0c;状态就是数据。 大白话&#xff1a;Vuex 是一个插件&#xff0c;可以帮我们管理 Vue 通用的数据 (多组件共享的数据)。 使用场景 某个状态 在 很多个组件 来使用 (个人信息) 多个组件 共同维护 一份数据 (购物车) …

虚函数、纯虚函数、多态

一.虚函数 在基类的函数前加上virtual关键字&#xff0c;在派生类中重写该函数&#xff0c;运行时将会根据所指对象的实际类型来调用相应的函数&#xff0c;如果对象类型是派生类&#xff0c;就调用派生类的函数&#xff0c;如果对象类型是基类&#xff0c;就调用基类的函数。 …