Spring基础学习

本文介绍了Spring框架的基础知识,包括IoC容器的概念和依赖注入,详细讲解了Bean的定义、属性、生命周期以及实例化方式。此外,文章还讨论了不同类型的依赖注入,如setter注入和构造器注入,并提到了自动装配和集合装配。接着,内容转向了注解开发,如@Component、@Service、@Repository和@Configuration等,以及如何使用注解进行bean作用域、生命周期和自动装配的管理。最后,文章阐述了AOP的核心概念,包括连接点、切入点、通知和切面,并展示了AOP的基本使用和工作流程,以及事务管理的实现和作用。
摘要由CSDN通过智能技术生成

Spring基础学习

1. Spring概念

1.1 控制反转 依赖注入

  1. IoC(Inversion of Control)控制反转
    • 使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部,此思想成为控制反转。
  2. Spring技术实现IoC思想
    • Spring提供了一个容器,称为IoC容器
    • Ioc容器负责对象的创建。初始化等一系列工作,被创建或被管理的对象在IoC容器中统称为Bean
  3. DI(Dependency Injection)依赖注入
    • 在容器中建立bean与bean之间的依赖关系的整个过程,成为依赖注入。

目的:充分解耦

示例代码:

两个测试组件代码:

public class bookImp1 implements BookDao {
    public void save() {
        System.out.println("bookdao saving ....");
    }
}
public class BookService {
    private BookDao bookDao1;
    public void save(){
        bookDao1.save();
        System.out.println("bookService save doing");
    }
    public void setBookdao(bookImp1 bookdao) {
        this.bookDao1 = bookdao;
    }
}

测试代码:

public class App {
    public static void main( String[] args ) {

        ApplicationContext context = new ClassPathXmlApplicationContext("Application.xml");

        //测试控制反转,通过xml中注册bean
        BookDao bookDao = (BookDao) context.getBean("bookDao");
        bookDao.save();
        System.out.println("-------------");
        //测试依赖注入
        BookService bookService = (BookService) context.getBean("bookService");
        bookService.save();

    }
}

spring配置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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="bookDao" class="com.cqut.dao.impl.bookImp1"/>
    <bean id="bookService" class="com.cqut.service.BookService">
        <property ref="bookDao" name="bookdao"/>
    </bean>
</beans>

代码说明:

  1. bookDao测试控制反转。通过<bean id="bookDao" class="com.cqut.dao.impl.bookImp1"/>注册bean组件,这里需要注意的是class指定的是实体类不是接口,spring默认是通过调用无参构造函数来创建对象的

  2. bookService中依赖于bookDao对象,这里就是依赖注入。将容器中创建好的bookDao对象赋值到bookService中的引用。这里注意是使用的setter属性注入,将bookDao注册到bookService中的,bookService中需要提供setter方法name的值是通过和setter后面跟着的名称来进行对应调用的

  3. 这里的id作为获取对应名称的bean组件使用BookDao bookDao = (BookDao) context.getBean("bookDao");参数即为id的值。ref是对应当前xml文件中定义好的id值。这里的name="bookdao"<bean id="bookDao" class="com.cqut.dao.impl.bookImp1"/>中id值一一对应的。

  4. 使用spring需要导入spring-context坐标

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

2. Spring-Bean

2.1 bean相关属性

类别描述
名称bean
功能定义Spring核心容器管理的对象
属性列表id:bean的id,使用容器可以通过id值获取对应的bean,在一个容器中id值唯一
class:bean的类型,即配置的bean的全路径类名

示例代码:参上

类别描述
名称name
功能定义bean的别名,可以定义多个,使用逗号,分号;空格 分隔

示例代码:

<bean id="bookDao" name="bookImp1 bookImp12" class="com.cqut.dao.impl.bookImp1"/>
<bean id="bookService" name="bookService2;bookService3" class="com.cqut.service.BookService">
    <property ref="bookDao" name="bookdao"/>
</bean>
类别描述
名称scope
功能定义bean的作用范围
- singleton:单例(默认)
- prototype:非单例

示例代码:

<bean id="bookDao" name="bookImp1 bookImp12" scope="prototype" class="com.cqut.dao.impl.bookImp1"/>
类别描述
名称lazy-init
功能true:控制bean延迟加载

