我认真总结并分析了 Spring 事务失效的十种常见场景

概述

Spring针对Java Transaction API (JTA)、JDBC、Hibernate和Java Persistence API(JPA)等事务 API,实现了一致的编程模型,而Spring的声明式事务功能更是提供了极其方便的事务配置方式,配合Spring Boot的自动配置,大多数Spring Boot项目只需要在方法上标记@Transactional注解,即可一键开启方法的事务性配置。

但是,事务如果没有被正确出,很有可能会导致事务的失效,带来意想不到的数据不一致问题,随后就是大量的人工接入查看和修复数据,该篇主要分享Spring事务在技术上的正确使用方式,避免因为事务处理不当导致业务逻辑产生大量偶发性BUG。

在分析事务失效的常见场景之前,我们先来了解一下:事务的传播类型 和 @Transactionnal 注解的不同属性的含义。

事务的传播类型

//如果有事务, 那么加入事务, 没有的话新建一个(默认)
@Transactional(propagation=Propagation.REQUIRED)
//容器不为这个方法开启事务 
@Transactional(propagation=Propagation.NOT_SUPPORTED)
//不管是否存在事务, 都创建一个新的事务, 原来的挂起, 新的执行完毕, 继续执行老的事务 
@Transactional(propagation=Propagation.REQUIRES_NEW) 
//必须在一个已有的事务中执行, 否则抛出异常
@Transactional(propagation=Propagation.MANDATORY) 
//必须在一个没有的事务中执行, 否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.NEVER) 
//如果其他bean调用这个方法, 在其他bean中声明事务, 那就用事务, 如果其他bean没有声明事务, 那就不用事务
@Transactional(propagation=Propagation.SUPPORTS) 

isolation

该属性用于设置底层数据库的事务隔离级别,事务的隔离级别介绍:

// 读取未提交数据(会出现脏读, 不可重复读) 基本不使用
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
// 读取已提交数据(会出现不可重复读和幻读) Oracle默认
@Transactional(isolation = Isolation.READ_COMMITTED)
// 可重复读(会出现幻读) MySQL默认
@Transactional(isolation = Isolation.REPEATABLE_READ)
// 串行化
@Transactional(isolation = Isolation.SERIALIZABLE)

@Transactionnal注解属性

@Transactional注解可以作用于接口、接口方法、类以及类方法上,它可以通过不同的参数来选择什么类型Exception异常下执行回滚或者不回滚操作。

我认真总结并分析了 Spring 事务失效的十种常见场景

Spring事务失效的场景

1. 事务方法未被Spring管理

如果事务方法所在的类没有注册到Spring IOC容器中,也就是说,事务方法所在类并没有被Spring管理,则Spring事务会失效,举个例子🌰:

/**
 * 商品业务实现层
 *
 * @author: austin
 * @since: 2023/2/10 14:19
 */

public class ProductServiceImpl extends ServiceImpl<ProductMapperProductimplements IProductService {

    @Autowired
    private ProductMapper productMapper;

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateProductStockById(Integer stockCount, Long productId) {
        productMapper.updateProductStockById(stockCount, productId);
    }
}

ProductServiceImpl实现类上没有添加@Service注解,Product的实例也就没有被加载到Spring IOC容器,此时updateProductStockById()方法的事务就会在Spring中失效。

2. 方法使用final类型修饰

有时候,某个方法不想被子类重新,这时可以将该方法定义成final的。普通方法这样定义是没问题的,但如果将事务方法定义成final,例如:

@Service
public class OrderServiceImpl {

    @Transactional
    public final void cancel(OrderDTO orderDTO) {
        // 取消订单
        cancelOrder(orderDTO);
    }
}

OrderServiceImpl的cancel取消订单方法被final修饰符修饰,Spring事务底层使用了AOP,也就是通过JDK动态代理或者cglib,帮我们生成了代理类,在代理类中实现的事务功能。但如果某个方法用final修饰了,那么在它的代理类中,就无法重写该方法,从而无法添加事务功能。这种情况事务就会在Spring中失效。

