spring(四) aop

devtools/2024/11/26 12:53:05/

aop自定义配置

aop利用了spring的自定义标签

http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

在bean的配置文件中只要找到了aop命名空间对应的标签,就会通过AopNamespaceHandler进行解析。

    @Overridepublic void init() {// In 2.0 XSD as well as in 2.5+ XSDsregisterBeanDefinitionParser("config", new ConfigBeanDefinitionParser());registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());// Only in 2.0 XSD: moved to context namespace in 2.5+registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());}

aop注册beanPostProcessor

重点来分析一下AspectJAutoProxyBeanDefinitionParser

    public BeanDefinition parse(Element element, ParserContext parserContext) {AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);extendBeanDefinition(element, parserContext);return null;}
    // 主要是注册了这个AnnotationAwareAspectJAutoProxyCreator bean定义public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);}

AnnotationAwareAspectJAutoProxyCreator这个类实现了InstantiationAwareBeanPostProcessor这个接口。按照前文描述的扩展点来说会在bean的实例化周期按照次序调用其具体的方法。主要关注一下postProcessAfterInitialization。 aop就是利用了这个方法,在获取到原对象之后对其进行增强。

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (bean != null) {Object cacheKey = getCacheKey(bean.getClass(), beanName);if (this.earlyProxyReferences.remove(cacheKey) != bean) {return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;}

简短总结:

aop生效的原理

        配置了aop,就会让其注册AnnotationAwareAspectJAutoProxyCreator这个bean定义,其本质利用了spring的扩展点,让每一个bean初始化之后都会调用postProcessAfterInitialization方法,从而返回生成的新的代理对象

aop的postProcess方法

在上述描述中已经知道为什么aop会生效了,其postProcessAfterInitialization方法也就是生成代理的方法,其实生成代理的方法也很好描述,就是找到哪个对象需要代理,代理之后的方法如何实现。

aop中三个概念

Advice

Advice 就是你想要添加到现有代码中的额外逻辑。

Pointcut

决定了“哪里”应用 Advice

Advisor

可以看作是一个将 Advice 与 Pointcut 结合起来的概念

找到advisor

java">// 找到合适的advisor的外层方法
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {// 找到所有的advisorList<Advisor> candidateAdvisors = findCandidateAdvisors();// 找到当前bean对应的advisorList<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);extendAdvisors(eligibleAdvisors);if (!eligibleAdvisors.isEmpty()) {eligibleAdvisors = sortAdvisors(eligibleAdvisors);}return eligibleAdvisors;}
java">// 找到所有的advisorprotected List<Advisor> findCandidateAdvisors() {// 找到实现了advisor接口的对象List<Advisor> advisors = super.findCandidateAdvisors();// 找到所有被aspect标记的对象advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());return advisors;}
java">// 找到注册到bean工厂定义的所有Advisor
public List<Advisor> findAdvisorBeans() {String[] advisorNames = this.cachedAdvisorBeanNames;advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Advisor.class, true, false);for (String name : advisorNames) {List<Advisor> advisors = new ArrayList<Advisor>();advisors.add(this.beanFactory.getBean(name, Advisor.class));}}
java">// 这个是pointcutAdvisor类型的根据当前类匹配advisor的方法
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {if (!pc.getClassFilter().matches(targetClass)) {return false;}MethodMatcher methodMatcher = pc.getMethodMatcher();if (methodMatcher == MethodMatcher.TRUE) {// No need to iterate the methods if we're matching any method anyway...return true;}Set<Class<?>> classes = new LinkedHashSet<Class<?>>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));classes.add(targetClass);for (Class<?> clazz : classes) {Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);for (Method method : methods) {if ((introductionAwareMethodMatcher != null &&introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||methodMatcher.matches(method, targetClass)) {return true;}}}return false;}

代理增强

代理增强有两种方案

一种是基于接口的jdk动态代理、而是基于当前类为父类的cglib动态代理。默认是jdk动态代理,但是如果在aop配置中指定了traget-class,则会默认使用cglib

入口:org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy

java">// 生成动态代理工厂,然后去获取代理类
protected Object createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {if (this.beanFactory instanceof ConfigurableListableBeanFactory) {AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);}ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.copyFrom(this);if (!proxyFactory.isProxyTargetClass()) {if (shouldProxyTargetClass(beanClass, beanName)) {proxyFactory.setProxyTargetClass(true);}else {evaluateProxyInterfaces(beanClass, proxyFactory);}}Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);proxyFactory.addAdvisors(advisors);proxyFactory.setTargetSource(targetSource);return proxyFactory.getProxy(getProxyClassLoader());}

接下来以jdk动态代理为例,看看其是怎么生成代理的,jdk动态代理主要是要实现InvocationHandler接口,然后在调用方法的时候会调用到其invoke方法

java">public Object getProxy(ClassLoader classLoader) {// 找到合适的代理的父接口,然后生成代理Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);}
java">    @Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 构建切面的拦截器链List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);// 通过构造的拦截器链进行方法调用retVal = invocation.proceed();}

通过切面构造拦截器链的过程。主要两种方式

1、通过注册到AdvisorAdapterRegistry

2、advisor本身实现了MethodInterceptor接口

java">AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);public MethodInterceptor[] getInterceptors(Advisor advisor) {List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);Advice advice = advisor.getAdvice();// 自身实现了MethodInterceptor接口if (advice instanceof MethodInterceptor) {interceptors.add((MethodInterceptor) advice);}// 通过注册上来的适配器,转换成对应的interceptorfor (AdvisorAdapter adapter : this.adapters) {if (adapter.supportsAdvice(advice)) {interceptors.add(adapter.getInterceptor(advisor));}}if (interceptors.isEmpty()) {throw new UnknownAdviceTypeException(advisor.getAdvice());}return interceptors.toArray(new MethodInterceptor[interceptors.size()]);}// 默认注册上来的interceptor
public DefaultAdvisorAdapterRegistry() {registerAdvisorAdapter(new MethodBeforeAdviceAdapter());registerAdvisorAdapter(new AfterReturningAdviceAdapter());registerAdvisorAdapter(new ThrowsAdviceAdapter());}

链式调用interceptor方法

java">invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();public Object proceed() throws Throwable {// 调用链执行完了之后,执行被代理对象的方法if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {return invokeJoinpoint();}// 获取下一个执行的拦截器Object interceptorOrInterceptionAdvice =this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);// 执行下一个拦截器的invoke方法return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);}
}


