spring基础

Spring

快速开发

1.导入pom依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>

2.编写需要管理的bean

//接口
public interface UserDao {
    void save();
}
//实现类
public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("哈哈哈哈,我是胡大头");
    }
}

3.创建spring核心配置文件

applicationContext.xml

4.在spring配置文件中配置bean

<bean id="userDao" class="com.feng.Doa.Impl.UserDaoImpl"></bean>

5.使用spring的API获得bean实例

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao useDao = (UserDao) applicationContext.getBean("useDao");
useDao.save();

spring配置文件

Bean标签基本配置

用于配置对象交由Spring来创建。
默认情况下它调用的是类中的无参构造函数,如果没有无参构造函数则不能创建成功。

基本属性

id:Bean实例在spring容器中的唯一标识

class:Bean的全限定名

范围配置

scope:指对象的作用范围,取值如下

image-20230412125006969

属性为singleton时,在加载配置文件时,bean就会被创建

image-20230412125917687

属性为prototype时,只有在getBean时才会创建Bean

image-20230412130004825

Bean生命周期配置

init-method参数

指定类中的初始化方法名称

在Bean对象被创建时,会调用

destroy-method参数

指定类中销毁方法名称

在Bean对象被销毁时,会调用

Bean实例化的三种方式

无参构造方法实例化

上述例子就是无参构造方法

工厂静态方法实例化

1.创建工厂

public class StaticFactory {

    public static UserDao getUserDao(){
        return new UserDaoImpl();
    }
}

2.编写配置文件

<bean id="userDao" class="com.feng.factory.StaticFactory" factory-method="getUserDao"></bean>

3.创建Bean实例

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao useDao = (UserDao) applicationContext.getBean("useDao");
useDao.save();

此时,获取Bean实例就是从工厂方法getUserDao()获取的

因为是静态方法,所以,不必创建工厂实例

工厂实例方法实例化

1.创建工厂

public class DynamicFactory {

    public UserDao getUserDao(){
        return new UserDaoImpl();
    }
}

2.编写配置文件

<bean id="factory" class="com.feng.factory.DynamicFactory"></bean>
<bean id="userDao" factory-bean="factory" factory-method="getUserDao"></bean>

3.创建Bean实例

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao useDao = (UserDao) applicationContext.getBean("useDao");
useDao.save();

因为不是静态方法,所以需要先有工厂实例,才能使用工厂的方法

getUserDao()获取Bean实例

Bean的依赖注入分析

image-20230412132953386

在编写程序时,通过控制反转,把对象的创建交给了Spring,但是代码中不可能出现没有依赖的情况。IOC解耦只是降低他们的依赖关系,但不会消除。例如:业务层仍会调用持久层的方法。

那这种业务层和持久层的依赖关系,在使用Spring之后,就让Spring来维护了。
简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。

如何注入

有参构造方法

1.把需要注入的对象封装为私有属性,并给出有参构造方法

注意,给出有参构造方法后,要注意默认的无参构造方法就没有了

private UserDao userDao;

    public UserServiceImpl() {
    }

    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

2.编写配置文件

<bean id="userService" class="com.feng.service.impl.UserServiceImpl">
	<constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean>

被注入的Bean和要注入的Bean都需要在spring配置文件中配置,让spring来管理

constructor-arg标签的name指构造方法参数名,ref指要注入的Bean的id

set方法

1.把需要注入的对象封装为私有属性,并给出setXxx()方法

private UserDao userDao;

public void setUserDao(UserDao userDao) {
	this.userDao = userDao;
}

2.编写配置文件

<bean id="userDao" class="com.feng.Doa.Impl.UserDaoImpl"></bean>

<bean id="userService" class="com.feng.service.impl.UserServiceImpl">
	<property name="userDao" ref="userDao"></property>
</bean>

被注入的Bean和要注入的Bean都需要在spring配置文件中配置,让spring来管理

并且在被注入的Bean中property标签就是上面setXxx()方法中的Xxx,其中Xxx的第一个小写