💡Tips: 如果某个方法是static的,同样无法通过动态代理将方法声明为事务方法。

3. 非public修饰的方法

如果事务方式不是public修饰,此时Spring事务会失效,举个例子🌰:

/**
 * 商品业务实现层
 *
 * @author: austin
 * @since: 2023/2/10 14:19
 */

@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapperProductimplements IProductService {

    @Autowired
    private ProductMapper productMapper;

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    private void updateProductStockById(Integer stockCount, String productId) {
        productMapper.updateProductStockById(stockCount, productId);
    }
}

虽然ProductServiceImpl添加了@Service注解,同时updateProductStockById()方法上添加了@Transactional(propagation = Propagation.REQUIRES_NEW)注解,但是由于事务方法updateProductStockById()被 private 定义为方法内私有,同样Spring事务会失效。

4. 同一个类中的方法相互调用

@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapperOrderimplements IOrderService {
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private ProductMapper productMapper;

    @Override
    public ResponseEntity submitOrder(Order order) {
        // 保存生成订单信息
        long orderNo = Math.abs(ThreadLocalRandom.current().nextLong(1000));
        order.setOrderNo("ORDER_" + orderNo);
        orderMapper.insert(order);

        // 扣减库存
        this.updateProductStockById(order.getProductId(), 1L);
        return new ResponseEntity(HttpStatus.OK);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateProductStockById(Integer num, Long productId) {
        productMapper.updateProductStockById(num, productId);
    }
}

submitOrder()方法和updateProductStockById()方法都在OrderService类中,然而submitOrder()方法没有添加事务注解,updateProductStockById()方法虽然添加了事务注解,这种情况updateProductStockById()会在Spring事务中失效。

5. 方法的事务传播类型不支持事务

如果内部方法的事务传播类型为不支持事务的传播类型,则内部方法的事务同样会在Spring中失效,举个例子:

@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapperOrderimplements IOrderService {
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private ProductMapper productMapper;

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public ResponseEntity submitOrder(Order order) {
        long orderNo = Math.abs(ThreadLocalRandom.current().nextLong(1000));
        order.setOrderNo("ORDER_" + orderNo);
        orderMapper.insert(order);

        // 扣减库存
        this.updateProductStockById(order.getProductId(), 1L);
        return new ResponseEntity(HttpStatus.OK);
    }

 
    /**
     * 扣减库存方法事务类型声明为NOT_SUPPORTED不支持事务的传播
     */

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void updateProductStockById(Integer num, Long productId) {
        productMapper.updateProductStockById(num, productId);
    }
}

6. 异常被内部catch,程序生吞异常

@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapperOrderimplements IOrderService {
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private ProductMapper productMapper;

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public ResponseEntity submitOrder(Order order) {
        long orderNo = Math.abs(ThreadLocalRandom.current().nextLong(1000));
        order.setOrderNo("ORDER_" + orderNo);
        orderMapper.insert(order);

        // 扣减库存
        this.updateProductStockById(order.getProductId(), 1L);
        return new ResponseEntity(HttpStatus.OK);
    }

    /**
     * 扣减库存方法事务类型声明为NOT_SUPPORTED不支持事务的传播
     */

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void updateProductStockById(Integer num, Long productId) {
        try {
            productMapper.updateProductStockById(num, productId);
        } catch (Exception e) {
            // 这里仅仅是捕获异常之后的打印(相当于程序吞掉了异常)
            log.error("Error updating product Stock: {}", e);
        }
    }
}

7. 数据库不支持事务

Spring事务生效的前提是连接的数据库支持事务,如果底层的数据库都不支持事务,则Spring事务肯定会失效的,例如🌰:使用MySQL数据库,选用MyISAM存储引擎,因为MyISAM存储引擎本身不支持事务,因此事务毫无疑问会失效。

8. 未配置开启事务

如果项目中没有配置Spring的事务管理器,即使使用了Spring的事务管理功能,Spring的事务也不会生效,例如,如果你是Spring Boot项目,没有在SpringBoot项目中配置如下代码:

@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
}

