Spring学习笔记——AOP(4)

news2024/11/20 12:18:46

Spring学习笔记——AOP(4)

  • 一、学习AOP
    • 1.1 AOP的概述
    • 1.2 AOP思想实现方案
    • 1.3、模拟AOP的基础代码
    • 1.4、AOP的相关概念
  • 二、基于xml配置AOP
    • 2.1 AOP基础入门
    • 2.2、XML方式AOP配置详解
    • 2.3、XML方式AOP原理剖析
  • 三、注解式开发AOP
    • 3.1 注解式开发AOP入门
    • 3.2 AOP注解详细介绍
    • 3.3、注解方式AOP原理解析
  • 四、基于AOP的声明式事务控制
    • 4.1 Spring事务编程概述
    • 4.2 搭建测试环境
    • 4.3 基于XML声明式事务控制
    • 4.4 注解声明式事务控制

一、学习AOP

1.1 AOP的概述

AOP---->面向切面编程 是对面向对象编程OOP的升华。OOP是纵向对一个事物的抽象,一个对象包括静态的属性信息,包括动态的方法信息等。而AOP是横向的对不同事物的抽象,属性与属性方法与方法对象与对象都可以组成一个切面,而用这种思维去设计编程的方式叫做面向切面编程

在这里插入图片描述

1.2 AOP思想实现方案

代理技术:
动态代理技术,在运行期间,对目标方法进行增强,代理对象同名方法内执行原有逻辑或方法的同时可以执行其他的增强逻辑,或者其他对象的方法
在这里插入图片描述

1.3、模拟AOP的基础代码

其实在之前学习BeanPostProcessor时,在BeanPostProcessorafter方法中使用动态代理对Bean进行了增强,实际存储到单例池singleObjects中的不是当前目标对象本身,而是当前目标对象的代理对象Proxy,这样在调用目标对象方法时,实际调用的是代理对象Proxy的同名方法,起到了目标方法前后都进行增强的功能,对该方式进行一下优化,将增强的方法提取出去到一个增强类中,且只对com.itheima.service.impl包下的任何类的任何方法进行增强

//自定义增强类
pubiic class MyAdvice{
	public void beforeAdvice(){
		system.out.println("beforeAdvice...");
	)
	public void afterAdvice() {
		System.out.println("afterAdvice...");
	}
} 
public class MockAopBeanPostProcessor implements BeanPostProcessor , ApplicationContextAware {
    private ApplicationContext applicationContext;
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        //目的:对UserServiceImpl中的Show1和Show2方法进行增强,增强方法存在于Myadvice
        //问题:筛选 service.impl包下的所有类的所有方法都可以进行增强 解决方案:if-else
        //问题:Myadvice怎么获取 解决方案:从Spring容器中获取Myadivce
        if (bean.getClass().getPackage().getName().equals("com.Smulll.service.Impl")){
            //生成Bean的Proxy对象
            Object beanProxy = Proxy.newProxyInstance(
                    bean.getClass().getClassLoader(),
                    bean.getClass().getInterfaces(),
                    (Object proxy, Method method, Object[] args) -> {
                        Myadvice myadvice = applicationContext.getBean(Myadvice.class);
                        //执行增强对象的before方法
                        myadvice.before();
                        //执行目标对象的指定方法
                        Object invoke = method.invoke(bean, args);
                        //执行增强对象的after方法
                        myadvice.after();
                        return invoke;
                    });
            return beanProxy;
        }
        return null;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
} 

1.4、AOP的相关概念

概念单词解释
目标对象Target被增强的方法所在的对象
代理对象Proxy对目标对象进行增强后的对象,客户端实际调用的对象
连接点Joinpoint目标对象中可以被增强的方法
切入点Pointcut目标对象中实际被增强的方法
通知\增强Advice增强部分的代码逻辑
切面Aspect增强和切入点的组合
织入Weaving将通知和切入点组合动态组合的过程

在这里插入图片描述

二、基于xml配置AOP

2.1 AOP基础入门

基本步骤:

  1. 导入AOP相关坐标;
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.6</version>
</dependency> 
  1. 准备目标类、准备通知类,并配置给Spring管理;
  2. 配置切点表达式(哪些方法被增强);
  3. 配置织入(切点被哪些通知方法增强,是前置增强还是后置增强)。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