ref就是要注入的Bean的id ref是引用Bean对象数据类型才用的

Bean的依赖注入的数据类型

上面的操作,都是注入的引用Bean,除了对象的引可以注入,普通数据类型,集合等都可以在容器中进行注入。

注入数据的三种数据类型

引用数据类型

上述均是引用数据类型

普通数据类型

1.私有属性,给出setXxx方法

private String name;
private int age;

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

public void setAge(int age) {
	this.age = age;
}

2.编写配置文件

<bean id="userDao" class="com.feng.Doa.Impl.UserDaoImpl">
	<property name="name" value="胡大头"></property>
	<property name="age" value="18"></property>
</bean>

要注意此时,普通数据类型的set方法注入要用value

集合数据类型

1.私有属性,给出setXxx方法

private List<String> strList;
private Map<String, User> userMap;
private Properties properties;
    

public void setStrList(List<String> strList) {
	this.strList = strList;
}

public void setUserMap(Map<String, User> userMap) {
	this.userMap = userMap;
}

public void setProperties(Properties properties) {
	this.properties = properties;
}

2.编写配置文件

<bean id="userDao" class="com.feng.Doa.Impl.UserDaoImpl">
        <property name="strList">
            <list>
                <value>胡大头1</value>
                <value>胡大头2</value>
                <value>胡大头3</value>
            </list>
        </property>
        <property name="properties">
            <props>
                <prop key="p1">哈哈哈</prop>
                <prop key="p2">嘻嘻嘻</prop>
                <prop key="p3">呜呜呜</prop>
            </props>
        </property>
        <property name="userMap">
            <map>
                <entry key="u1" value-ref="user1">
                </entry>
                <entry key="u2" value-ref="user1">
                </entry>
            </map>
        </property>
</bean>

<bean name="user1" class="com.feng.pojo.User">
	<property name="address" value="河南师范大学"></property>
	<property name="emil" value="666@qq.com"></property>
</bean>
<bean name="user2" class="com.feng.pojo.User">
	<property name="address" value="河南师范大学"></property>
	<property name="emil" value="777	@qq.com"></property>
</bean>

引入其他配置文件

实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,而在Spring主配置文件通过import标签进行加载

<import resource="applicationContext-xxx.xml"/>

spring相关API

ApplicationContext

接口类型,代表应用上下文,可以通过其实例获得Spring容器中的Bean对象

image-20230412143007687

getBean()方法

image-20230412143223479

其中,当参数的数据类型是字符串时,表示根据Bean的id从容器中获得Bean实例,返回是Object,需要强转。当参数的数据类型是Class类型时,表示根据类型从容器中匹配Bean实例,当容器中相同类型的Bean有多个时,则此方法会报错。

配置数据源/连接池

1.导入依赖

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.49</version>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.21</version>
</dependency>

<dependency>
    <groupId>c3p0</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.1.2</version>
</dependency>

c3p0数据源

配置文件

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/smbm"></property>
    <property name="user" value="root"></property>
    <property name="password" value="123456"></property>
</bean>

代码编写

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

DataSource dataSource = (DataSource) applicationContext.getBean("dataSource");
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();

spring中加载properties文件引入数据源

1.在spring的配置文件中引入约束

xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation=
".............................................
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

2.引入properties配置文件

<context:property-placeholder location="classpath:jdbc.properties"/>

3.引入数据源

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${driver}"></property>
    <property name="url" value="${url}"></property>
    <property name="username" value="${username}"></property>
    <property name="password" value="${password}"></property>
</bean>

properties配置文件:

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/smbm
username=root
password=123456

注解

原始注解

image-20230412161527174

注意:

使用注解进行开发时,需要在applicationContext.xml中配置组件扫描,作用是指定哪个包及其子包下的Bean需要讲行扫描以便识别使用注解配置的类、字段和方法。

注解在service中注入dao

public interface AdminDao {
    void save();
}

//@Component("adminDao")
@Repository("adminDao")
public class AdminDaoImpl implements AdminDao {