2.2 bean默认为单例

  • 在使用bean大部分是通过创建对象调用它的方式来实现业务逻辑,业务逻辑相同,不需要创建多个重复的bean。
  • 适合交给容器管理的bean:
    • 表现层对象
    • 业务层对象
    • 数据层对象
    • 工具对象
  • 不适合交给容器管理的bean:
    • 封装实体的域对象

2.3 bean实例化

2.3.1 构造方法(常用)
<bean id="bookDao" name="bookImp1 bookImp12" class="com.cqut.dao.impl.bookImp1"/>
  • 通过类的无参构造方法来示例化对象,底层是通过类的反射来实现的。即使无参构造方法限制为private任然可以构造该对象。如果该类没有无参构造方法,则会抛出异常BeanCreationException
2.3.2 静态工厂(了解)
public class BookDaoFactory {
    public static BookDao getBookDao(){
        return new bookImp1();
    }
}
<bean id="bookDaoByFactory" class="com.cqut.factory.BookDaoFactory" factory-method="getBookDao"/>
  • 这里如果没有指定factory-method则示例化出来的是factory对象,这里指定了工程中的静态方法,在加载类中,通过方法指定来执行该方法,返回创建的bookDao对象,而factory对象本身没有被创建。
  • 这里静态工厂创建默认是单例创建。
2.3.3 实例化工厂(了解)
public class BookServiceFactory {
    public BookService getBookService(){
        return new BookService();
    }
}
<bean id="bookServiceFactory" class="com.cqut.factory.BookServiceFactory"></bean>
<bean id="bookServiceByFactory" factory-bean="bookServiceFactory" factory-method="getBookService"/>
  • 这里的实例化工厂来创建对象,还需要把工程对象创建出来。在通过指定工程和方法来创建对于对应的bean。
  • 这里实例化工厂创建默认是单例创建。
2.3.4 Spring标准实例化工厂(实用)

Spring中提供了FactoryBean接口来简化xml文件bean的配置方法:(Spring标准)(实用)

public class BookDaoFactoryBean implements FactoryBean<BookDao> {
    //返回创建的实例化对象
    public BookDao getObject() throws Exception {
        return new bookImp1();
    }
	//返回创建对象的类
    public Class<?> getObjectType() {
        return BookDao.class;
    }
	//设施单例模式还是非单例模式
    public boolean isSingleton() {
        return true;
    }
}
<bean id="bookDaoFactoryBean" class="com.cqut.factory.BookDaoFactoryBean"/>
  • 这里还是会创建出工厂的实例化。直接简化了xml中的配置。

2.4 bean的生命周期

  1. 实例化:Spring容器根据配置文件或注解等方式,创建Bean的实例。
  2. 属性注入:Spring容器将Bean的属性值注入到实例中,可以通过构造函数、Setter方法或字段注入的方式进行属性注入。
  3. 初始化:Spring容器调用Bean的初始化方法,可以通过实现InitializingBean接口的afterPropertiesSet()方法或在配置文件中通过init-method属性指定初始化方法。
  4. 使用:Bean可以被其他组件或服务使用。
  5. 销毁:当Bean不再被使用时,Spring容器会调用Bean的销毁方法进行资源的释放,可以通过实现DisposableBean接口的destroy()方法或在配置文件中通过destroy-method属性指定销毁方法。
public class BookService {
    private BookDao bookDao1;
    public void save(){
        bookDao1.save();
        System.out.println("bookService save doing");
    }
    public void setBookdao(bookImp1 bookdao) {
        this.bookDao1 = bookdao;
    }
    public void init(){
        System.out.println("bookService init ...");
    }
    public void destroy(){
        System.out.println("bookService destory ...");
    }
}
    <bean id="bookService" class="com.cqut.service.BookService" init-method="init" destroy-method="destroy">
        <property ref="bookDao" name="bookdao"/>
    </bean>
  • 这里是通过xml文件来指定初始化执行函数和销毁执行函数。
public class bookImp1 implements BookDao, InitializingBean, DisposableBean {
    public void save() {
        System.out.println("bookdao saving ....");
    }

    public void destroy() throws Exception {
        System.out.println("bookImp1 destory....");
    }