">
    <!--配置目标类-->
    <bean id="userService" class="com.huanglei.service.Impl.UserServiceImpl"></bean>
    <!--配置增强类-->
    <bean id="myadvice" class="com.huanglei.advice.Myadvice"></bean>
    <!--配置aop-->
    <aop:config>
        <!--配置切点表达式 目的:指定哪些方法被增强-->
        <aop:pointcut id="mtPointcut" expression="execution(void com.Smulll.service.Impl.UserServiceImpl.show1())"/>
        <!--配置织入 目的:指定哪些切点与哪些通知结合-->
        <aop:aspect ref="myadvice">
            <aop:before method="before" pointcut-ref="mtPointcut"></aop:before>
            <aop:after method="after" pointcut-ref="mtPointcut"></aop:after>
        </aop:aspect>
    </aop:config>
</beans> 

2.2、XML方式AOP配置详解

xml配置AOP的方式还是比较简单的,下面看一下AOP详细配置的细节:

  • 切点表达式的配置方式
    可以配置多个切点
<!--配置aop-->
<aop:config>
    <!--配置切点表达式 目的:指定哪些方法被增强-->
    <aop:pointcut id="mtPointcut" expression="execution(void com.huanglei.service.Impl.UserServiceImpl.show1())"/>
    <aop:pointcut id="mtPointcut2" expression="execution(void com.huanglei.service.Impl.UserServiceImpl.show2())"/>
</aop:config> 

pointcut属性可以再后面直接写上要结合的切点

<!--配置aop-->
<aop:config>
    <!--配置切点表达式 目的:指定哪些方法被增强-->
    <aop:pointcut id="mtPointcut" expression="execution(void com.huanglei.service.Impl.UserServiceImpl.show1())"/>
    <!--配置织入 目的:指定哪些切点与哪些通知结合-->
    <aop:aspect ref="myadvice">
        <aop:before method="before" pointcut-ref="mtPointcut"></aop:before>
        <aop:after method="after" pointcut="execution(void com.huanglei.service.Impl.UserServiceImpl.show1())"></aop:after>
    </aop:aspect>
</aop:config> 

切点表达式的配置语法

execution([访问修饰符]返回值类型 包名.类名.方法名(参数))
  • 详解:
    • 访问修饰符可以省略不写;
    • 返回值类型、某一级包名、类名、方法名可以使用*表示任意;
    • 包名与类名之间使用单点.表示该包下的类,使用双点…表示该包及其子包下的类;
    • 参数列表可以使用两个点…表示任意参数。

常见的切点表达式:

//表示访问修饰符为public、无返回值、在com.itheima . aop包下的TargetImpl类的无参方法show
execution(public void com.itheima.aop.TargetImpl.show())
//表述com.itheima.aop包下的TargetImpl类的任意方法
execution(* com.itheima.aop.TargetImpl.*(..))
//表示com.itheima.aop包下的任意类的任意方法
execution(* com.itheima.aop.*.*(..))
//表示com.itheima. aop包及其子包下的任意类的任意方法
execution(* com.itheima.aop..*.*(..))
//表示任意包中的任意类的任意方法
execution(* *..*.*(..) )
  • 通知的类型
通知名称配置方式执行时机
前置通知<aop:before>目标方法执行之前执行
后置通知<aop:after-returning>目标方法执行之后执行,目标方法异常时,不在执行
环绕通知<aop:around>目标方法执行前后执行,目标方法异常时,环绕后方法不在执行
异常通知<aop:after-throwing>目标方法抛出异常时执行
最终通知<aop:after>不管目标方法是否有异常,最终都会执行
  • 通知方法再被调用时,Spring可以为其传递一些必要的参数
参数类型作用
JoinPoint连接点对象,任何通知都可使用,可以获得当前目标对象、目标方法参数等信息
ProceedingJoinPointJoinPoint子类对象,主要是在环绕通知中执行proceed(),进而执行目标方法
Throwable异常对象,使用在异常通知中,需要在配置文件中指出异常对象名称
public void 通知方法名称(JoinPoint joinPoint){
	//获得目标方法的参数
	system.out.println(joinPoint.getArgs()) ;
	//获得目标对象
	system.out.println(joinPoint.getTarget());
	//获得精确的切点表达式信息
	System.out.println(joinPoint.getstaticPart());
} 