    @Override
    public void save() {
        System.out.println("save running.......");
    }
}

public interface AdminService {
    void save();
}

//@Component("adminService")
@Service("adminService")
public class AdminServiceImpl implements AdminService {

    @Autowired
    @Qualifier("adminDao")
    private AdminDao adminDao;

    @Override
    public void save() {
        adminDao.save();
    }
}
<!--配置组件扫描-->
<context:component-scan base-package="com.feng"/>

@Autowired

@Qualifier(“adminDao”)

如果只写一个@Autowired 也能注入,

它是按照数据类型从Spring容器中进行匹配的

但是如果有多个adminDao类型,就不能自动匹配了

@Qualifier(“adminDao”)

是按照id值从容器中进行匹配的但是此处@Qualifier要结合@Autowired一起使用

@Resource(name=“adminDao”)

相当于@Autowired + @Qualifier(“adminDao”)

新注解

image-20230412172559857

//表明该类是Spring的核心配置类
@Configuration
//<context:component-scan base-package="com.feng"/>
@ComponentScan("com.feng")
//<context:property-placeholder location="classpath:jdbc.properties"/>
@PropertySource("classPath:jdbc.properties")
public class SpringConfig {

    @Value("${driver}")
    private String driver;
    @Value("${url}")
    private String url;
    @Value("${username}")
    private String username;
    @Value("${password}")
    private String password;

    @Bean("dataSource")  //spring会将当前方法的返回值以指定名称存储到spring容器中
    public DataSource getDateSource() throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass(driver);
        dataSource.setJdbcUrl(url);
        dataSource.setUser(username);
        dataSource.setPassword(password);
        return dataSource;
    }

}

测试类中测试

ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfig.class);
//注入的是UserDaoImpl,用多态的方式调用了save()方法
UserDao useDao = (UserDao) app.getBean("userDao");
UserDao useDao2 = (UserDao) app.getBean("userDao");

useDao.save();

AOP

AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理 实现程序功能的统一维护的一种技术。

AOP 是 OOP 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

运行期间执行

动态代理: 在不修改源码的情况下,对目标方法进行增强 完成程序间的松耦合

作用及优势

作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强

优势:减少重复代码,提高开发效率,并且便于维护

底层实现

实际上,AOP 的底层是通过 Spring 提供的的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。

常用动态代理技术

JDK 代理 : 基于接口的动态代理技术

image-20230415145527903

cglib 代理:基于父类的动态代理技术

image-20230415145547063

目标对象是要增强方法的对象

Jdk的动态代理

1.目标接口

public interface TargetInterface {
    public void save();
}

2.目标类

public class Target implements TargetInterface {
    @Override
    public void save() {
        System.out.println("Target running....");
    }
}

3.增强方法的类

public class Advice {

    public void before(){
        System.out.println("前置增强");
    }

    public void afterRunning(){
        System.out.println("后置增强");
    }


}

3.动态代理代码

public class PropertyTest {

    public static void main(String[] args) {

        Target target = new Target();

        Advice advice = new Advice();

        //返回值就是动态生成的代理对象
        //注意生成的代理对象和目标对象的关系
        //不能是目标对象类型的
        TargetInterface proxy = (TargetInterface)Proxy.newProxyInstance(
                //目标对象的类加载器
                target.getClass().getClassLoader(),
                //目标对象相同的接口字节码对象数组
            	//因为可能会实现多个接口,所以是数组
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    //调用代理对象的任何方法,实质执行的都是invoke()方法
                    //proxy 代理对象
                    //目标方法的方法字节码对象
                    //args 传递的参数
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        
                        //增强方法
                        advice.before();
                        
                        //执行目标方法
                        //第一个参数代表执行谁的,第二个是参数
                        //返回值就是目标方法的返回值
                        Object invoke = method.invoke(target, args);
                        
                        //增强方法
                        advice.before();
                        return invoke;
                        
                    }
                }
        );

        //调用代理对象
        //接口有什么方法,代理对象就有什么方法
        //实际调用的是目标对象的方法
        proxy.save();

    }

}