    public void afterPropertiesSet() throws Exception {
        System.out.println("bookImp1 init....");
    }
}
  • 这里通过InitializingBeanDisposableBean来实现初始化和销毁函数。
  • 对于 prototype 作用域的 Bean,Spring 只负责创建,当容器创建了 Bean 的实例后,Bean 的实例就交给客户端代码管理,Spring 容器将不再跟踪其生命周期。

2.5 依赖注入方式

2.5.1 setter注入
<bean id="bookService" class="com.cqut.service.BookService">
    <property ref="bookDao" name="bookdao"/>
    <property name="database" value="mysql"/>
</bean>
  • setter注入方法类必须提供对应的setter方法。
  • 对应原则是通过setXXX()的XXX对应的。
  • 属性注入使用的是ref,基础数据类型注入使用的是value
2.5.2 构造器注入
<bean id="bookDao" class="com.cqut.dao.impl.bookImp1">
    <constructor-arg name="database" value="mysql"/>
    <constructor-arg name="password" value="13456"/>
</bean>
  • 构造器注入根据类中指定的构造方式来实例化对象,只会指定一个特定的,没有包含关系,就指定几个参数和类型的构造方法。
  • 除了通过name属性来对指定属性进行赋值,还提供了其他的方法对参数赋值。
    • 配置中使用constructor-arg标签type属性设置按形参类型注入(可能存在相同类型的参数,按位置先后顺序进行赋值)
    • 配置中使用constructor-arg标签index属性设置按形参位置注入
2.5.3 依赖注入选择
  1. 强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现
  2. 选择setting注入进行,灵活性强
  3. Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨

2.6 自动装配

  • 配置中使用bean标签autowire属性设置自动装配的类型

    将bookDao注入到bookService中:

<bean id="bookDao" class="com.cqut.dao.impl.bookImp1"/>
<bean id="bookService" class="com.cqut.service.BookService" autowire="byType"/>
  • 自动装配用于引用类型依赖注入,不能对简单类型进行操作
  • 使用按类型装配(byType)必须保证容器中相同类型的bean唯一,推荐使用
  • 使用按名称装配(byName)必须保证容器中具有指定名称的bean,变量名与配置耦合,不推荐使用
  • 自动装配优先级低于setter注入与构造器注入,同时出现自动装配配置失效

2.7 集合装配

  • 注入数组对象
<property name="xxx">
    <array>
        <value>100</value>
        <value>200</value>
        <value>300</value>
    </array>
</property>
  • 注入Map对象(重点)
<property name="xxx">
    <map>
        <entry key="country" value="china"/>
        <entry key="province" value="chongqin"/>
        <entry key="city" value="bana"/>
    </map>
</property>
  • 注入Set对象(会自动去重)
<property name="xxx">
    <set>
        <value>100</value>
        <value>200</value>
        <value>300</value>
    </set>
</property>
  • 注入Properties对象
<property name="xxx">
    <props>
    	<prop key="country">china</prop>
        <prop key="province">chongqin</prop>
        <prop key="city">bana</prop>
    </props>
</property>

2.8 加载properties文件

  • 开启context命名空间
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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
">
</beans>
  • 使用context命名空间,加载指定properties文件
<context:property-placeholder location="jdbc.properties"/>
  • 使用${}读取加载的属性值
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/data_01
jdbc.username=root
jdbc.password=123456
<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>

properties文件加载设置

  • 不加载系统属性
<context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>
  • 加载多个properties文件
<context:property-placeholder location="jdbc.properties,xxx.properties"/>
  • 加载所有properties文件标准格式(只会加载类路径下的properties文件)
<context:property-placeholder location="classpath:*.properties"/>
  • 从类路径或jar包中搜索并加载properties文件
<context:property-placeholder location="classpath*:*.properties"/>

2.9 容器创建&bean获取方式

容器创建:

  • 类路径加载配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("Application.xml");
  • 文件路径加载配置文件
ApplicationContext context = new FileSystemXmlApplicationContext("D:\\Application.xml");
  • 加载多个配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml", "bean2.xml");

bean获取方式:

  • 使用bean名称获取
BookDao bookDao = (BookDao) context.getBean("bookDao");
  • 使用bean名称获取并指定类型