http://www.ppmy.cn/devtools/137118.html

相关文章

Python双向链表、循环链表、栈

一、双向链表 1.作用 双向链表也叫双面链表。 对于单向链表而言。只能通过头节点或者第一个节点出发&#xff0c;单向的访问后继节点&#xff0c;每个节点只能记录其后继节点的信息&#xff08;位置&#xff09;&#xff0c;不能向前遍历。 所以引入双向链表&#xff0c;双…

浅谈网络 | 传输层之套接字Socket

目录 基于 TCP 协议的 Socket 程序调用过程基于 UDP 协议的 Socket 程序函数调用过程服务器如何接入更多的项目构建高并发服务端&#xff1a;从多进程到 IO 多路复用 在前面&#xff0c;我们已经介绍了 TCP 和 UDP 协议&#xff0c;但还没有实践过。接下来这一节&#xff0c;我…

10 —— Webpack打包模式

开发模式&#xff1a;development &#xff1b;场景&#xff1a;本地开发 生产模式&#xff1a;production &#xff1b; 场景&#xff1a;打包上线 这两种模式如何设置给webpack&#xff1a; 方式1.webpack.config.js 配置文件设置mode选项 module.exports { mode:produc…

计算机网络谢希仁第七章课后题【背诵版本】

目录 【7-01】计算机网络都面临哪几种威胁?主动攻击和被动攻击的区别是什么?对于计算机网络的安全措施都有哪些? 【7-02】 试解释以下名词:(2)拒绝服务;(3)访问控制;(4)流量分析;(5)恶意程序。 【7-03】为什么说&#xff0c;计算机网络的安全不仅仅局限于保密性?试举例说…

.net的winfrom程序 窗体透明打开窗体时出现在屏幕右上角

窗体透明&#xff0c; 将Form的属性Opacity&#xff0c;由默认的100% 调整到 80%&#xff0c;这个数字越小越透明(尽量别低于50%&#xff0c;不信你试试看)&#xff01; 打开窗体时出现在屏幕右上角 //构造函数 public frmCalendarList() {InitializeComponent();//打开窗体&…

应用系统开发(14) 涡流检测系统硬件设计

涡流检测整体系统架构 涡流检测系统整体结构如上图 所示,DAC 转换与功率放大电路将数字正弦信号转 换为模拟正弦信号,为涡流探头提供正弦激励。互感式探头由两个线圈组成,一个作为 激励,另一个接收检测信号,AD 转换电路将传感器探头感应到的电压滤波放大,将电 压值调整到…

nn.MultiheadAttention返回的注意力权重和标准的计算注意力权重的区别

#本篇只是随笔小记 试了一下&#xff0c;拿正弦位置编码直接做自注意力&#xff0c;如果手算注意力权重&#xff0c;应该是这样的&#xff1a; attention_scores torch.matmul(pos_x, pos_x.T)/512**0.5 # (seq_len, seq_len)# 应用 softmax 来获得注意力权重 attention_we…

RabbitMQ的交换机总结

1.direct交换机 2.fanout交换机