springboot aop来实现读写分离和事物配置

使用Spring实现读写分离 1 背景 我们一般应用对数据库而言都是“读多写少”,也就说对数据库读取数据的压力比较大,有一个思路就是说采用数据库集群的方案, 其中一个是主库,负责写入数据,我们称之为:写库; 其它都是从库,负责读取数据,我们称之为:读库; 2 方案 解决读写分离的方案有两种:应用层解决和中间件解决。 2.1 应用层解决: 优点: 多数据源切换方便,由程序自动完成; 不需要引入中间件... 阅读详情
AI摘要

本文介绍如何使用Spring Boot和AOP技术实现数据库读写分离,并配置事务管理,减轻主数据库的压力,提高应用性能。

springboot aop来实现读写分离,以及事物配置

什么事读写分离

​ 读写分离,基本的原理是让主数据库处理事务性增、改、删操作(INSERT、UPDATE、DELETE),而从数据库处理SELECT查询操作。数据库复制被用来把事务性操作导致的变更同步到集群中的从数据库。

为什么要实现读写分离
  • 增加冗余

  • 增加了机器的处理能力

  • 对于读操作为主的应用,使用读写分离是最好的场景,因为可以确保写的服务器压力更小,而读又可以接受点时间上的延迟。

实现

​ 本文介绍利用spring aop来动态切换数据源来实现读写分离。

先建一个maven项目,导入springBoot依赖。
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.2.RELEASE</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <!--mybatis-->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>1.3.1</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <!-- druid -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>${druid.version}</version>
    </dependency>
    <!-- mysql connector-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>${mysql-connector.version}</version>
    </dependency>

    <!--test-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

</dependencies>

然后在配置文件application.yml中自定义数据源配置项

server:
  port: 8080
logging:
  level:
    org.springframework: INFO
    com.qiang: DEBUG
spring:
  output:
    ansi:
      enabled: always
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db_area?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root
    password: root
  db:
    readsize: 2
    read0:
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/db_area?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
      username: root
      password: root

    read1:
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/db_area?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
      username: root
      password: root
  aop:
    auto: true
    proxy-target-class: true

配置Druid

package com.qiang.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.sql.DataSource;

/**
 * @author gengqiang
 * @date 2018/5/3
 */
@Configuration
public class DruidConfig {

    private Logger logger = LoggerFactory.getLogger(DruidConfig.class

    /**
     * 主据源
     * @return
     */
    @Primary
    @Bean(name = "dataSource")
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource() {
        return DataSourceBuilder.create().type(com.alibaba.druid.pool.DruidDataSource.class).build();

    }

    /**
     * 从数据源1
     * @return
     */
    @Bean(name = "readDataSource0")
    @ConfigurationProperties(prefix = "spring.db.read0")
    public DataSource readDataSource0() {
        return DataSourceBuilder.create().type(com.alibaba.druid.pool.DruidDataSource.class).build();

    }
    /**
     * 从数据源2
     * @return
     */
    @Bean(name = "readDataSource1")
    @ConfigurationProperties(prefix = "spring.db.read1")
    public DataSource readDataSource1() {
        return DataSourceBuilder.create().type(com.alibaba.druid.pool.DruidDataSource.class).build();

    }

}   

配置Mybaits

package com.qiang.config;

import com.qiang.config.db.DataSourceType;
import com.qiang.config.db.RoutingDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;

import javax.sql.DataSource;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author gengqiang
 * @date 2018/5/3
 */
@Configuration
@EnableTransactionManagement(order = 2)
@MapperScan(basePackages = {"com.qiang.demo.mapper"})
public class MybatisConfig  implements TransactionManagementConfigurer, ApplicationContextAware {


    private static ApplicationContext context;


    /**
     * 写库数据源
     */
    @Autowired
    private DataSource dataSource;