BookDao bookDao = context.getBean("bookDao", BookDao.class);
  • 使用bean类型获取(配置文件中只能有一个对应的bean)
BookDao bookDao = context.getBean(BookDao.class);

附录了解:

Resource resources = new ClassPathResource("Application.xml");
BeanFactory bf = new XmlBeanFactory(resources);
BookDao bookDao = bf.getBean(BookDao.class);
  • BeanFactory是IoC容器的顶层接口,初始化后,所有的bean均延迟加载,不会立即执行执行实例化bean
  • ApplicationContext接口时Spring容器的核心接口,初始化时bean立即加载

3. 注解开发

  • 使用@Component定义bean

    Spring提供@Component注解的三个衍生注解

    • @Controller:用作表现层bean定义
    • @Service:用作业务层bean定义
    • @Repository:用作数据层bean定义
    • @component注解可以放在接口上,也可以放在实现类上。如果放在接口上,表示这个接口是一个组件,Spring会自动扫描并创建它的实现类的实例。如果放在实现类上,表示这个实现类是一个组件,Spring会自动扫描并创建它的实例。个人推荐放在实现类上,一个接口会有多个实现类,不好处理。
@Service
public class UserService {
    public void save(){
        System.out.println("UserService save doing ...");
    }
}
@Repository
public class UserDaoImp implements UserDao {
    public void save() {
        System.out.println("UserDaoImp save doing ...");
    }
}
  • @Configuration注解用于设定当前类为配置类,替代了xml配置文件
  • @ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据使用数组格式
@Configuration
@ComponentScan({"com.cqut.dao", "com.cqut.service"})
public class SpringConfig {
}
  • Spring3.0开启纯注解开发模式,读取Spring核心配置文件初始化容器对象切换为读取Java配置类初始化容器对象
public class App01 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = 
            new AnnotationConfigApplicationContext(SpringConfig.class);
        UserDao userDao = context.getBean(UserDao.class);
        userDao.save();
    }
}
  • 使用@Scope定义bean作用范围

    这里设置为非单例模式,通过class来获取bean对象,每次返回的是不同的对象

@Repository
@Scope("prototype")
public class UserDaoImp implements UserDao {
    public void save() {
        System.out.println("UserDaoImp save doing ....");
    }
}
  • 使用@PostConstruct@PreDestory定义bean生命周期
@Repository
public class UserDaoImp implements UserDao {
    public void save() {
        System.out.println("UserDaoImp save doing ....");
    }
    @PostConstruct
    public void init(){
        System.out.println("init doing ...");
    }
    @PreDestroy
    public void destroy(){
        System.out.println("destroy doing ...");
    }
}
  • 使用@Autowired注解开启自动装配模式(按类型)
    • 自动装配基于反射设计创建对象并暴力反射对应属性为私有属性初始化数据,无需提供setter方法
    • 自动装配建议使用无参构造方法创建对象(默认),如果不提供对应构造方法,需要提供唯一的构造方法
  • 使用@Qualifiter注解开启指定名称装配bean(@Qualifier注解无法单独使用,必须配合@Autowired注解使用)
@Service
public class UserService {
    @Autowired
    @Qualifier("userDao")
    private UserDao userDao;
    public void save(){
        userDao.save();
        System.out.println("UserService save doing ...");
    }
}
  • 使用@Value实现简单类型注入
  • 使用@PropertySource注解加载properties文件(路径仅支持单一文件配置,多文件使用数组格式配置,不允许使用通配符*
  • 使用@Bean配置第三方bean
    • 引用类型注入只需要为bean定义方法设置形参即可,容器根据类型自动装配对象
@Configuration
public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;
    @Bean
    public DataSource dataSource(UserDao userDao){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }
}
  • 将独立的配置类加入核心配置

    • 扫描式:使用@ComponentScan注解扫描配置类所在的包,加载对应配置类信息
    @Configuration
    @ComponentScan({"com.cqut.dao", "com.cqut.service", "com.cqut.config"})
    @PropertySource({"classpath:jdbc.properties"})
    public class SpringConfig {
    }
    
    • 导入式:使用@Import注解手动加入配置类到核心配置,此注解只能添加一次,多个数据使用数组格式(可以不用加@Configuration注解)
    @Configuration
    @ComponentScan({"com.cqut.dao", "com.cqut.service"})
    @Import({JdbcConfig.class})
    @PropertySource({"classpath:jdbc.properties"})
    public class SpringConfig {
    }
    