cglib的动态代理

spring-context依赖集成了cjlib

1.目标方法类

public class Target {

    public void save() {
        System.out.println("Target running....");
    }
}

2.增强方法类

public class Advice {

    public void before(){
        System.out.println("前置增强");
    }

    public void afterRunning(){
        System.out.println("后置增强");
    }

}

3.测试代理对象

	public class PropertyTest {

    public static void main(String[] args) {

        //目标对象
        Target target = new Target();
        //增强对象
        Advice advice = new Advice();

        //返回值就是 动态生成的代理对象 基于cjlib
        //1.创建增强器
        Enhancer enhancer = new Enhancer();
        //2.设置父类(目标)
        enhancer.setSuperclass(Target.class);
        //设置回调函数
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                //执行前置
                advice.before();

                //执行目标
                Object invoke = method.invoke(target, args);


                //执行后置
                advice.afterRunning();
                return invoke;
            }
        });
        //生成代理对象
        Target proxy = (Target) enhancer.create();

        proxy.save();

    }
}

AOP相关概念

Spring 的 AOP 实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。

image-20230415155955322

AOP开发明确的事项

image-20230415160536755

快速入门

1.导入 AOP 相关坐标

<!--导入spring的context坐标,context依赖aop-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.1.0.RELEASE</version>
</dependency>
<!-- aspectj的织入 -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.13</version>
</dependency>

2.创建目标接口和目标类(内部有切点)

public class Target implements TargetInterface{

    @Override
    public void save() {
        System.out.println("Target running....");
    }

}

public interface TargetInterface {

    public void save();
}

3.创建切面类(内部有增强方法)

public class MyAspect {
    //前置增强方法
    public void before(){
        System.out.println("前置代码增强.....");
    }
}

4.将目标类和切面类的对象创建权交给 spring

<!--配置目标类-->
<bean id="target" class="com.feng.Target"></bean>
<!--配置切面类-->
<bean id="myAspect" class="com.feng.MyAspect"></bean>

5.在 applicationContext.xml 中配置织入关系

配置切点表达式和前置增强的织入关系

需要导入aop命名空间

xmlns:aop="http://www.springframework.org/schema/aop"

http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd


<!--配置织入: 告诉spring框架 哪些方法(切入点),需要哪些增强(前置、后置....)-->

<aop:config>
    
    <!--声明myAspect的Bean为切面对象-->
    <!--切面:切入点  加 通知(增强)-->
    <aop:aspect ref="myAspect">
        <!--配置Target的method方法执行时要进行myAspect的before方法前置增强-->
        <aop:before method="before" pointcut="execution(public void 
                                              com.feng.Target.save())"/>				
    </aop:aspect>
    
</aop:config>

6.测试代码

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
    @Autowired
    private TargetInterface target;
    @Test
    public void test(){
        target.save();
    }
}

7.结果

image-20230415163302526

XML 配置 AOP 详解

首要的是需要操作的bean都要交给spring来管理

切点表达式的写法

表达式语法:

execution([修饰符] 返回值类型 包名.类名.方法名(参数))

image-20230415163456442

image-20230415163505041

通知的类型

通知的配置语法:

<aop:通知类型 method=“切面类中方法名” pointcut=“切点表达式"></aop:通知类型>

image-20230415163549052

环绕方法需要有参数 ProceedingJoinPoint pjp
这个参数就是指的是目标方法 例如

publuc Obiect around(ProceedingJoinPoint pjp){
    System.out.println("环绕前代码增强.....");
    //切点方法
    Object obj = pjp.proceed();
    System.out.println("环绕后码增强.....");
    return obj;
}
切点表达式的抽取

当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用 pointcut-ref 属性代替 pointcut 属性来引用抽 取后的切点表达式。