Throwable对象

public void afterThrowing (JoinPoint joinPoint, Throwable th) {
//获得异常信息
	System.out.println("异常对象是:"+th+"异常信息是:"+th.getMessage());
} 
<aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut" throwing="th"/> 
  • AOP的配置的两种方式
    • 使用<advisor>配置切面
    • 使用<aspect>配置切面

spring定义了一个Advice接口,实现了该接口的类都可以作为通知类出现

public interface Advice{
}

2.3、XML方式AOP原理剖析

动态代理的实现的选择,在调用getProxy()方法时,我们可选用的AopProxy接口有两个实现类,如上图,这两种都是动态生成代理对象的方式,一种就是基于JDK的,一种是基于Cglib的

代理技术使用条件配置方式
JDK动态代理技术目标类有接口,是基于接口动态生成实现类的代理对象目标类有接口的情况下,默认方式
Cglib 动态代理技术目标类无接口且不能使用final修饰,是基于被代理对象动态生成子对象为代理对象目标类无接口时,默认使用该方式;目标类有接口时,手动配置<aop:config proxy-target-class="true”>强制使用Cglib方式

在这里插入图片描述

Target target = new Target() ;//目标对象
Advices advices = new Advices();//通知对象
Enhancer enhancer = new Enhancer();//增强器对象
enhancer.setSuperclass(Target.class);//增强器设置父类
//增强器设置回调
enhancer.setCallback((MethodInterceptor) (o,method,objects,methodProxy)->{
	advices.before ( ) ;
	object result = method.invoke(target,objects);
	advices.afterReturning();
	return result;
});
//创建代理对象
Target tagetProxy = (Target) enhancer.create();
//测试
String result = targetProxy.show("haohao");

三、注解式开发AOP

3.1 注解式开发AOP入门

Spring的AOP也提供了注解方式配置,使用相应的注解替代之前的xml配置,xml配置AOP时,我们主要配置了三部分:目标类被Spring容器管理、通知类被Spring管理、通知与切点的织入(切面),如下:

<!--配置目标-->
<bean id="target" class="com.itheima.aop.TargetImpl"></bean>
<!--配置通知-->
<bean id="advices" class="com.itheima.aop.Advices"></bean>
<!--配置aop-->
<aop:config proxy-target-class="true">
	<aop:aspect ref="advices">
		<aop:around method="around" pointeut="execution(* com.itheima.aop.*.*(..))"/>
	</aop:aspect>
</aop:config> 

配置aop,其实配置aop主要就是配置通知类中的哪个方法(通知类型)对应的切点表达式是什么:

在这里插入图片描述
注解@Aspect、@Around需要被Spring解析,所以在Spring核心配置文件中需要在配置文件当中添加的自动代理

<aop:aspectj-autoproxy> 

3.2 AOP注解详细介绍

通过不同的注解去完成各项通知类型:

