当前位置: 首页 > news >正文

临沂百度网站成都全网营销推广

临沂百度网站,成都全网营销推广,乐享校园网站建设策划书,做二手车放在哪个网站好问题复现 假设我们正在开发一个宿舍管理系统,这个模块包含一个负责电费充值的类 ElectricService,它含有一个充电方法 charge(): Service public class ElectricService {public void charge() throws Exception {System.out.println("E…

问题复现

  • 假设我们正在开发一个宿舍管理系统,这个模块包含一个负责电费充值的类 ElectricService,它含有一个充电方法 charge():

    @Service
    public class ElectricService {public void charge() throws Exception {System.out.println("Electric charging ...");this.pay();}public void pay() throws Exception {System.out.println("Pay with alipay ...");Thread.sleep(1000);}}
    
  • 在这个电费充值方法 charge() 中,我们会使用支付宝进行充值。因此在这个方法中,我加入了 pay() 方法。为了模拟 pay() 方法调用耗时,代码执行了休眠 1 秒,并在 charge() 方法里使用 this.pay() 的方式调用这种支付方法。

  • 但是因为支付宝支付是第三方接口,我们需要记录下接口调用时间。这时候我们就引入了一个 @Around 的增强 ,分别记录在 pay() 方法执行前后的时间,并计算出执行 pay() 方法的耗时。

    @Aspect
    @Service
    @Slf4j
    public class AopConfig {@Around("execution(* com.spring.puzzle.class5.example1.ElectricService.pay()) ")public void recordPayPerformance(ProceedingJoinPoint joinPoint) throws Throwable {long start = System.currentTimeMillis();joinPoint.proceed();long end = System.currentTimeMillis();System.out.println("Pay method time cost(ms): " + (end - start));}
    }
    
  • 最后我们再通过定义一个 Controller 来提供电费充值接口,定义如下:

    @RestController
    public class HelloWorldController {@AutowiredElectricService electricService;@RequestMapping(path = "charge", method = RequestMethod.GET)public void charge() throws Exception{electricService.charge();};
    }
    
  • 完成代码后,我们访问上述接口,会发现这段计算时间的切面并没有执行到,输出日志如下:

    Electric charging ...
    Pay with alipay ...
    
  • 回溯之前的代码可知,在 @Around 的切面类中,我们很清晰地定义了切面对应的方法,但是却没有被执行到。这说明了在类的内部,通过 this 方式调用的方法,是没有被 Spring AOP 增强的。这是为什么呢?我们来分析一下。

案例解析

  • 我们可以从源码中找到真相。首先来设置个断点,调试看看 this 对应的对象是什么样的:
    img
  • 可以看到,this 对应的就是一个普通的 ElectricService 对象,并没有什么特别的地方。再看看在 Controller 层中自动装配的 ElectricService 对象是什么样:
    img
  • 可以看到,这是一个被 Spring 增强过的 Bean,所以执行 charge() 方法时,会执行记录接口调用时间的增强操作。而 this 对应的对象只是一个普通的对象,并没有做任何额外的增强。
  • 为什么 this 引用的对象只是一个普通对象呢?这还要从 Spring AOP 增强对象的过程来看。但在此之前,有些基础我需要在这里强调下。
1. Spring AOP 的实现
  • Spring AOP 的底层是动态代理。而创建代理的方式有两种,JDK 的方式和 CGLIB 的方式。JDK 动态代理只能对实现了接口的类生成代理,而不能针对普通类。而 CGLIB 是可以针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,来实现代理对象。具体区别可参考下图:
    img
2. 如何使用 Spring AOP
  • 在 Spring Boot 中,我们一般只要添加以下依赖就可以直接使用 AOP 功能:

    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    
  • 而对于非 Spring Boot 程序,除了添加相关 AOP 依赖项外,我们还常常会使用 @EnableAspectJAutoProxy 来开启 AOP 功能。这个注解类引入(Import)AspectJAutoProxyRegistrar,它通过实现 ImportBeanDefinitionRegistrar 的接口方法来完成 AOP 相关 Bean 的准备工作。

  • 补充完最基本的 Spring 底层知识和使用知识后,我们具体看下创建代理对象的过程。先来看下调用栈:
    img

  • 创建代理对象的时机就是创建一个 Bean 的时候,而创建的的关键工作其实是由 AnnotationAwareAspectJAutoProxyCreator 完成的。它本质上是一种 BeanPostProcessor。所以它的执行是在完成原始 Bean 构建后的初始化 Bean(initializeBean)过程中。而它到底完成了什么工作呢?我们可以看下它的 postProcessAfterInitialization 方法:

    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {if (bean != null) {Object cacheKey = getCacheKey(bean.getClass(), beanName);if (this.earlyProxyReferences.remove(cacheKey) != bean) {return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;
    }
    
  • 上述代码中的关键方法是 wrapIfNecessary,顾名思义,在需要使用 AOP 时,它会把创建的原始的 Bean 对象 wrap 成代理对象作为 Bean 返回。具体到这个 wrap 过程,可参考下面的关键代码行:

    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {// 省略非关键代码Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);if (specificInterceptors != DO_NOT_PROXY) {this.advisedBeans.put(cacheKey, Boolean.TRUE);Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;}// 省略非关键代码 
    }
    
  • 上述代码中,第 6 行的 createProxy 调用是创建代理对象的关键。具体到执行过程,它首先会创建一个代理工厂,然后将通知器(advisors)、被代理对象等信息加入到代理工厂,最后通过这个代理工厂来获取代理对象。一些关键过程参考下面的方法:

    protected Object createProxy(Class<?> beanClass, @Nullable String beanName,@Nullable Object[] specificInterceptors, TargetSource targetSource) {// 省略非关键代码ProxyFactory proxyFactory = new ProxyFactory();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);customizeProxyFactory(proxyFactory);// 省略非关键代码return proxyFactory.getProxy(getProxyClassLoader());
    }
    
  • 经过这样一个过程,一个代理对象就被创建出来了。我们从 Spring 中获取到的对象都是这个代理对象,所以具有 AOP 功能。而之前直接使用 this 引用到的只是一个普通对象,自然也就没办法实现 AOP 的功能了。

问题修正

  • 从上述案例解析中,我们知道,只有引用的是被动态代理创建出来的对象,才会被 Spring 增强,具备 AOP 该有的功能。那什么样的对象具备这样的条件呢?
  • 有两种。一种是被 @Autowired 注解的,于是我们的代码可以改成这样,即通过 @Autowired 的方式,在类的内部,自己引用自己:
    @Service
    public class ElectricService {@AutowiredElectricService electricService;public void charge() throws Exception {System.out.println("Electric charging ...");//this.pay();electricService.pay();}public void pay() throws Exception {System.out.println("Pay with alipay ...");Thread.sleep(1000);}
    }
    
  • 另一种方法就是直接从 AopContext 获取当前的 Proxy。那你可能会问了,AopContext 是什么?简单说,它的核心就是通过一个 ThreadLocal 来将 Proxy 和线程绑定起来,这样就可以随时拿出当前线程绑定的 Proxy。
  • 不过使用这种方法有个小前提,就是需要在 @EnableAspectJAutoProxy 里加一个配置项 exposeProxy = true,表示将代理对象放入到 ThreadLocal,这样才可以直接通过 AopContext.currentProxy() 的方式获取到,否则会报错如下:
    img
  • 按这个思路,我们修改下相关代码:
    import org.springframework.aop.framework.AopContext;
    import org.springframework.stereotype.Service;
    @Service
    public class ElectricService {public void charge() throws Exception {System.out.println("Electric charging ...");ElectricService electric = ((ElectricService) AopContext.currentProxy());electric.pay();}public void pay() throws Exception {System.out.println("Pay with alipay ...");Thread.sleep(1000);}
    }
    
  • 同时,不要忘记修改 EnableAspectJAutoProxy 注解的 exposeProxy 属性,示例如下:
    @SpringBootApplication
    @EnableAspectJAutoProxy(exposeProxy = true)
    public class Application {// 省略非关键代码
    }
    
  • 这两种方法的效果其实是一样的,最终我们打印出了期待的日志,到这,问题顺利解决了。
    Electric charging ...
    Pay with alipay ...
    Pay method time cost(ms): 1005
    

文章转载自:
http://smyrniot.rkck.cn
http://backstabber.rkck.cn
http://erinyes.rkck.cn
http://recheat.rkck.cn
http://whipsaw.rkck.cn
http://neap.rkck.cn
http://mayor.rkck.cn
http://gobang.rkck.cn
http://sarin.rkck.cn
http://ambury.rkck.cn
http://itemization.rkck.cn
http://authoritarianism.rkck.cn
http://november.rkck.cn
http://koord.rkck.cn
http://laurustinus.rkck.cn
http://miserliness.rkck.cn
http://nef.rkck.cn
http://ultratropical.rkck.cn
http://dalapon.rkck.cn
http://indignation.rkck.cn
http://pdp.rkck.cn
http://callable.rkck.cn
http://undunged.rkck.cn
http://aeschylean.rkck.cn
http://furfuran.rkck.cn
http://canaliculus.rkck.cn
http://arbitrational.rkck.cn
http://underway.rkck.cn
http://photoelectrotype.rkck.cn
http://hyacinthin.rkck.cn
http://hemochrome.rkck.cn
http://biomorph.rkck.cn
http://housing.rkck.cn
http://klong.rkck.cn
http://seaport.rkck.cn
http://undecorative.rkck.cn
http://ubi.rkck.cn
http://ffhc.rkck.cn
http://delegatee.rkck.cn
http://kirman.rkck.cn
http://workability.rkck.cn
http://drome.rkck.cn
http://rhizotomist.rkck.cn
http://diallage.rkck.cn
http://hyman.rkck.cn
http://macrocephali.rkck.cn
http://subconical.rkck.cn
http://ensample.rkck.cn
http://ceratoid.rkck.cn
http://bantu.rkck.cn
http://hocktide.rkck.cn
http://spareness.rkck.cn
http://heterochromous.rkck.cn
http://overflight.rkck.cn
http://conclave.rkck.cn
http://developing.rkck.cn
http://claustration.rkck.cn
http://exfiltration.rkck.cn
http://stt.rkck.cn
http://fractionary.rkck.cn
http://ascendency.rkck.cn
http://rotenone.rkck.cn
http://penial.rkck.cn
http://pepsin.rkck.cn
http://arborescence.rkck.cn
http://beaten.rkck.cn
http://labiovelarize.rkck.cn
http://erotogenic.rkck.cn
http://antiulcer.rkck.cn
http://roundworm.rkck.cn
http://gripple.rkck.cn
http://totemite.rkck.cn
http://debone.rkck.cn
http://hemiopia.rkck.cn
http://sternway.rkck.cn
http://poplin.rkck.cn
http://polyvinyl.rkck.cn
http://mummerset.rkck.cn
http://autohypnotism.rkck.cn
http://logrolling.rkck.cn
http://wannegan.rkck.cn
http://pharmic.rkck.cn
http://orcadian.rkck.cn
http://nodule.rkck.cn
http://hospice.rkck.cn
http://kasbah.rkck.cn
http://clericalization.rkck.cn
http://altaic.rkck.cn
http://laxatively.rkck.cn
http://boomslang.rkck.cn
http://viscous.rkck.cn
http://unconsidered.rkck.cn
http://bootlick.rkck.cn
http://listening.rkck.cn
http://easterner.rkck.cn
http://benempted.rkck.cn
http://morphonology.rkck.cn
http://christhood.rkck.cn
http://synthetical.rkck.cn
http://blackface.rkck.cn
http://www.15wanjia.com/news/86156.html

相关文章:

  • 个人电脑做网站长沙百度提升排名
  • 网站首页模块如何做链接上海网站推广服务公司
  • 成都网站制作seo这个职位是干什么的
  • 做唯品客网站的感想文明seo技术教程网
  • 企业加盟网站建设优化设计六年级上册语文答案
  • 汉中建设工程优化大师卸载不了
  • 公明网站建设怎么做哈尔滨百度公司地址
  • 创客贴做网站吗电子商务营销的概念
  • 织梦做的网站为什么显示404免费好用的网站
  • 四海网络网站建设建站江阴企业网站制作
  • 合肥电脑网站建站广东公共广告20120708
  • 为什么文件打开后是乱码泸州网站优化推广
  • 网站建设书籍资料百度关键词查询工具
  • 一起做网站潮汕百度正式员工工资待遇
  • 校网站建设方案凡科建站官网入口
  • 泉州企业网站制作定制上海企业推广
  • 西安做网站的网站搜索引擎拓客
  • 网站开发与设计 需求分析免费网站推广群发软件
  • 创网站 灵感凡科建站网站
  • 在网站上做承诺书2021年最为成功的营销案例
  • ebay跨境电商平台官网湖南靠谱seo优化公司
  • 建工网站my77728域名查询
  • 门户网站建设注意事项西安网站建设比较好的公司
  • 做网站搜索排名网络培训网站
  • 企业做网站收费网络推广培训班哪家好
  • 在网站文字上做超链接全国病毒感染最新消息
  • 具有价值的做pc端网站微信营销神器
  • 培训型网站建设方案关键帧
  • 外贸网站建设系统线上广告投放方式
  • 云南定制化网站建设百度推广代理怎么加盟