如果是以往的Spring MVC项目,如果没有配置下面的代码,Spring事务也不会生效,正常需要在applicationContext.xml文件中,手动配置事务相关参数,比如:

<!-- 配置事务管理器 --> 
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager"> 
    <property name="dataSource" ref="dataSource"></property> 
</bean> 
<tx:advice id="advice" transaction-manager="transactionManager"> 
    <tx:attributes> 
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes> 
</tx:advice> 
<!-- 用切点把事务切进去 --> 
<aop:config> 
    <aop:pointcut expression="execution(* com.universal.ubdk.*.*(..))" id="pointcut"/> 
    <aop:advisor advice-ref="advice" pointcut-ref="pointcut"/> 
</aop:config> 

9. 错误的传播特性

其实,我们在使用@Transactional注解时,是可以指定propagation参数的。

该参数的作用是指定事务的传播特性,目前Spring支持7种传播特性:

  • REQUIRED 如果当前上下文中存在事务,那么加入该事务,如果不存在事务,创建一个事务,这是默认的传播属性值。
  • SUPPORTS 如果当前上下文存在事务,则支持事务加入事务,如果不存在事务,则使用非事务的方式执行。
  • MANDATORY 如果当前上下文中存在事务,否则抛出异常。
  • REQUIRES_NEW 每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。
  • NOT_SUPPORTED 如果当前上下文中存在事务,则挂起当前事务,然后新的方法在没有事务的环境中执行。
  • NEVER 如果当前上下文中存在事务,则抛出异常,否则在无事务环境上执行代码。
  • NESTED 如果当前上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。

如果我们在手动设置propagation参数的时候,把传播特性设置错了,比如:

@Service
public class OrderServiceImpl {

    @Transactional(propagation = Propagation.NEVER)
    public void cancelOrder(UserModel userModel) {
        // 取消订单
        cancelOrder(orderDTO);
        // 还原库存
        restoreProductStock(orderDTO.getProductId(), orderDTO.getProductCount());
    }
}

我们可以看到cancelOrder()方法的事务传播特性定义成了Propagation.NEVER,这种类型的传播特性不支持事务,如果有事务则会抛异常。

10. 多线程调用

在实际项目开发中,多线程的使用场景还是挺多的。如果Spring事务用在多线程场景中使用不当,也会导致事务无法生效。

@Slf4j
@Service
public class OrderServiceImpl {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private MessageService messageService;

    @Transactional
    public void orderCommit(orderModel orderModel) throws Exception {
        orderMapper.insertOrder(orderModel);
        new Thread(() -> {
            messageService.sendSms();
        }).start();
    }
}

@Service
public class MessageService {

    @Transactional
    public void sendSms() {
        // 发送短信
    }
}

通过示例,我们可以看到订单提交的事务方法orderCommit()中,调用了发送短信的事务方法sendSms(),但是发送短信的事务方法sendSms()是另起了一个线程调用的。

这样会导致两个方法不在同一个线程中,从而是两个不同的事务。如果是sendSms()方法中抛了异常,orderCommit()方法也回滚是不可能的。

实际上,Spring的事务是通过ThreadLocal来保证线程安全的,事务和当前线程绑定,多个线程自然会让事务失效。

我认真总结并分析了 Spring 事务失效的十种常见场景

总结

本篇文章主要是介绍Spring事务传播特性,阐明了@Transactional注解属性的使用方式,通过不同的代码示例演示了Spring事务失效的常见场景,如果文章对你有所帮助,欢迎 点赞👍+评论💬+收藏❤

作者:austin流川枫

来源:juejin.cn/post/7198894062534475834

推荐

Java面试题宝典

技术内卷群,一起来学习!!

我认真总结并分析了 Spring 事务失效的十种常见场景

PS:因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。“在看”支持我们吧!

原文始发于微信公众号(Java知音):我认真总结并分析了 Spring 事务失效的十种常见场景

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/106438.html