4. AOP基础学习

4.1. AOP核心概念

  • 连接点(Join Point):程序执行过程中的任意位置,粒度为执行方法,抛出异常,设置变量等
    • 在SpringAOP中,理解为方法的执行
  • 切入点(Pointcut):匹配连接点的式子
    • 在SpringAOP中,一个切入点可以只描述一个具体方法,也可以匹配多个方法
      • 一个具体方法
      • 匹配多个方法:所有save方法,所有get开头的方法,所有以Dao结尾的接口中的任意方法,所有带一个参数的方法等
  • 通知(Active):在切入点出执行的操作,共性功能
    • 在SpringAOP中,功能最终以方法的形式呈现
  • 通知类:定义通知的类
  • 切面(Aspect):描述通知和切入点的对应关系

4.2. AOP基本使用

  1. 导入AOP相关坐标
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.19</version>
    <scope>runtime</scope>
</dependency>
  1. 定义dao接口与实现类
  2. 定义通知类,制作通知
  3. 定义切入点
  4. 绑定切入点与通知关系,并指定通知添加到连接点到原始连接点的具体执行位置
  5. 定义通知类受Spring容器管理,并定义当前类为切面类
/**
 * 定义通知类受Spring容器管理,并定义当前类为切面类
 */
@Component
@Aspect
public class UserDaoAop {

    /**
     * 定义切入点
     * 切入点定义依托一个不具有实际意义的方法进行,无参数,无返回值,方法无实际逻辑
     */
    @Pointcut("execution(void com.cqut.dao.UserDao.save())")
    private void p1(){}

    /**
     * 定义通知
     * 绑定切入点与通知关系,并指定通知添加到原始连接点的具体执行位置
     */
    @Before("p1()")
    public void printTime(){
        System.out.println(System.currentTimeMillis());
    }
}
  1. 开启Spring对AOP注解驱动支持
@Configuration
@ComponentScan({"com.cqut.dao", "com.cqut.service", "com.cqut.aop"})
@Import({JdbcConfig.class})
@PropertySource({"classpath:jdbc.properties"})
//开启Spring对AOP注解驱动支持
@EnableAspectJAutoProxy
public class SpringConfig {
}

4.3. AOP工作流程

  1. Spring容器启动
  2. 读取所有切面配置的切入点
  3. 初始化bean,判定bean对应的类中的方法是否匹配到任意切入点
    • 匹配失败,创建对象(原始对象)
    • 匹配成功,创建原始对象(目标对象)的代理对象
  4. 获取bean执行方法
    • 获取bean,调用方法并执行,完成操作
  • 目标对象(Target):原始对象去掉共性功能对应的类产生的对象,这种对象是无法直接完成最终工作的
  • 代理(Proxy):目标对象无法直接完成工作,需要对其进行功能回填,通过原始对象的代理对象实现

4.4. AOP切入点表达式

  • 标准格式:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数)异常名)
execution(void com.cqut.dao.UserDao.save())
  • 可以使用通配符描述切入点

    • *:单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现
    excution(public * com.cqut.*.UserService.find*(*))
    
    • ..:多个连续的任意符号,可以独立出现,常用作简化包名与参数的书写
    excution(public * com..UserService.find*(..))
    
    • +专门用于匹配子类类型
    excution(* * com..*Service+.*(..))
    
  • 书写规范

    • 所有代码按照标准规范开发
    • 描述切入点通常描述接口,而不是描述实现类
    • 访问控制修饰符针对开发接口均采用public描述(可省略访问控制修饰符描述)
    • 返回值类型对于增删改类使用精准类型加速匹配,对于查询类使用*通配快速描述
    • 包名书写尽量不使用..匹配,效率过低,常用*做单个包描述匹配,或精准匹配
    • 接口名/类名书写名称与模块相关的采用*匹配,例如UserService书写成*Service,绑定业务层接口名
    • 方法名书写以动词进行精准匹配,名词采用*匹配,例如getById书写成getBy*
    • 通常不使用异常作为匹配规则