//前置通知
@Before("execution( *com. itheima.aop.*.*(..))")
public void before(JoinPoint joinPoint){}
//后置通知
@AfterReturning("execution(* com.itheima.aop.*.*(..))")
public void afterReturning(JoinPoint joinPoint){}
//环绕通知
@Around("execution (* com.itheima.aop.*.*(..))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable{}
//异常通知
@AfterThrowing("execution(* com.itheima.aop.*.*(..))")
public void afterThrowing(JoinPoint joinPoint){}
//最终通知
@After("execution(* com.itheima.aop.*.*(..))")
public void after(JoinPoint joinPoint){} 

3.3、注解方式AOP原理解析

之前在使用xml配置AOP时,是借助的Spring的外部命名空间的加载方式完成的,使用注解配置后,就抛弃了<aop.config>标签,而该标签最终加载了名为AspectJAwareAdvisorAutoProxyCreator的BeanPostProcessor ,最终,在该BeanPostProcessor中完成了代理对象的生成。

同样,从aspectj-autoproxy标签的解析器入手

this.registerBeanDefinitionParser("aspectj-autoproxy",new AspectJAutoProxyBeanDefinitionParser());

四、基于AOP的声明式事务控制

4.1 Spring事务编程概述

事务在开发的时候是必不可少的东西,在使用JDBC开发的时候,我们使用的是connection对事物进行控制,使用Mybatis时,我们使用的时SqlSession对事物进行控制,缺点显而易见,我们在切换数据库访问技术的时候,事务控制的方式就会总是在改变,Spring就在这些技术的基础上,提供了统一的控制事务的接口。Spring的事务分为:编程式事务控制声明式事务控制

事务控制的方式解释
编程式事务控制Spring提供了事务控制的类和方法,使用编码的方式对业务代码进行了事务控制,事务控制的代码和业务操作代码耦合在一起,在开发当中很少使用
声明式事务控制Spring提供了事务控制的封装,对外提供了xml配置和注解式开发的配置方式,可以通过配置的方式完成对事物的控制,可以达到事务控制和业务操作代码的解耦合,在开发当中推荐使用

Spring事务编程相关的类:

事务控制相关的类解释
平台事务管理器 PlatformTransactionManager是一个接口标准,实现类都具备事务提交、回滚和获得事务对象的功能,不同持久层框架可能会有不同实现方案
事务定义 TransactionDefinition封装事务的隔离级别、传播行为、过期时间等属性信息
事务状态 TransactionStatus存储当前事务的状态信息,如果事务是否提交、是否回滚、是否有回滚点等

虽然我们在开发当中不怎么使用这个编程式事务控制,但是对于这个编程式事务控制的相关类我们需要了解一下,因为我们在进行声明式配置的时候我们还会看见他们

4.2 搭建测试环境

搭建一个转账的环境,dao层一个转出钱的方法,一个转入钱的方法service层一个转账业务方法,内部分别调
用dao层转出钱和转入钱的方法,准备工作如下:

  • 数据库准备一个账户表tb_account;
  • dao层准备一个AccountMapper,包括incrMoney和decrMoney两个方法;
  • service层准备一个transferMoney方法,分别调用incrMoney和decrMoney方法;
  • 在applicationContext文件中进行Bean的管理配置;
  • 测试正常转账与异常转账。

xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:cotext="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
">
    <!--组件扫描-->
    <cotext:component-scan base-package="com.huanglei"/>
    <!--加载properties文件-->
    <cotext:property-placeholder location="classpath:jdbc.properties"/>
    <!--配置数据源信息-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <!--配置SqlSessionFactoryBean,作用将SqlSessionFactory存储到Spring容器-->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--MapperScannerConfigurer,作用扫描指定的包,产生Mapper对象存储到Spring容器-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.huanglei.mapper"></property>
    </bean>
</beans>

Mapper映射文件

public interface accountMapper {
    /*
    *   加钱
    * */
    @Update("update tb_account set money = money+#{money} where account_name = #{accountName}")
    public void incrMoney(@Param("accountName") String accountName,@Param("money") Double money);
    /*
    *   减钱
    * */
    @Update("update tb_account set money = money-#{money} where account_name = #{accountName}")
    public void decrMoney(@Param("accountName") String accountName,@Param("money") Double money);
}

service代码:

@Service("accountService")
public class AccountServiceImpl implements AccountService {
    @Autowired
    private accountMapper accountMapper;
    public void transferMoney(String outAccount, String inAccount, Double money){
        accountMapper.decrMoney(outAccount,money);
        accountMapper.incrMoney(inAccount,money);
    }
}

4.3 基于XML声明式事务控制

结合我们学过的AOP技术,我们可以使用AOP对service的方法进行事务增强。

  • 目标类:自定义的AccountServicelmpl,内部的方法是切点

  • 通知类: Spring提供的,通知方法已经定义好,只需要配置即可

  • 通知类是Spring提供的,需要导入Spring事务的相关的坐标;

  • 配置目标类AccountServicelmpl;

  • 使用advisor标签配置切面。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:cotext="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd

">
    <!--组件扫描-->
    <cotext:component-scan base-package="com.huanglei"/>
    <!--加载properties文件-->
    <cotext:property-placeholder location="classpath:jdbc.properties"/>
    <!--配置数据源信息-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <!--配置SqlSessionFactoryBean,作用将SqlSessionFactory存储到Spring容器-->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--MapperScannerConfigurer,作用扫描指定的包,产生Mapper对象存储到Spring容器-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.huanglei.mapper"></property>
    </bean>

    <!--配置平台事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--事务增强的aop-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>

    <!--事务增强的AOP-->
    <aop:config>
        <!--配置切点表达式-->
        <aop:pointcut id="txPointcut" expression="execution(* com.Smulll.service.Impl.*.*(..))"/>
        <!--配置织入关系 通知advice-ref引入Spring提供好的-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
    </aop:config>
</beans>
<!--事务增强的aop-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
   <tx:attributes>
       <!--
           配置不同的方法的事务属性
           name:方法名称   *代表通配符
           isolaton:控制事务的隔离级别
           timeout:超时时间 默认-1 单位是秒
                例:设置为3 则若3秒内事务没有提交则系统回自动返回
           read-only:是否只读  查询操作设置为只读,其他的为false
           propagation:事务的传播行为 解决业务方法调用业务方法(事务嵌套问题)
       -->
       <tx:method name="*" isolation="READ_COMMITTED" propagation="" timeout="3" read-only="false"/>
   </tx:attributes>
</tx:advice>

**isolation属性:**指定了事务的隔离级别,事务并发存在三大问题:脏读,不可重复读,幻读/虚度。可以通过设置事务的隔离级别来保证并发问题的实现,常用的是READ_COMMITTED和REPEATABLE_READ

常见的isolation设置:

isolation属性解释
DEFAULT表示的为默认的隔离级别,这个取决于你使用的哪种数据库,如MySQL就是REPEATABLE_READ
READ_UNCOMMITTEDA事务可以读取到B事务尚未提交的事务记录,不能解决任何并发问题,安全性最低,性能最高
READ_COMMITTEDA事务只能读取到其他事务已经提交的记录,不能读取到未提交的记录。可以解决脏读问题,但是不能解决不可重复读和幻读
REPEATABLE_READA事务多次从数据库读取某条记录结果一致,可以解决不可重复读,不可以解决幻读
SERIALIZABLE串行化,可以解决任何并发问题,安全性最高,但是性能最低

**read-only属性:**就是设置当前的状态,为只读还是可以修改。设置为true表示只读,那么这样可以提高查询的性能,如果要增加或者删除修改,那必须得设置为法false

<!--一般查询相关的业务操作都会设置只读模式-->
<tx:method name="select*" read-only="true"/>
<tx:method name="find*" read-only="false"/>

**timeout属性:**设置事务提交的最长时间,如果超过这个时间那么事务就会自动回滚,不再执行。默认值为-1,表示没有时间限制。

<!--设置查询操作的超时时间是3秒-->
<tx:method name="select*" read-only="true" timeout="3" />

propagation属性:设置事务的传播行为,主要解决是A方法调用B方法时,事务的传播方式问题的,例如:使用单方的事务,还是A和B都使用自己的事务等。事务的传播行为有如下七种属性值可配置

事务传播的行为解释
REQUIRED (默认值)A调用B,B需要事务,如果A有事务B就加入A的事务中,如果A没有事务,B就自己创建一个事务
REQUIRED_NEWA调用B,B需要新事务,如果A有事务就挂起,B自己创建一个新的事务
SUPPORTSA调用B,B有无事务无所谓,A有事务就加入到A事务中,A无事务B就以非事务方式执行
NOT_SUPPORTSA调用B,B有无事务无所谓,A有事务就加入到A事务中,A无事务B就以非事务方式执行
NEVERA调用B,B以无事务方式执行,A如有事务则抛出异常
MANDATORYA调用B,B要加入A的事务中,如果A无事务就抛出异常
NESTEDA调用B, B创建一个新事务,A有事务就作为嵌套事务存在,A没事务就以创建的新事务执行

4.4 注解声明式事务控制

@Configuration
@ComponentScan("com.huanglei")
@PropertySource("classpath:jdbc.properties")
@MapperScan("com.huanglei.mapper")
@EnableTransactionManagement//相当于<tx:annotation-driven transaction-manager="transactionManager"/>
public class SpringConfig {

    @Bean
    public DataSource dataSource(
            @Value("${jdbc.driver}") String driver,
            @Value("${jdbc.url}") String url,
            @Value("${jdbc.username}") String username,
            @Value("${jdbc.password}") String password
    ){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);

        return dataSource;
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }

    @Bean
    public DataSourceTransactionManager transactionManager(DataSource dataSource){
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource);
        return dataSourceTransactionManager;
    }
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1199479.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Swift 常用类别整理

