当前位置: 首页 > 后端技术 > Java

SpringAOP源码学习(AOP入门)

时间:2023-04-01 16:17:03 Java

前言开启AOP功能后,AOP逻辑是如何启动的。1.Bean生命周期要说AOP,就必须了解SpringBean的生命周期。AOP的逻辑就是在Bean初始化后的这个阶段开始的。SpringBean的生命周期如上图所示:2.AOP(1)@EnableAspectJAutoProxy这个注解在Spring中用来开启AOP功能。这个注解会在IOC容器中注册一个AutoProxyCreator[AnnotationAwareAspectJAutoProxyCreator,AspectJAwareAdvisorAutoProxyCreator]相关的类。AutoProxyCreator相关类实现了InstantiationAwareBeanPostProcessor接口,即在Bean实例化前后会进行一些AOP的前期准备操作。其中,AbstractAutoProxyCreator实现了BeanFactoryAware接口,是我们事务和aop导入的后处理器的顶层父类;在AbstractAutoProxyCreatorpostProcessBeforeInstantiation实例化之前的方法中,AOP切面Bean会被缓存(Advice、PointCut、Advisor基本bean类型不解析直接跳过);/**AbstractAutoProxyCreator*在我们的Bean创建过程中(实例化前后)还没有调用构造函数实例化bean时调用*我们的aop分析切面和事务分析事务注解都在这里完成*@parambeanClassbean的类对象当前正在创建*@parambeanNamebeanName*@return*@throwsBeansException*/@OverridepublicObjectpostProcessBeforeInstantiation(ClassbeanClass,StringbeanName)throwsBeansException{//构建我们的缓存键ObjectcacheKey=getCacheKey(beanClass,beanName);if(!StringUtils.hasLength(beanName)||!this.targetSourcedBeans.contains(beanName)){//如果解析成功,直接返回if(this.advisedBeans.containsKey(cacheKey)){returnnull;}/***判断是否是基本bean*判断是否应该跳过(aop解析直接解析出我们的切面信息(并保存我们的切面信息),这里不解析事务)*/如果(isInfrastructureClass(beanClass)||shouldSkip(beanClass,beanName)){this.advisedBeans.put(cacheKey,Boolean.FALSE);返回空值;/***这个地方一般不会生成代理对象,除非我们的容器中有TargetSourceCreator,我们的bean需要实现*TargetSource接口*/TargetSourcetargetSource=getCustomTargetSource(beanClass,beanName);if(targetSource!=null){if(StringUtils.hasLength(beanName)){this.targetSourcedBeans.add(beanName);对象[]specificInterceptors=getAdvicesAndAdvisorsForBean(beanClass,beanName,targetSource);对象代理=createProxy(beanClass,beanName,specificInterceptors,targetSource);this.proxyTypes.put(cacheKey,proxy.getClass());返回代理;}返回空值;}(2)创建Proxy代理对象在AbstractAutoProxyCreator的postProcessAfterInitialization后初始化方法【每个Bean创建时都会调用】,会创建AOP代理对象,主要方法:wrapIfNecessary;/**摘要ctAutoProxyCreator*在这个post方法中,这里生成AOP代理对象*@parambeanbean实例*@parambeanNamebean名称*@return*@throwsBeansException*/@OverridepublicObjectpostProcessAfterInitialization(@NullableObjectbean,StringbeanName)throwsBeansException{if(bean!=null){//获取缓存键ObjectcacheKey=getCacheKey(bean.getClass(),beanName);if(this.earlyProxyReferences.remove(cacheKey)!=bean){//找到}}returnbean;}wrapIfNecessary方法如下:/**AbstractAutoProxyCreatorwrapIfNecessary*如有必要,包装给定的bean,即如果它有资格被代理。*@parambean原始bean实例*@parambeanNamebean的名称*@paramcacheKey用于元数据访问的缓存键*@return包装bean的代理,或原样的原始bean实例*/protectedObjectwrapIfNecessary(目的bean,StringbeanName,ObjectcacheKey){//已经被处理了if(StringUtils.hasLength(beanName)&&this.targetSourcedBeans.contains(beanName)){returnbean;}//不需要增强if(Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))){returnbean;}//它是不是一个基本bean?如果(isInfrastructureClass(bean.getClass())||shouldSkip(bean.getClass(),beanName)){this.advisedBeans.put(cacheKey,Boolean.FALSE);返回豆;}//如果我们有一个通知,创建一个代理对象Object[]specificInterceptors=getAdvicesAndAdvisorsForBean(bean.getClass(),beanName,null);//我们合适的notifier不为空if(specificInterceptors!=DO_NOT_PROXY){//表示当前对象已经被代理模式处理过this.advisedBeans.put(cacheKey,Boolean.TRUE);//创建我们真正的代理对象Objectproxy=createProxy(bean.getClass(),beanName,specificInterceptors,newSingletonTargetSource(bean));//加入缓存this.proxyTypes.put(cacheKey,proxy.getClass());返回代理;}this.advisedBeans.put(cacheKey,Boolean.FALSE);返回豆;}创建代理的核心方法:createProxy(基于ProxyFactory)其中真正创建代理对象的方法是ProxyFactory.getProxy方法;/***根据这个工厂中的设置创建一个新的代理。*

可以重复调用。如果我们添加了*或删除了接口,效果会有所不同。可以添加和删除拦截器。*

使用给定的类加载器(如果需要创建代理)。*@paramclassLoader用于创建代理的类加载器*(或{@codenull}用于低级代理设施的默认值)*@return代理对象*/publicObjectgetProxy(@NullableClassLoaderclassLoader){//createAopProxy()用于获取我们的代理工厂returncreateAopProxy().getProxy(classLoader);}createAopProxy方法会决定使用JDK动态代理或者CGLIBBCGLIB代理方法,第一次执行AOP操作时可能会耗时较长,因为需要生成新的字节码文件;一般来说,CGLIB和JDK方法的性能相似;但与使用原生的AspectJ相比,性能还是差了一些。AspectJ基于字节码编译技术,没有借助Proxy对象。/***实现类DefaultAopProxyFactorycreateAopProxy*@paramconfig用于为我们指定我们的advisor信息*该方法用于创建我们的代理对象*所以我们的targetClass对象实现了该接口,而ProxyTargetClass没有指定强制cglib代理,然后就是创建一个jdk代理*我们代理的类没有实现接口,那么会直接去cglib代理*如果我们的ProxyTargetClass指定为false,代理类是接口,那么就会使用jdk代理我们是否还是cglib代理*@return*@throwsAopConfigException*/@OverridepublicAopProxycreateAopProxy(AdvisedSupportconfig)throwsAopConfigException{//判断我们是否强制使用cglib代理ProxyTargetClass=truefasleif(config.isOptimize()||config.isProxyTargetClass()||hasNoUserSuppliedProxyInterfaces(config)){ClasstargetClass=config.getTargetClass();if(targetClass==null){thrownewAopConfigException("TargetSource无法确定目标类:"+"创建代理需要接口或目标。");}//targetClass是使用jdk代理的接口if(targetClass.isInt界面()||Proxy.isProxyClass(targetClass)){返回新的JdkDynamicAopProxy(config);}//cglib代理returnnewObjenesisCglibAopProxy(config);}else{//动态代理returnnewJdkDynamicAopProxy(config);}}//实现类JdkDynamicAopProxygetProxy方法@OverridepublicObjectgetProxy(@NullableClassLoaderclassLoader){if(logger.isDebugEnabled()){logger.debug("CreatingJDKdynamicproxy:targetsourceis"+this.advised.getTargetSource());}Class[]proxiedInterfaces=AopProxyUtils.completeProxiedInterfaces(this.advised,true);findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);返回Proxy.newProxyInstance(classLoader,proxiedInterfaces,this);}/***查找可能在提供的接口集上定义的任何{@link#equals}或{@link#hashCode}方法。*@paramproxiedInterfaces要自省的接口*/privatevoidfindDefinedEqualsAndHashCodeMethods(Class[]proxiedInterfaces){for(ClassproxiedInterface:proxiedInterfaces){Method[]methods=proxiedInterface.getDeclaredMethods();对于(方法方法:方法){如果(AopUtils.isEqualsMethods(方法)){this.equalsDefined=true;}if(AopUtils.isHashCodeMethod(method)){this.hashCodeDefined=true;}如果(this.equalsDefined&&this.hashCodeDefined){返回;}}}}创建的proxy对象会保存在DefaultSingletonBeanRegistry的单例池的singletonObjects中,然后getBean()会在这个单例池中获取代理对象。后续思考AOP的一些优化措施,尤其是围绕通知,当遇到所有AOP性能瓶颈点时,基本都是环绕通知。