4.5. AOP通知类型

  • @Before:当前通知方法在原始切入点方法前运行
    • JoinPoint对象描述了连接点方法的运行状态,可以获取到原始方法的调用参数
    • ProceedJoinPoint是JoinPoint的子类
    @Before("p1()")
    public void before(JoinPoint joinPoint){
        System.out.println("UserDao Before doing ...");
    }
  • @After:当前通知方法在原始切入点方法后运行
    @After("p1()")
    public void after(JoinPoint joinPoint){
        System.out.println("UserDao After doing ...");
    }
  • @AfterThrowing:当前通知方法在原始切入点方法运行抛出异常后执行
    • 抛出异常后通知可以获取切入点方法中出现的异常信息,使用形参可以接收对应的异常对象
    @AfterThrowing(value = "p1()", throwing = "ret")
    public void throwable(Throwable ret){
        System.out.println(ret);
    }
  • @AfterReturning当前通知方法在原始切入点方法正常执行完毕后运行
    @AfterReturning(value = "p1()", returning = "ret")
    public void afterReturning(JoinPoint joinPoint, Object ret){
        System.out.println(ret);
    }
  • @Around:当前通知方法在原始切入点方法前后运行
    • 环绕通知必须依赖形参ProceedingJoinPoint才能实现对原始方法的调用,进而实现原始方法调用前后同时添加通知
    • 通知如果未使用ProceedingJoinPoint对原始方法进行调用将跳过原始方法的执行
    • 对原始方法的调用可以不接收返回值,通知方法的返回值类型设置成void即可,如果接收返回值,也可以设置为Object类型
    • 原始方法的返回值如果式void类型,通知方法的返回值类型可以设置为void,也可以设置成Object
    • 由于无法预知原始方法运行后是否会抛出异常,因此环绕通知方法必须抛出Throwable对象
    @Pointcut("execution(* com.cqut.*.*Service.getBy*(..))")
    private void service(){}
    @Around("service()")
    public Object deal(ProceedingJoinPoint point) throws Throwable {
        Object[] args = point.getArgs();
        for (int i = 0; i < args.length; i++){
            if(args[i].getClass().equals(String.class)){
                args[i] = args[i].toString().trim();
            }
        }
        System.out.println("Aroud Before ...");
        Object proceed = point.proceed(args);
        System.out.println("Aroud After ...");
        return proceed;
    }
}

5. Spring事务管理

5.1. 事务简介

  • 事务作用:在数据层保障一系列的数据库操作同成功同失败

  • Spring事务作用:在数据层或者业务层保障一系列的操作库操作同成功同失败

  • 实现步骤:

    • 在业务层接口上添加Spring事务管理
      • Spring注解式事务通常添加在业务层接口中而不会添加业务层实现类中,降低耦合
      • 注解式事务可以添加到业务方法上表示当前方法开启事务,也可以添加到接口上表示当前接口所有方法开启事务
    public interface AccountService {
        @Transactional
        public void transport();
    }
    
    • 设置事务管理器
      • 这里的dataSource和mybatis中的dataSource要是同一个对象
        @Bean
        public PlatformTransactionManager transactionManager(DataSource dataSource){
            DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
            transactionManager.setDataSource(dataSource);
            return transactionManager;
        }
    
    • 开启注解式事务驱动
    @Configuration
    @ComponentScan({"com.cqut.dao", "com.cqut.service", "com.cqut.aop"})
    @Import({JdbcConfig.class})
    @PropertySource({"classpath:jdbc.properties"})
    //开启Spring对AOP注解驱动支持
    @EnableAspectJAutoProxy
    @EnableTransactionManagement
    public class SpringConfig {
    }
    
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.3.18</version>
    </dependency>
    

5.2. 事务角色

  • 事务管理员:发起事务方,在Spring中通常指代业务层开启事务的方法
  • 事务协调员:加入事务方,在Spring中通常指代数据层方法,也可以是业务层方法

5.3. 事务属性

  • 自行查阅相关设置。
  • 设置事务传播行为REQUIRES_NEW(需要新事物)
@Transactional(propagation = Propagation.REQUIRES_NEW)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值