生成颜色&#xff0c;传入16进制数字生成对应颜色 个人不喜欢传字符串的写法&#xff0c;比如 "0x0080FF" 或者 "0080FF"&#xff0c;原因如下&#xff1a; 传了字符串最后还是要解析成数字参与颜色运算的&#xff0c;需要额外做字符串转数字的操作&…

【C++】C++入门详解 I【C++入门 这一篇文章就够了】

C入门 前言一、C关键字&#xff08;C98&#xff09;二、命名空间 namespace&#xff08;一&#xff09;namespace的出现&#xff08;二&#xff09;namespace的定义&#xff08;1&#xff09;namespace 的正常定义&#xff08;2&#xff09;namespace的功能特性1. 命名空间 可嵌…

Android系统开发快速寻找代码(如何在文件夹中寻找代码)

很多时候对于Android系统开发小白而言&#xff0c;例如预置APK&#xff0c;知道了APK包名不知道具体代码位置需要去寻找代码&#xff0c;但是Android系统代码十分庞大&#xff0c;如何快速准确查询代码是个问题。 本人目前只探索到了一些方法&#xff0c;如有更有效的办法可以…

github私有仓库开发,公开仓库发布版本

文章目录 github私有仓库开发,公开仓库发布版本需求背景实现思路GitHub Releases具体步骤广告 github私有仓库开发,公开仓库发布版本 需求背景 github私有仓库开发,公开仓库发布版本&#xff0c;既可以保护源代码,又可以发布版本给用户使用。许多知名软件项目都采用了这样的开…

