linux cpu占用率如何看
291
2022-11-06
AopUtils详解
在本节中,我们介绍Spring非常重要的一个工具类AopUtils。稍微注意一点就是AopUtils主要是针对于AOP过程中的一些工具方法,还有一个叫做AopProxyUtils,这个工具类是针对怎么去做Proxy的工具方法;
SpringAOP JavaConfig案例准备
因为是作为AOP相关工具类,所以我们需要准备一些基本的测试案例,或者先对Spring AOP测试做一个准备。在这里我们选择基于JavaConfig的配置方式,来准备一个AOP测试案例:
首先使用maven引入依赖,因为只是测试AOP,所以只需要引入Spring的一些基础包即可:
不出意外,首先先准备一个业务接口及其实现:
接下来创建一个用于增强的类,我们使用Annotation的方式完成:
使用@Aspect将该类变成一个Spring能够识别的切面提供类;标记@Component,能够让Spring配置对象扫描到;
接下来创建主配置类:
注意:
1,@Configuration代表这是Spring的java配置类;
2,@ComponentScan代表自动扫描组件,所以Advice类会自动加载;
3,@EnableAspectJAutoProxy,如果要使用annotation的方式完成AOP,必须要在主配置类上添加这个标签,相当于XML中配置的
4,提供被代理的目标对象,EmployeeServiceImpl;
至此,基本的AOP测试环境搭建完成,我们后面的AopUtils测试会基于这个环境进行测试。
判定是否是代理对象相关
1,boolean isJdkDynamicProxy(Object object)
第一个方法,也是使用最多的方法,判定一个对象,是否是代理对象;
我们先来做一个简单测试:
注意:
1,这是一个标准的Spring测试;@ContextConfiguration标签中确定加载的主配置类;
2,测试service应该是代理对象,因为IEmployeeService匹配Advice中定义的前置增强切入点:execution(cn.wolfcode.spring.utilstest.Service.*(..))
这个方法的实现非常简单:
可以看到,做了三个判断:
1,判断是否是SpringProxy类型,Spring中的所有的代理对象,都会实现这个接口,这个接口是一个标记接口:
2,使用JDK的Proxy的isProxyClass方法,判定该对象是否是JDK的代理实现,即是否是基于接口代理;
3,使用ClassUtils的isCglibProxyClass方法判断该对象是否是cglib代理实现;ClassUtils也是Spring中针对反射提供的非常有用的工具类,后面会介绍;
4,为什么要这样判断?因为Spring需要判定这个对象是否是由spring完成的AOP代理;
boolean isJdkDynamicProxy(Object object)
第二个方法,判断是否是JDK代理对象;
简单的测试:
该方法功能很简单,实现也很简单:
类似的还有一个方法:
boolean isCglibProxy(Object object),很明显该方法用于判定一个类是否是cglib完成的代理;我们就不具体测试了;这个方法的实现应该也能很容易想到:
Class> getTargetClass(Object candidate)
这个方法用于获取对象的真实类型;为了完成方法测试,我们增加一个没有接口的组件:
在Advice类中增加一个针对MyComponent的增强:
完成测试代码:
输出:
class cn.wolfcode.springboot.utilstest.MyComponentEnhancerBySpringCGLIB238e719a
class cn.wolfcode.springboot.utilstest.MyComponent
很好理解,因为MyComponent是没有接口的,所以使用cglib完成代理,那么component的类型肯定是代理之后的类型,那我们要想得到真实类型,使用getTargetClass方法完成。
其实该方法的实现也非常简单:
首先,判断该类是否实现了TargetClassAware接口,该接口是SpringAOP代理类去实现的接口,比如通过ProxyFactoryBean实现的代理对象,都会实现这个接口。这个接口中定义了getTargetClass方法,用于获取动态代理对象的原始类型;
如果没有实现TargetClassAware接口,接着判定,如果是Cglib的动态代理实现,则直接返回该类的父类,我们知道,cglib就是通过继承来完成动态代理的,所以代理对象的父类即为真实对象类型;否则,剩下的就是JDK完成的代理,只需要得到本身类型即可,因为这就是接口的类型;
boolean isEqualsMethod(Method method)
boolean isHashCodeMethod(Method method)
boolean isToStringMethod(Method method)
boolean isFinalizeMethod(Method method)
这四个方法很简单,用于判定给定的方法是否是equals方法,hashCode方法,toString方法或者finalize方法。因为在动态代理的时候,都是针对类级别的代理,加入匹配的切入点是 .(..),那么是否是该类所有方法都会被代理?但是Object本身的equals,hashCode,toString,finalize方法,都是需要剔除在代理方法之外的,这四个方法就是来辅助做判断的。如果有兴趣的童鞋可以看一下JdkDynamicAopProxy类中的invoke方法中的相关实现;
Method getMostSpecificMethod(Method method, Class> targetClass)
该方法是一个有趣的方法,他能从代理对象上的一个方法,找到真实对象上对应的方法。举个例子,MyComponent代理之后的对象上的someLogic方法,肯定是属于cglib代理之后的类上的method,使用这个method是没法去执行目标MyComponent的someLogic方法,这种情况下,就可以使用getMostSpecificMethod,找到真实对象上的someLogic方法,并执行真实方法。来写一个测试验证:
打印结果:
代码解释:
m是通过代理对象的class得到的someLogic方法,从打印结果可以非常明显的看出,这个method隶属于MyComponentEnhancerBySpringCGLIB238e719a;那么使用这个方法肯定是没法去执行真实MyComponent的someLogic方法,所以我们执行
传入的方法是代理类的方法,通过targetClass得到真实类型,返回的方法就是MyComponent类上的方法了。
boolean canApply(Pointcut pc, Class> targetClass):判断一个切入点能否匹配一个指定的类型;
boolean canApply(Pointcut pc, Class> targetClass, boolean hasIntroductions):判断一个切入点能否匹配一个指定的类型,是否支持引入匹配;
boolean canApply(Advisor advisor, Class> targetClass):判断一个建议(advisor)能否匹配一个指定的类型;
boolean canApply(Advisor advisor, Class> targetClass, boolean hasIntroductions):判断一个建议(advisor)能否匹配一个指定的类型,是否支持引入匹配;
List
这两组方法也算AOP核心方法了,用于判断一个切入点是否匹配一个类型;
我们先做一个简单的测试:
可以看到,我们创建了一个AspectJExpressionPointcut,看名字也能明白就是使用AspectJ的表达式设置的切入点,去分别测试MyComponent和IEmployeeService,结果和我们预期相同。
两组方法中都有一个hasIntroductions的boolean类型参数,这个参数是用于指定,判定是否包含introduction,如果不包含introduction,匹配会更加的有效;
那什么是introduction?字面意思是引入,其实这是一个AOP概念,简单理解就是使用AOP来改变对象本来的行为,比如使用AOP为一个类动态的添加一个父类,或者额外实现一个接口,甚至增加一个字段等等操作。这种操作使用的场景很有限,但是感觉很牛X,我们就来简单看一个例子,体会一下introduction的概念:
我们额外准备一个接口和实现:
我们的目标是,在不修改IEmployeeService的前提下,为EmployeeServiceImpl额外添加IAddition接口,并使用AdditionImpl作为其实现。意思就是,当我得到EmployeeServiceImpl对象的时候,我可以强转为IAddition接口,并执行additional方法,打印出”out additional…”;
要实现这个需求非常方便,只需要在Advice中添加:
简单解释一下这个代码:
1,public IAddition addition字段,代表我要引入的接口;
2,@DeclareParents标签说明这个接口要引入的目标,其中value=”cn.wolfcode.springboot.utilstest.IEmployeeService+”代表要引入到的目标类是IEmployeeService及其所有子类;
3,defaultImpl代表接口默认的实现,即我们的AdditionImpl类;
到这里,一个基本的introduction就已经配置完成;
接下来看测试代码:
执行测试,输出:
Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args)
执行一个目标方法;这个方法其实就是method.invoke方法的更完善的方法,指在target对象上,使用args参数列表执行method;
我们可以来看看代码的实现:
代码很简单,就是在默认的反射调用之上,对异常做了一些包装,其中使用到了ReflectionUtils,这也是Spring中非常有用的一个工具类,后面介绍;
小结
本节介绍了AopUtils,其中也想补充一下Spring中使用JavaConfig实现AOP和introduction的知识。
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~