<aop:config>
    <!--引用myAspect的Bean为切面对象-->
    <aop:aspect ref="myAspect">
        
        <!--抽取的切点表达式-->
        <aop:pointcut id="myPointcut" expression="execution(* com.feng.aop.*.*(..))"/>
        
        <!--引用抽取的切点表达式-->
        <aop:before method="before" pointcut-ref="myPointcut"></aop:before>
        
    </aop:aspect>
</aop:config>

基于注解的AOP开发

1.创建目标接口和目标类(内部有切点)

public interface TargetInterface {
    public void save();
}

public class Target implements TargetInterface {
    
    @Override
    public void save() {
        System.out.println("Target running....");
    }
    
}

2.创建切面类(内部有增强方法)

public class MyAspect {
    //前置增强方法
    public void before(){
        System.out.println("前置代码增强.....");
    }
}

3.将目标类和切面类的对象创建权交给 spring

@Component("target")
public class Target implements TargetInterface {
    @Override
    public void save() {
        System.out.println("Target running....");
    }
}

@Component("myAspect")
public class MyAspect {
    public void before(){
        System.out.println("前置代码增强.....");
    }
}

4.在切面类中使用注解配置织入关系

@Component("myAspect")
@Aspect //标注当前myAspect是一个切面类
public class MyAspect {
    
    @Before("execution(* com.itheima.aop.*.*(..))")
    public void before(){
        System.out.println("前置代码增强.....");
    }
    
}

5.在配置文件中开启组件扫描和 AOP 的自动代理

<!--注意命名空间 context aop-->

<!--组件扫描-->
<context:component-scan base-package="com.feng.aop"/>
<!--aop的自动代理-->
<aop:aspectj-autoproxy/>

6.测试代码

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
    
    @Autowired
    private TargetInterface target;
    
    @Test
    public void test1(){
        
        target.save();
        
    }
    
}

注解配置 AOP 详解
注解通知的类型