(2)
Java知音的头像Java知音bm

相关推荐

发表回复

登录后才能评论

评论列表(41条)

  • Prime Outlet Store的头像
    Prime Outlet Store 2023年2月16日 上午11:55

    Fantastic goods from you, man. I have understand your stuff previous to and you are
    just extremely fantastic. I really like what you have acquired
    here, certainly like what you are saying and the way in which you say it.
    You make it entertaining and you still take care
    of to keep it wise. I can’t wait to read far more from you.
    This is really a tremendous site.

  • minneapolis interior remodeling的头像
    minneapolis interior remodeling 2023年2月16日 上午11:56

    Thanks for sharing such a fastidious opinion, article
    is fastidious, thats why i have read it fully

  • trailers repair service minneapolis mn的头像
    trailers repair service minneapolis mn 2023年2月16日 下午12:10

    Hey there! I understand this is kind of off-topic however I had to ask.

    Does managing a well-established blog such as yours
    require a large amount of work? I’m brand new to
    operating a blog however I do write in my journal on a daily basis.
    I’d like to start a blog so I can share my personal experience and feelings online.
    Please let me know if you have any kind of suggestions or tips for brand new aspiring bloggers.
    Appreciate it!

  • slot Gacor malam ini的头像
    slot Gacor malam ini 2023年2月16日 下午12:31

    hey there and thank you for your info – I’ve certainly picked up anything
    new from right here. I did however expertise a few technical points using this web site, since I experienced to reload the site a lot of times previous to I could get it to load correctly.

    I had been wondering if your web host is OK? Not that I’m complaining, but
    slow loading instances times will sometimes affect your placement in google and can damage your
    high quality score if advertising and marketing with Adwords.

    Anyway I’m adding this RSS to my e-mail and could look out for a lot more of your respective exciting content.
    Ensure that you update this again soon.

    • 小半的头像
      小半 2023年2月16日 下午12:42

      @slot Gacor malam iniRecently, the number of visits has suddenly increased. I am trying to do some optimization, hoping to bring you a better use experience.

  • offsidetavernnyc.com的头像
    offsidetavernnyc.com 2023年2月16日 下午2:02

    I am really grateful to the holder of this web page who has shared this enormous article at at this place.

    my homepage bar offside tavern New York; offsidetavernnyc.com,

  • animal rescue的头像
    animal rescue 2023年2月16日 下午2:37

    Goood post. I learn something totally new and challenging on websites I stumbleupon onn a daily basis.
    It’s always interesting to read through articles from other authors and practice something from their websites.

    My homepage … animal rescue

  • Sports Betting的头像
    Sports Betting 2023年2月16日 下午2:40

    I think this is one of the most important info for me.

    And i’m glad reading your article. But should remark on few general things, The web site style is ideal, the articles is really
    excellent : D. Good job, cheers

  • Call Girls kolkata Escort Service Available的头像
    Call Girls kolkata Escort Service Available 2023年2月16日 下午2:48

    Thanks for a marvelous posting! I seriously enjoyed reading it, you may be a great author.I will ensure that
    I bookmark your blog and definitely will come back
    in the foreseeable future. I want to encourage you continue your great job, have a nice afternoon!

  • minnesota new home builders的头像
    minnesota new home builders 2023年2月16日 下午2:59

    I got this website from my buddy who shared with
    me concerning this site and now this time I am browsing this site and reading very informative articles at this place.

  • personalized logo srixon golf balls的头像
    personalized logo srixon golf balls 2023年2月16日 下午3:39

    Hey terrific blog! Does running a blog such as this
    take a large amount of work? I’ve virtually no expertise in computer programming however I
    was hoping to start my own blog in the near future.
    Anyhow, should you have any recommendations or tips for new blog owners please share.
    I understand this is off subject but I just wanted to ask.

    Appreciate it!

  • Uptown Carpet Repair的头像
    Uptown Carpet Repair 2023年2月16日 下午3:54

    Hi this is somewhat of off topic but I was wondering if blogs use WYSIWYG editors or if you have
    to manually code with HTML. I’m starting a blog soon but have no coding skills so I wanted to
    get advice from someone with experience. Any help would be greatly appreciated!

  • goggle straps的头像
    goggle straps 2023年2月16日 下午4:16

    Magnificent web site. Lots of helpful info here. I am sending it to some buddies
    ans also sharing in delicious. And of course, thanks to your sweat!

  • freestyle libre tape的头像
    freestyle libre tape 2023年2月16日 下午4:32

    Thankfulness to my father who told me on the topic of this weblog, this weblog is actually
    amazing.

  • fast skin的头像
    fast skin 2023年2月16日 下午4:40

    Your way of describing the whole thing in this article is truly fastidious,
    all be capable of easily know it, Thanks a lot.

  • finis的头像
    finis 2023年2月16日 下午7:53

    Hi there, this weekend is nice in support of me, since this moment
    i am reading this impressive informative article here at my house.

  • Website的头像
    Website 2023年2月16日 下午8:05

    First of all I want to say fantastic blog! I had a quick question in which
    I’d like to ask if you don’t mind. I was interested to find out how you center yourself and clear your mind before writing.
    I’ve had difficulty clearing my thoughts in getting my thoughts out.
    I truly do take pleasure in writing however it just seems like
    the first 10 to 15 minutes are lost simply just trying to figure out how to begin.
    Any suggestions or hints? Cheers!

  • made america fire pit的头像
    made america fire pit 2023年2月16日 下午8:08

    It’s great that you are getting ideas from this article as
    well as from our argument made at this place.

  • kick boards的头像
    kick boards 2023年2月16日 下午8:14

    I constantly emailed this weblog post page to all my associates,
    since if like to read it next my friends will too.

  • kombi Servisi的头像
    kombi Servisi 2023年2月16日 下午8:21

    I simply could not go away your web site prior
    to suggesting that I really loved the standard info a
    person supply for your guests? Is gonna be again often in order to check up on new posts

  • idnpoker88.cc的头像
    idnpoker88.cc 2023年2月16日 下午8:27

    I’m extremely inspired with your writing abilities as neatly as with the layout in your weblog.
    Is this a paid topic or did you modify it yourself? Anyway keep
    up the nice high quality writing, it is rare to peer a great weblog like this one today..

  • idn poker 88的头像
    idn poker 88 2023年2月16日 下午8:38

    What’s up, its good piece of writing on the topic of media
    print, we all know media is a impressive source of data.

  • http://ivistroy.ru/的头像
    http://ivistroy.ru/ 2023年2月16日 下午9:11

    You made some decent points there. I checked on the web
    to learn more about the issue and found most
    people will go along with your views on this website.

  • Tranh gỗ cao cấp的头像
    Tranh gỗ cao cấp 2023年2月16日 下午9:29

    Today, I went to the Ьeachfront with my chilԀren. I found a
    seɑ shell and gaνe it too my 4 ʏеar old
    daughter and said “You can hear the ocean if you put this to your ear.”
    She put the shell to her ear and screamed.

    There was a hermit crab inside and it pijched her ear.

    Sһe never wants to go back! LoL I know this is entirely
    off topic but I hadd to tеll someone!

    Have a look at myy blog post – Tranh gỗ cao cấp

  • osg-777.biz的头像
    osg-777.biz 2023年2月16日 下午9:30

    You need to be a part of a contest for one of the greatest blogs on the web.
    I am going to recommend this web site!

  • טלגראס כיוונים ירושלים的头像
    טלגראס כיוונים ירושלים 2023年2月16日 下午9:33

    Greetings from Ohio! I’m bored to tears at work
    so I decided to browse your blog on my iphone during lunch break.
    I enjoy the information you provide here
    and can’t wait to take a look when I get home. I’m surprised at how quick your
    blog loaded on my mobile .. I’m not even using WIFI, just 3G ..
    Anyways, superb blog!

  • Alzheimer Training Online的头像
    Alzheimer Training Online 2023年2月16日 下午10:44

    I will immediately seize your rss as I can not in finding
    your e-mail subscription link or newsletter service.
    Do you have any? Please permit me recognise so that I could subscribe.
    Thanks.

  • Composite Floor Decking的头像
    Composite Floor Decking 2023年2月16日 下午11:47

    whoah this weblog is fantastic i really like studying your posts.
    Keep up the great work! You understand, lots of people are searching around for this information, you could aid them greatly.

  • callaway golf balls online的头像
    callaway golf balls online 2023年2月17日 上午1:12

    Wow that was unusual. I just wrote an really long comment but after I clicked submit my comment didn’t
    show up. Grrrr… well I’m not writing all that over again. Anyway, just
    wanted to say superb blog!

  • jammers swimsuit的头像
    jammers swimsuit 2023年2月17日 上午1:58

    My brother suggested I would possibly like this blog. He was once totally right.
    This publish actually made my day. You can not imagine simply how a lot time I
    had spent for this information! Thanks!

  • can dogs drink tea的头像
    can dogs drink tea 2023年2月17日 上午3:01

    Howdy, I believe your website could possibly be having internet browser compatibility problems.
    Whenever I take a look at your web site in Safari, it
    looks fine however, if opening in Internet Explorer,
    it’s got some overlapping issues. I just wanted to provide you with a quick heads up!
    Aside from that, wonderful site!

  • wholesale golf balls的头像
    wholesale golf balls 2023年2月17日 上午3:26

    Greetings! Very useful advice in this particular article!
    It is the little changes that produce the largest changes.
    Thanks a lot for sharing!

  • Luisa的头像
    Luisa 2023年2月17日 上午3:56

    Preciserly ѡhat I waѕ loߋking for, regardss foor
    posting.

  • eVdEn EVE NAKliYAt的头像
    eVdEn EVE NAKliYAt 2023年2月17日 上午4:41

    I get pleasure from, result in I found exactly what I was taking a look
    for. You have ended my 4 day long hunt! God Bless you man. Have a nice day.
    Bye

  • home.101ko.com的头像
    home.101ko.com 2023年2月17日 上午4:57

    Ahaa, its fastidious conversation concerning this article
    here at this web site, I have read all that, so at this time me also
    commenting at this place.

  • medtronic guardian sensor tape的头像
    medtronic guardian sensor tape 2023年2月17日 上午5:17

    Yes! Finally someone writes about expression med dexcom.

  • kids barber的头像
    kids barber 2023年2月17日 上午5:38

    My brother suggested I might like this web site. He was entirely right.
    This post actually made my day. You can not imagine simply how so much
    time I had spent for this info! Thanks!

  • my web,的头像
    my web, 2023年2月17日 上午6:29

    Hello just wanted to give you a quick heads up. The text in your content
    seem to be running off the screen in Opera. I’m not sure if this is a formatting issue or something
    to do with internet browser compatibility but I thought I’d
    post to let you know. The style and design look great though!

    Hope you get the issue fixed soon. Cheers

  • company logo wilson golf balls的头像
    company logo wilson golf balls 2023年2月17日 上午7:31

    hey there and thank you for your information – I have certainly picked
    up something new from right here. I did however expertise
    several technical points using this web site, as I experienced to reload the site a lot of
    times previous to I could get it to load correctly.
    I had been wondering if your hosting is OK? Not that I’m
    complaining, but sluggish loading instances times will often affect
    your placement in google and could damage your high quality score if advertising and marketing with Adwords.
    Well I am adding this RSS to my e-mail and can look
    out for much more of your respective fascinating content.
    Ensure that you update this again very soon.

  • Showcase Store Wholesale的头像
    Showcase Store Wholesale 2023年2月17日 上午8:42

    An outstanding share! I have just forwarded this onto a friend who has been doing a little research
    on this. And he actually ordered me lunch due to the fact that I discovered it for him…

    lol. So let me reword this…. Thanks for the meal!!
    But yeah, thanks for spending some time to talk about this matter here on your website.

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!