VS Code画流程图:draw.io插件

文章目录 简介快捷键 简介 Draw.io是著名的流程图绘制软件&#xff0c;开源免费&#xff0c;对标Visio&#xff0c;用过的都说好。而且除了提供常规的桌面软件之外&#xff0c;直接访问draw.io就可以在线使用&#xff0c;堪称百分之百跨平台&#xff0c;便捷性直接拉满。 那么…

3.HTML中语法规范

3. HTML语法规范 3.1 基本语法概述 3.1.1 HTML标签 1 HTML 标签是由尖括号包围的关键字&#xff0c;例如<html>。 2. HTML 标签通常是成对出现的&#xff0c;例如<html>和</html>,我们称为双标签。标签对中的第一个标签是开始标签&#xff0c;第二个标签是…

Windows10腾讯文档下载和安装

文章目录 Windows10腾讯文档下载和安装官网下载执行安装 Windows10腾讯文档下载和安装 官网下载 官网 下载后&#xff1a; 执行安装 找到下载目录 安装后打开 扫描登录即可

2023亚太杯数学建模A题B题C题思路代码分析

文章目录 0 赛题思路1 竞赛信息2 竞赛时间3 建模常见问题类型3.1 分类问题3.2 优化问题3.3 预测问题3.4 评价问题 4 建模资料5 最后 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 竞赛信息 2023年第十三…

gma 2.0.3 (2023.11.12) 更新日志

安装 gma 2.0.3 pip install gma2.0.3新增 此版本为 gma 2 功能更新最大的版本&#xff0c;且主要集中在矢量数据处理上。 0.1 io.ReadVector&#xff1a;直接打开矢量数据为Layer&#xff0c;用以简化io.Open.GetLayer 过程。Layer的新增功能如下&#xff1a; 序号功能性质说…

redis的基本命令,并用netty操作redis(不使用springboot或者spring框架)就单纯的用netty搞。

大家如果对使用netty搞这些http请求什么的感兴趣的&#xff0c;可以参观我自己创建的这个项目。 nanshaws/nettyWeb: 复习一下netty&#xff0c;并打算做一个web项目出来 (github.com) Redis的基本命令包括&#xff1a; SET key value&#xff1a;设置指定key的值。 GET key…