    /**
     * 读数据源数量
     */
    @Value("${spring.db.readsize}")
    private Integer readsize;
    /**
     * 数据源路由代理
     *
     * @return
     */

    @Bean
    public AbstractRoutingDataSource routingDataSouceProxy() {
        RoutingDataSource proxy = new RoutingDataSource(readsize);
        Map<Object, Object> targetDataSources = new HashMap<>(readsize + 1);
        targetDataSources.put(DataSourceType.WRITE.getType(), dataSource);
        for (int i = 0; i < readsize; i++) {
            DataSource d = context.getBean("readDataSource" + i, DataSource.class);
            targetDataSources.put(i, d);
        }
        proxy.setDefaultTargetDataSource(dataSource);
        proxy.setTargetDataSources(targetDataSources);
        return proxy;
    }

    @Bean
    @ConditionalOnMissingBean
    public SqlSessionFactoryBean sqlSessionFactory() throws IOException {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(routingDataSouceProxy());
        bean.setVfs(SpringBootVFS.class);
        bean.setTypeAliasesPackage("com.qiang");
        Resource configResource = new ClassPathResource("/mybatis-config.xml");
        bean.setConfigLocation(configResource);
        ResourcePatternResolver mapperResource = new PathMatchingResourcePatternResolver();
        bean.setMapperLocations(mapperResource.getResources("classpath*:mapper/**/*.xml"));
        return bean;
    }

    @Override
    public PlatformTransactionManager annotationDrivenTransactionManager() {
        return new DataSourceTransactionManager(routingDataSouceProxy());
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if (context == null) {
            context = applicationContext;
        }
    }
}

其中实现数据源切换的功能就是自定义一个类扩展AbstractRoutingDataSource抽象类,就是代码中的定义的RoutingDataSource,其实该相当于数据源DataSourcer的路由中介,可以实现在项目运行时根据相应key值切换到对应的数据源DataSource上。

RoutingDataSource.class
package com.qiang.config.db;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 数据源路由
 *
 * @author gengqiang
 */
public class RoutingDataSource extends AbstractRoutingDataSource {

    private AtomicInteger count = new AtomicInteger(0);

    private int readsize;

    public RoutingDataSource(int readsize) {
        this.readsize = readsize;
    }

    @Override
    protected Object determineCurrentLookupKey() {
        String typeKey = DataSourceContextHolder.getJdbcType();
        if (typeKey == null) {
            logger.error("无法确定数据源");
        }
        if (typeKey.equals(DataSourceType.WRITE.getType())) {
            return DataSourceType.WRITE.getType();
        }
        //读库进行负载均衡
        int a = count.getAndAdd(1);
        int lookupkey = a % readsize;
        return lookupkey;
    }

}

其中用到了2个辅助类

package com.qiang.config.db;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 全局数据源
 *
 * @author gengqiang
 */
public class DataSourceContextHolder {

    private static Logger logger = LoggerFactory.getLogger(DataSourceContextHolder.class);

    private final static ThreadLocal<String> local = new ThreadLocal<>();

    public static ThreadLocal<String> getLocal() {
        return local;
    }

    public static void read() {
        logger.debug("切换至[读]数据源");
        local.set(DataSourceType.READ.getType());
    }

    public static void write() {
        logger.debug("切换至[写]数据源");
        local.set(DataSourceType.WRITE.getType());
    }

    public static String getJdbcType() {
        return local.get();
    }

}
package com.qiang.config.db;

/**
 * @author gengqiang
 */
public enum DataSourceType {
    READ("read", "读库"), WRITE("write", "写库");
    private String type;
    private String name;