通知的配置语法:@通知注解(“切点表达式")

image-20230415194945595

切点表达式的抽取

同 xml 配置 aop 一样,我们可以将切点表达式抽取。抽取方式是在切面内定义方法,在该方法上使用@Pointcut 注解定义切点表达式,然后在在增强注解中进行引用。具体如下:

@Component("myAspect")
@Aspect
public class MyAspect {
    
    @Before("MyAspect.myPoint()")
    //或者@Before("myPoint()")
    public void before(){
        System.out.println("前置代码增强.....");
    }
    
    @Pointcut("execution(* com.feng.aop.*.*(..))")
    public void myPoint(){}
    
}

spring事务

编程式事务控制相关对象

PlatformTransactionManager 接口是 spring 的事务管理器,它里面提供了我们常用的操作事务的方法。

平台事务管理器

image-20230415200443609

注意:

PlatformTransactionManager 是接口类型,不同的 Dao 层技术则有不同的实现类,例如:Dao 层技术是jdbc 或 mybatis 时:org.springframework.jdbc.datasource.DataSourceTransactionManager Dao 层技术是hibernate时:org.springframework.orm.hibernate5.HibernateTransactionManager

TransactionDefinition 是事务的定义信息对象,里面有如下方法:

image-20230415200617946

1.隔离级别

设置隔离级别,可以解决事务并发产生的问题,如脏读、不可重复读和虚读。

image-20230415200650042

1.默认

2.读不可提交的

3.读可提交的

4.可重复读

5.可串行化的

2.事务传播行为

指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。
例如:methodA事务方法调用methodB事务方法时,methodB是继续在调用者methodA的事务中运行呢,还是为自己开启一个新事务运行,这就是由methodB的事务传播行为决定的。

image-20230415200744844

TransactionStatus 接口提供的是事务具体的运行状态 方法如下

image-20230415200818302

xml声明式事务

Spring 的声明式事务顾名思义就是采用声明的方式来处理事务。这里所说的声明,就是指在配置文件中声明 ,用在 Spring 配置文件中声明式的处理事务来代替代码式的处理事务。

作用

image-20230415203407265

注意:Spring 声明式事务控制底层就是AOP。

声明式事务控制的实现 要注意

image-20230415203445439

1.引入tx命名空间

xmlns:tx="http://www.springframework.org/schema/tx"

http://www.springframework.org/schema/tx 
http://www.springframework.org/schema/tx/spring-tx.xsd

2.配置事务增强

3.配置事务 AOP 织入

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${jdbc.driver}"></property>
    <property name="url" value="${jdbc.url}"></property>
    <property name="username" value="${jdbc.username}"></property>
    <property name="password" value="${jdbc.password}"></property>
</bean>


<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"></property>
</bean>


<bean id="accountDao" class="com.feng.dao.AccountDao">
    <property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>


<!--目标对象  内部方法就是切点-->
<bean id="accountService" class="com.feng.service.AccountService">
    <property name="accountDao" ref="accountDao"></property>
</bean>


<!--平台事务管理器   在编程式事务中,就需要先指定平台事务管理器-->
<bean id="transactionManager" 
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--需要注入一个dataSource  transactionManager的底层会从dataSource中拿出connection
		进行事务的控制	-->
    <property name="dataSource" ref="dataSource"></property>
</bean>


<!--事务增强配置  需要平台事务管理器-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>


<!--事务的aop增强  事务aop织入-->
<aop:config>
    <aop:pointcut id="myPointcut" expression="execution(* 
                                              com.feng.service.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut"></aop:advisor>
</aop:config>

4.测试事务控制转账业务代码


public void transfer(String outMan, String inMan, double money) {
    accountDao.out(outMan,money);
    int i = 1/0;
    accountDao.in(inMan,money);
}

切点方法的事务参数的配置
<!--事务增强配置-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!--这里就是设置事务的属性信息-->
    <tx:attributes>
        <!--哪些方法被增强 -->
        <!--可以对不同的方法进行不同的事物增强 -->
        <tx:method name="transfer" isolation="REPEATABLE_READ"/>
        <tx:method name="save" isolation="REPEATABLE_READ"/>
        <!--代表以update开头的方法 -->
        <tx:method name="update*" isolation="REPEATABLE_READ"/>
        <!--*任意方法 -->
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

image-20230416132058276

使用注解配置声明式事务控制

1.编写 AccoutDao

@Repository("accountDao")
public class AccountDao{
    
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    public void out(String outMan, double money) {
        
        jdbcTemplate.update("update account set money=money-? where 		  			                                name=?",money,outMan);
                            
        }
                            
    public void in(String inMan, double money) {
        
        jdbcTemplate.update("update account set money=money+? where 
                              name=?",money,inMan);
                            
        }
                            
}

2.编写 AccoutService

@Service("accountService")
//在当前类上加注解代表当前类下面的所有方法都需要配置事务增强
//注解里面也能加入参数  进行事务增强的配置  所有方法都公用一套
//如果类和方法上面都有   就近原则
@Transactional(isolation = Isolation.READ_COMMITTED)
public class AccountService{
    
    @Autowired
    private AccountDao accountDao;
    
    
    //在需要事物的业务方法上加注解,相当于xml配置中的aop织入方法
    //里面的参数相当于xml配置中的 事物增强的配置
    @Transactional(isolation = Isolation.READ_COMMITTED,propagation = 
                   Propagation.REQUIRED)
    public void transfer(String outMan, String inMan, double money) {
        accountDao.out(outMan,money);
        int i = 1/0;
        accountDao.in(inMan,money);
    }
    
}

3.编写 applicationContext.xml 配置文件

<!—省略之前配置的datsSource、jdbcTemplate、平台事务管理器的配置  要有-->

<!--组件扫描-->
<context:component-scan base-package="com.feng"/>
<!--事务的注解驱动-->
<tx:annotation-driven transaction-manager="transactionManager"/>
注解配置声明式事务控制解析

image-20230416133310899

applicationContext.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:context="http://www.springframework.org/schema/context"
       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
       ">

<!--配置数据源    -->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

<!--配置jdbcTemplate    -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <context:component-scan base-package="com.feng"/>

</beans>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值