搭建产品帮助中心其实很简单,方法都在这了!

网站帮助中心是一个为用户提供支持和解答问题的重要资源。它不仅可以提高用户体验&#xff0c;还能减少用户问题反馈的数量。通过提供清晰、易于理解的文档和指南&#xff0c;帮助中心可以帮助用户更好地了解产品或服务&#xff0c;并解决他们在使用过程中遇到的问题。接下来我…

2023亚太杯数学建模B题思路

文章目录 0 赛题思路1 竞赛信息2 竞赛时间3 建模常见问题类型3.1 分类问题3.2 优化问题3.3 预测问题3.4 评价问题 4 建模资料5 最后 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 竞赛信息 2023年第十三…

matlab 二自由度操纵稳定性汽车模型

1、内容简介 略 19-可以交流、咨询、答疑 二自由度操纵稳定性汽车模型 二自由度、操纵稳定性、操纵动力学 2、内容说明 1 模型假设 忽略转向系的影响&#xff0c;以前、后轮转角作为输入&#xff1b;汽车只进行平行于地面的平面运动&#xff0c;而忽略悬架的作用&#xf…

Halcon WPF 开发学习笔记:HSmartWindowControlWPF正常加载

文章目录 加载问题相关文章彻底解决 加载问题 我们在WPF中使用Halcon的时候&#xff0c;会出现图片被拉伸的问题&#xff0c;需要拖动才可以解决&#xff0c;我网上找了好久&#xff0c;终于找到了如何成功解决这个问题。 相关文章 3.7 Halcon 窗体显示对象消失问题 【halcon】…

(二)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB

一、七种算法&#xff08;DBO、LO、SWO、COA、LSO、KOA、GRO&#xff09;简介 1、蜣螂优化算法DBO 蜣螂优化算法&#xff08;Dung beetle optimizer&#xff0c;DBO&#xff09;由Jiankai Xue和Bo Shen于2022年提出&#xff0c;该算法主要受蜣螂的滚球、跳舞、觅食、偷窃和繁…

手撕数据库连接池

1.有开源的数据库连接池&#xff0c;你为啥不用&#xff1f; 这个不是因为闲的没事干&#xff0c;先说下需求背景 我们有一个数据源管理模块&#xff0c;配置的数据源连接&#xff0c;用户名&#xff0c;密码等信息 在数据源管理模块配置好之后&#xff0c;去另一个模块选择数…

日语形容词分类

变成肯定形式很简单&#xff0c;就是在后面加个です就可以了&#xff0c;不列在表格了 一类形容词 汉字い并且是以い结尾的形容词 否定形式是去掉い然后在后面加上くないです 更加正式的否定是去掉い然后加上くありません 日文平假名过去式过去否定中文熱いあつい熱くないで…

[护网杯 2018]easy_tornado 1(两种解法!)

题目环境&#xff1a;发现有三个txt文本文件 /flag.txt/welcome.txt/hints.txt 依此点开 flag在/fllllllllllllag文件中 在hints.txt文件中发现md5计算 md5(cookie_secretmd5(filename)) 并且三个文件中都存在filehash&#xff08;文件名被哈希算法加密32位小写&#xff09; 猜…

Halcon WPF 开发学习笔记(2):Halcon导出c#脚本和WPF初步开发

文章目录 前言HalconC#教学简单说明如何二开机器视觉如何二次开发Halcon导出Halcon脚本新建WPF项目&#xff0c;导入Halcon脚本和Halcon命名空间 前言 我目前搜了一下我了解的机器视觉软件&#xff0c;有如下特点 优点缺点兼容性教学视频(B站前三播放量)OpenCV开源&#xff0…

如何让VirtualBox系统使用Ubuntu主机的USB

如何让VirtualBox系统使用Ubuntu主机的USB 当通过 VirtualBox 尝试不同的操作系统时&#xff0c;访问虚拟机中的 USB 驱动器来传输数据非常有用。 安装Guest Additions 自行百度安装Guest Additions的方法&#xff0c;最终的效果如下&#xff1a; 将用户添加到 vboxusers 组…