    DataSourceType(String type, String name) {
        this.type = type;
        this.name = name;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

最后通过aop设置切面,拦截读写来动态的设置数据源

package com.qiang.config.aop;

import com.qiang.config.db.DataSourceContextHolder;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * 拦截数据库读写
 *
 * @author gengqiang
 */

@Aspect
@Component
@Order(1)
public class DataSourceAspect {

    Logger logger = LoggerFactory.getLogger(getClass());

    @Before("execution(* com.qiang..*.*ServiceImpl.find*(..)) " +
            "|| execution(* com.qiang..*.*ServiceImpl.count*(..))" +
            "|| execution(* com.qiang..*.*ServiceImpl.sel*(..))" +
            "|| execution(* com.qiang..*.*ServiceImpl.get*(..))"
    )
    public void setReadDataSourceType() {
        logger.debug("拦截[read]方法");
        DataSourceContextHolder.read();
    }

    @Before("execution(* com.qiang..*.*ServiceImpl.insert*(..)) " +
            "|| execution(* com.qiang..*.*ServiceImpl.save*(..))" +
            "|| execution(* com.qiang..*.*ServiceImpl.update*(..))" +
            "|| execution(* com.qiang..*.*ServiceImpl.set*(..))" +
            "|| execution(* com.qiang..*.*ServiceImpl.del*(..))")
    public void setWriteDataSourceType() {
        logger.debug("拦截[write]操作");
        DataSourceContextHolder.write();
    }

}

主要的代码就写好了,下面来测试一下是否读写分离。

写一个测试类:

package com.qiang;

import com.qiang.demo.entity.Area;
import com.qiang.demo.service.AreaService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * @author gengqiang
 * @date 2018/5/4
 */

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestApplication {

    @Autowired
    private AreaService areaService;


    @Test
    public void test() {
        Area area = new Area();
        area.setDistrictId("0000");
        area.setName("test");
        area.setParentId(0);
        area.setLevel(1);
        areaService.insert(area);
    }

    @Test
    public void test2() {
        areaService.selectByPrimaryKey(1);
    }
}

其中第一个测试插入数据,第二个测试查询。

第一测试结果:

这里写图片描述

第二个测结果:

这里写图片描述

从结果看出来第一个走的写数据源,就是主数据源,第二个的走读数据源,就是从数据源。

然后我们在测试一下事物,看遇到异常是否会滚。

测试:

   @Test
    public void contextLoads() throws Exception {
        try {
            areaService.insertBack();

        } catch (Exception e) {
//            e.printStackTrace();
        }
        System.out.println(areaService.count(new Area()));

    }

其中service:

@Override
@Transactional(rollbackFor = Exception.class)
public void insertBack() {
    Area area = new Area();
    area.setDistrictId("0000");
    area.setName("test");
    area.setParentId(0);
    area.setLevel(1);
    mapper.insert(area);
    throw new RuntimeException();
}

方法上加@Transactional,声明一个事物。

这里写图片描述

看一下运行结果,虽然运行插入的时候,sql是运行了,但最后查询的时候数量为0,说明会滚了。

配置事物

这里写图片描述

第一步需要加一个注解@EnableTransactionManagement,后面的参数是为了区分aop和事物执行的顺序。

然后在需要会滚的方法上加一个注解@Transactional。

源代码已提交github https://github.com/qianggeng/ReadWriteSplittingDemo

030-若依pro(ruoyi-vue-pro)多数据源(读写分离) ③ 使用 Dynamic Datasource 提供的 @DSTransactional (opens new window)注解,支持多数据源的切换,不提供绝对可靠的多数据源的事务一致性(强于 ① 弱于 ②),可学习 《DSTransactional 实现源码分析 》 (opens new window)文章。在方法上添加 @Master (opens new window)注解,使用名字为 master 的数据源,即使用【主】库,一般适合【写】场景。 阅读详情

相关推荐

Java spring boot aop配置以及使用

什么是AOP,AOP的作用是什么,怎么实现AOP

Leaf吧的博客 928

(八)Spring Boot Web 开发【配置AOP】

1、AOP 简介        要介绍面向切面编程( Aspec -Oriented Programming , AOP),首先要想到这这样一个场景:公司有 个人力资源管理系统目前已经上线,但是系统运行不稳定,有时运行得很慢,为了检测出到底是哪个环节出问题了,开发人员想要监控每一个方法的执行时间,再根据这些执行时间判断出问题所在。当问题解决后,再把这些监控移除掉。系统目前己经运行,如果手动修改系统中成千上万个方法,那么工作量未免太大,而且

哟,你又在偷偷写bug! 925

springboot+mybatis配置多数据源+aop动态数据源切换

最近告诉项目需要做多数据源得配置,鼓捣了很久,一直没有做aop得数据源切换,一直只能调用默认得是具有,在网上找了一些资料进行了配置,项目能够成功得进行数据源得切换,把心得保存下来和大家分享一下。1.数据库多数据源配置2.自定义一个数据源配置类DataSourceConfigurer@Configuration public class DataSourceConfigurer { //主...

qq_37532651的博客 2133

【springboot】使用AOP

创建一个切面类,并使用 @Aspect 注解标记,并在这个类中定义切点和通知。我之前简单测试了下五种增强方法的执行顺序(console输出,AOP生效。测试aop是否生效。

BlackPudding_的博客 2177

使用Spring实现读写分离(MySQL实现主从复制)

我感觉很不错的技术文档,现在和大家分享,希望能够帮到大家,如果你需要可以下载看看,很适合喜欢研究技术的人员

从IMU数据到精准定位:手把手推导ESKF中的四元数运动学与误差状态方程

本文详细介绍了如何从IMU数据推导ESKF中的四元数运动学与误差状态方程,实现精准定位。通过分析误差状态卡尔曼滤波器的优势、四元数表示的必要性,以及IMU测量模型和运动学基础,提供了完整的数学推导和实现要点。适合机器人定位和自动驾驶领域的算法工程师参考,帮助优化导航系统性能。

weixin_30271335的博客 680

Springboot Mybatis MySQL读写分离及事物配置

为什么需要读写分离 当项目越来越大和并发越来大的情况下,单个数据库服务器的压力肯定也是越来越大,最终演变成数据库成为性能的瓶颈,而且当数据越来越多时,查询也更加耗费时间,当然数据库数据过大时,可以采用数据库分库分表,同时数据库压力过大时,也可以采用Redis等缓存技术来降低压力,但是任何一种技术都不是万金油,很多时候都是通过多种技术搭配使用,而本文主要就是介绍通过读写分离来加快数据库读取速度

wantnrun的专栏 6366

Unity中5分钟实现3D高斯泼溅渲染:从原理到实战部署

3D高斯泼溅(3D Gaussian Splatting)是一种新兴的神经渲染技术,它通过显式地使用大量3D高斯椭球来表示场景,革新了传统的体渲染和神经辐射场(NeRF)方法。其核心原理是将这些3D高斯投影到2D屏幕并进行快速光栅化与Alpha混合,从而绕过了耗时的光线步进,实现了极高的渲染效率。这项技术的价值在于,它能够在消费级GPU上达到实时甚至超实时的帧率,同时保持紧凑的数据表达和惊人的视觉细节,极大地降低了高质量3D重建与渲染的门槛。在应用场景上,它正快速渗透到数字孪生、虚拟现实、文化遗产数字化以及

weixin_29474431的博客 272

SpringBoot+AOP+Mybatis实现多数据源切换,实现数据库读写分离

自定义多数据源动态切换(SpringBoot+Mybatis)实现数据库读写分离1,自定义多数据源动态切换原理总结一下: 通过自定义动态数据DynamicDataSource重写的determineCurrentLookupKey()方法程序动态的获得不同的数据源名称,从而取出不同的数据源获得连接,实现动态数据源切换的功能2,自定义数据源动态实现(基于Spring AOP+SpringBoot+M...

Mr_lifengzi的博客 4426

【智能车学习】电磁循迹中的基本控制算法

今天心血来潮,突然想总结一下电磁车的一些基本算法,也希望可以给新手同学们一点帮助和参考。本篇主要讲述智能车电磁循迹中涉及到的基本算法,包括滤波算法,归一化算法,PID控制等

Carry_qing的博客 6万+

RK3588与银河麒麟V10 SP1 2403深度适配:国产化硬件与操作系统的完美融合

本文深入探讨了RK3588处理器与银河麒麟V10 SP1 2403操作系统的深度适配,展示了国产化硬件与操作系统的完美融合。通过性能验证和实际应用测试,证明了这套组合在多媒体处理、AI推理和日常办公中的卓越表现,实现了完全自主可控的计算平台。

weixin_29194255的博客 248

SpringBoot基于AOP的事物配置类

SpringBoot事物配置类 package com.mtpc.admin.config; import org.springframework.aop.aspectj.AspectJExpressionPointcut; import org.springframework.aop.support.DefaultPointcutAdvisor; import org.springframew...

weixin_43627406的博客 331

ADS 2024实战:手把手教你搞定2.3-3.5GHz宽带Doherty功放仿真与版图(附源文件)

本文详细解析了在ADS 2024环境中实现2.3-3.5GHz宽带Doherty功放的全流程设计,包括仿真与版图实现。从软件环境搭建、器件模型导入到核心电路设计、匹配网络优化,再到系统集成与版图生成,提供了实战技巧和关键参数设置。特别针对5G通信和卫星应用中的高效率需求,展示了如何通过ADS 2024的新功能提升设计效率。

weixin_42639246的博客 460

SpringBoot+aop实现多数据源动态切换

SpringBoot+aop实现多数据源动态切换 一、多数据源动态切换原理 项目中我们经常会遇到多数据源的问题,尤其是数据同步或定时任务等项目更是如此;又例如:读写分离数据库配置的系统。 1、多数据源设置: 1)静态数据源切换: 一般情况下,我们可以配置多个数据源,然后为每个数据源写一套对应的sessionFactory和dao层代码(以hibernate为例,...

青衫烟雨客的专栏 1万+

【100%通过率】华为od统一考试B卷【查找接口成功率最优时间段】Python 实现

服务之间交换的接口成功率作为服务调用关键质量特性,某个时间段内的接口失败率使用一个数组表示,数组中每个元素都是单位时间内失败率数值,数组中的数值为0~100的整数, 给定一个数值(minAverageLost)表示某个时间段内平均失败率容忍值,即平均失败率小于等于minAverageLost,找出数组中最长时间段,如果未找到则直接返回NULL。

MISAYAONE的博客 5869

PyTorch实战(26)——PyTorch分布式训练

在本节中,我们将探讨如何通过跨机器和机器内多进程的分布式训练来加速模型训练过程。我们将系统学习 PyTorch 提供的三大分布式训练 API——torch.distributed、torch.multiprocessing 以及 torch.utils.data.distributed.DistributedSampler,使用这些 API 能够极大的简化分布式训练,介绍如何使用 PyTorch 的分布式训练工具,在 CPU 和 GPU 上加速训练。

盼小辉丶的博客 3808

springboot实现AOP切面编程

概述 AOP(Aspect Oriented Programming) 即面向切面编程。面向切面是面向对象中的一种方式而已。在代码执行过程中,动态嵌入其他代码,叫做面向切面编程(将交叉业务逻辑封装成成切面,利用AOP功能将切面织入到主业务逻辑———与主业务逻辑无关的代码,使用场景如:安全检查,事物,日志等 。 AOP是一种方式,实现AOP的库有名的有两个AspectJ和spring AOP. 核心...

ityqing的博客 6001

抖音 webmssdk 签名算法解析:X-Dynosaur / X-Gnarly / X-Bogus 生成原理

它只依赖设备指纹/环境编码与随机数,与 query 内容无关,因此可以独立于 Dynosaur/Gnarly 生成。

猫敷雪 197

项目中实现读写分离

1、AbstractRoutingDataSource Spring boot提供了AbstractRoutingDataSource 根据用户定义的规则选择当前的数据源,这样我们可以在执行查询之前,设置使用的数据源。实现可动态路由的数据源,在每次数据库查询操作前执行。它的抽象方法 determineCurrentLookupKey() 决定使用哪个数据源。 大概意思是: AbstractRoutingDataSource的getConnection() 方法根据查找 lookup key ...

七秒鱼的博客 477

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

SpringBoot读写分离实践

SpringBoot读写分离实践 文章共6000字,纯文字的文章写的比较少,但是不多写一点很难让读者深入了解,所以请耐心看完,后面会有源码,基本上看明白原理了,复制粘贴即可 实际环境建议使用mysql5.7版本,8.0版本坑会比较多,如果使用云环境的mysql读写分离版本一般也基于5.7版本,但是搭建教程基本一致 基于mysql8+docker搭建主从集群文章地址 1 读写分离适用的场景 读多写少 并发量小 非强一致性场景 当并发量大时,应使用缓存架构,而非加强数据库层吞吐能力,当大量并发进入数据库

分享技术,记录问题,努力学习 561

Springboot引入AOP

AOP是Aspect Oriented Programming的缩写,意为面向切面编程。通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是spring框架的一个重要内容,她通过对既有程序定义一个切入点(pointcut),然后在切入点前后切入不同的执行任务,常见使用场景有:打开/关闭数据库连接、打开/关闭事物、记录日志等等。基于AOP不会破坏原来的程序逻辑,因此她可以很好地对...

kagurawill的博客 6488

SpringBoot事物管理器

一、springboot整合事物管理 springboot默认集成事物,只主要在方法上加上@Transactional即可 二、SpringBoot分布式事物管理 使用springboot+jta+atomikos分布式事物管理 1、pom文件 <parent> <groupId>org.springframework.b...

weixin_30275415的博客 362

Spring和SpringMVC以及SpringBoot的区别

Spring 是一个开源框架,为简化企业级应用开发而生。Spring 可以是使简单的 JavaBean 实现以前只有 EJB 才能 实现的功能。Spring 是一个 IOC 和 AOP 容器框架。 Spring 容器的主要核心是: 控制反转(IOC),传统的 java 开发模式中,当需要一个对象时,我们会自己使用 new 或者 getInstance 等直接 或者间接调用构造方法创建一个对象。而在...

yjt520557的博客 3万+

springboot---IoC 和 AOP

创建了许多重复对象,造成大量资源浪费;更换实现类需要改动多个地方;创建和配置组件工作繁杂,给组件调用方带来极大不便。切面逻辑编写繁琐,有多少个业务方法就需要编写多少次。

lipviolet的博客 1372

SpringBoot2.x【五】整合AOP切面编程

SpringBoot2.x【五】整合AOP切面编程 面向方面编程(AOP)通过提供另一种思考程序结构的方式来补充面向对象编程(OOP)。 OOP中模块化的关键单元是类,而在AOP中,模块化单元是方面。 准备工作 首先,使用AOP要在build.gradle中加入依赖 //引入AOP依赖 compile "org.springframework.boot:spring-boot-starter-...

Java Pro 19万+

AOP的那些事以及在SpringBoot的应用

面向切面编程(Aspect Oriented Programming),通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。 *AOP其实只是OOP的补充而已。OOP从横向上区分出一个个的类来,而AOP则从纵向上向对象中加入特定的代码。有了AOP,OOP变得立体了。如果加上时间维度,AOP使OOP由原来的二维变为三维了,由平面变成立体了*

zhaoenweiex的博客 2346
上一篇: java异常总结
下一篇: pom文件详解
Coder_Qiang
博客等级 码龄10年 33粉丝 39原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Coder_Qiang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值