当前位置: 首页 > 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://www.hkea.cn/news/755369/

相关文章:

  • 做网站需要备案吗外贸网站推广与优化
  • 独立网站建设流程b站视频推广网站动漫
  • 泰安诚信的网站建设b站推广入口2023年
  • 高校网站建设资料库东莞seo推广公司
  • 电子印章手机在线制作软件四川seo整站优化费用
  • 个人风采网站制作外贸网站平台哪个好
  • 沈阳企业建站谷歌推广和seo
  • .la域名做的网站如何快速推广app
  • 广州优化网站建设怎么用手机制作网站
  • 做微网站的第三方学网络营销
  • 湖南做网站的公司有哪些搜索引擎是什么
  • flash网站管理系统seo优化排名易下拉用法
  • 永年网站建设友链互换平台推荐
  • 企业网站的设计公司网络广告营销的典型案例
  • 高校思政主题网站建设的意义关键词歌词任然
  • 哪里做网站比较快2345网址导航下载桌面
  • 广州建设委员会官方网站凡科建站下载
  • 全球做网站的公司排名百度一下你就知道官网
  • 小企业网站价格免费发链接的网站
  • 买了空间和域名 怎么做网站哪家公司网站做得好
  • 网站备案是否关闭衡阳网站建设公司
  • 遂昌建设局网站个人怎么做网站
  • 软件开发和网站建设网络营销的未来6个发展趋势
  • 做网站一年多少钱免费seo网站推广
  • 智通人才网东莞最新招聘信息官网seo是如何做优化的
  • 个人做跨境电商网站百度地图导航手机版免费下载
  • 阿里云注册网站之后怎么做网站百度联盟是什么
  • 动画制作视频河南网站排名优化
  • 网站关键词怎么做排名掌门一对一辅导官网
  • 现在什么网站做推广比较好网页设计需要学什么