Spring

Spring框架概述

Spring官网地址

Spring概述

Spring的两个核心:IoC控制反转&AOP面向切面编程
Spring是一个框架,半成品软件,它是一个容器管理对象,Spring是存储对象的容器

Spring优点

①轻量;使用的jar小,占用资源少,运行效率高,不依赖其他jar
②针对接口编程,解耦合;原来在代码中对象的创建方式,现在由容器完成,实现模块、类之间的解耦合,降低代码耦合度,把关系变得松散,让程序的改动、升级更容易。
③AOP编程的支持;OOP不容易实现的用AOP方便许多,通过声明方式进行事务管理。
④方便集成各种优秀框架;Spring提供了各种框架的直接支持,可以很好的集成其他框架 如MyBatis,只需对Spring一个框架进行编写即可。

Spring体系结构

在这里插入图片描述

★IoC控制反转

IoC控制反转概述

Inversion of Control(IoC)控制反转:是一个概念,是一种思想
控制:创建对象,给对象的属性赋值,对象之间的关系管理。
反转:把原来的开发人员管理,创建对象的权限转移给代码之外的容器实现。 由容器代替开发人员管理对象。创建对象,给属性赋值。
正转:由开发人员在代码中,使用new 构造方法创建对象, 开发人员主动管理对象。
控制反转就是对对象控制权的转移,从程序本身反转(转移)到了外部容器,通过容器实现对象的创建、属性赋值、依赖的管理。
容器:是一个服务器软件,是一个框架(Spring)

使用IoC的原因:减少对代码的改动,也能实现不同的功能,IoC实现对象之间的解耦合。
Spring框架使用依赖注入(DI)实现IoC
IoC的实现:
依赖注入DI(Dependency Injection):程序代码不做定位查询,这些工作由容器自行完成。

Spring的第一个程序

在这里插入图片描述
配置文件applicationContext.xml
在实际开发中建议使用多配置文件
1.多配置文件占用资源小,效率高
2.避免多人竞争带来的冲突(多个人开发一项目)
建议按功能模块划分,一个模块一个配置文件;
按类的功能划分,如数据库相关的一个、事务功能的一个、service的一个…

主配置文件包含其他配置文件

<import resource="classpath:spring-school.xml"/>
<import resource="classpath:spring-student.xml"/>

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--
    spring的配置文件:
    1.beans:spring把java对象称为bean
    2.spring-beans.xsd是约束文件
-->
<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">

<!--
    告诉Spring创建对象
    声明bean,就是告诉Spring要创建某个类对象
    id:对象的自定义名称,是唯一值,Spring通过这个名称找到对象
    class:类的全限定名称(不能是接口,因为Spring是反射机制创建对象,必须使用类)

    Spring就完成了SomeService someService = new SomeServiceImpl();对象的创建
    Spring是把创建好的对象放入map集合中,Spring框架有一个map用来存放对象的

    一个bean标签声明一个对象
-->
    <bean id="someService" class="com.whh.service.impl.SomeServiceImpl"/>

</beans>

容器创建对象

//使用传统方式创建对象
@Test
public void test(){
    SomeService someService = new SomeServiceImpl();
    someService.doSome();
}

//使用Spring容器创建对象
@Test
public void testSpring(){
    //使用Spring容器创建对象
    //1.指定Spring配置文件的名称
    String config = "beans.xml";
    //2.创建表示Spring容器的对象ApplicationContext
    //ApplicationContext就是Spring容器,通过容器创建对象  在这行创建的对象
    ApplicationContext ac = new ClassPathXmlApplicationContext(config);
    //3.从容器获取某个对象调用对象的方法
    SomeService service = (SomeService) ac.getBean("someService");
    //4.使用Spring容器创建好的对象
    service.doSome();
}

在容器创建时,会在容器中创建全部对象
创建容器的同时创建了该容器内的全部对象,一个bean标签一个对象,调用的是该类的空参构造器

//创建容器,同时创建了该容器内的全部对象,一个bean标签一个对象,调用的是该类的空参构造器
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");

一个bean标签声明一个对象,创建一个对象
<bean id="someService" class="com.whh.service.impl.SomeServiceImpl"/>

容器创建对象&给java对象属性赋值

di(依赖注入):表示创建对象,给对象属性赋值
di的实现有两种:
1.在Spring配置文件中,使用标签和属性完成,叫做基于XML的di实现
2.使用Spring中的注解,完成属性赋值,叫做基于注解的di实现(常用)

1.基于XML的di实现

1.set注入(设置注入):Spring调用set方法实现属性的赋值(常用)
①简单类型(属性类型;基本数据类型+String)的set注入

<!--简单类型(属性类型)的set注入,其实就是调用set方法赋值-->
<bean id="student" class="com.whh.di.Student">
    <property name="name" value="张三"></property><!--调用setName("张三"),只调用setName方法,与属性无关-->
    <property name="age" value="18"></property><!--调用setAge(18)-->
</bean>

②引用类型的set注入

<bean id="student" class="com.whh.di.Student">
    <property name="name" value="张三"></property><!--调用setName("张三")-->
    <property name="age" value="18"></property><!--调用setAge(18)-->
    <!--引用数据类型(属性类型)的set注入-->
    <property name="school" ref="mySchool"/><!--要求传入School对象,在下方再写一个bean标签即可-->
</bean>

<bean id="mySchool" class="com.whh.di.School">
    <property name="address" value="上海"></property>
    <property name="year" value="2000"></property>
</bean>

2.构造注入:Spring调用类的有参构造器创建对象并给属性赋值

<bean id="student" class="com.whh.di2.Student">
    <constructor-arg name="name" value="张三"/><!--根据构造器中的属性名赋值-->
    <constructor-arg name="age" value="20"/>
    <constructor-arg name="school" ref="mySchool"/>
</bean>
<bean id="mySchool" class="com.whh.di2.School">
    <constructor-arg name="address" value="上海"/>
    <constructor-arg name="year" value="2000"/>
</bean>

引用类型属性自动注入
①引用类型的byName方式自动注入

    <bean id="student" class="com.whh.di3.Student" autowire="byName">
        <property name="name" value="张三"></property>
        <property name="age" value="20"></property> 
<!--        <property name="school" ref="school"></property>-->
    </bean>
    <bean id="school" class="com.whh.di3.School"><!--id值与引用数据类型的属性名一致-->
        <property name="address" value="上海"></property>
        <property name="year" value="2000"></property>
    </bean>

②引用类型的byType方式自动注入
byType(按类型注入) : java类中引用类型的数据类型和spring容器中(配置文件)的class属性是同源关系的,这样的bean能够赋值给引用类型
同源就是一类的意思:
1.java类中引用类型的数据类型和bean的class的值是一样的。
2.java类中引用类型的数据类型和bean的class的值父子类关系的。
3.java类中引用类型的数据类型和bean的class的值接口和实现类关系的

2.基于注解的di实现(常用)

①定义 Bean 的注解@Component(用于创建对象的注解)
1.在需要的类上加上@Component注解

/*
    @Component(value = "myStudent")等同于
    <bean id="myStudent" class="com.whh.annotation1.Student"/>
    注解加在类名前
 */
@Component(value = "myStudent")
public class Student {
}

2.在配置文件applicationContext.xml加上组件扫描器

<!--
    组件扫描器
    Spring会扫描遍历base-package指定的包和子包中的所有类,找到类中的注解
    按照注解的功能创建对象或给属性赋值
-->
<context:component-scan base-package="com.whh.annotation1"/>

另外,Spring 还提供了 3 个创建对象的注解:
@Repository 用于对 DAO 实现类进行注解 ,即持久层对象(用在持久层类的上面)
@Service 用于对 Service 实现类进行注解 ,即业务层对象(用在业务层类的上面)
@Controller 用于对 Controller 实现类进行注解,控制层对象(用在控制层类的上面)
以上三个注解的使用和语法和@Component是一样的,都能创建对象,但是这三个还有额外功能
如果创建的对象的类是dao实现类则用@Repository…若不是这三种类创建对象则用@Component

②简单类型属性注入@Value

@Value("张三")
private String name;
@Value("20")
private Integer age;

引用数据类型属性赋值
Spring中通过注解给引用数据类型赋值,使用的是自动注入原理,支持byName、byType
③byType 自动注入@Autowired

//按byType注入,找到School类型的同源对象
@Autowired
private School school;

④byName 自动注入@Autowired和@Qualifier

//按byName注入
@Autowired//里的require属性默认为true,为true时若没赋值上会报错,为false时,没赋值上会继续向下执行
@Qualifier("mySchool")
private School school;

⑤JDK 注解@Resource 自动注入
使用@Resourcr注解需要添加依赖(JDK11后没有集成该注解)

<!--JDK注解@Resource依赖-->
<dependency>
  <groupId>javax.annotation</groupId>
  <artifactId>javax.annotation-api</artifactId>
  <version>1.3.2</version>
</dependency>
//默认是byName自动注入,若byName赋值失败,再使用byType
@Resource
private School school;

//设置只用byName赋值
@Resource(name = "mySchool")
private School school;

基于XML的di实现与基于注解的di实现对比

开发中注解为主,XML配置文件为辅
注解优点:方便、直观、高效
XML优点:配置和代码是分离的;在xml 中做修改无需编译代码
XML缺点:编写麻烦,效率低,大型项目过于复杂

★AOP面向切面编程

动态代理

动态代理:在原有程序代码不改动时,可以增加额外的功能(功能增强)装饰器模式
动态代理/AOP的作用:
1)在目标类源代码不改变的情况下,增加功能。
2)减少代码的重复。
3)专注业务逻辑代码。
4)解耦合,让你的业务功能和日志,业务非业务功能分离。

AOP简介

AOP(Aspect Orient Programming)面向切面编程,是从动态角度考虑程序运行过程。
Aspect 切面:给目标类增加的功能就是切面;特点:一般都是非业务方法,独立使用的。

AOP底层就是采用动态代理模式实现的,采用了JDK的动态代理(有接口)与CGLIB的动态代理
AOP就是动态代理的规范化,把动态代理的实现步骤、方式都定义好了,让开发人员用一种统一的方式使用动态代理。
AOP面向切面编程的优点:1.减少重复2.专注业务;减少代码重复,专注业务实现。
怎么理解面向切面编程:
1.需要在分析项目功能时,找出切面
2.合理的安排切面的执行时间
3.合理的安全切面执行的位置,在哪个类,哪个方法增加增强功能
4.面向切面编程是面向对象编程的一种补充

AOP编程术语

①切面(Aspect)
表示增强的功能,就是一堆代码完成某一个非业务功能。常见的切面功能有:日志、事务、统计信息、参数检测、权限验证
②连接点(JoinPoint)
连接业务方法和切面的位置,指可以被切面织入的具体方法,通常业务接口中的方法均为连接点

@Before(value = "execution(void doSome(String, Integer))")
public void myBefore(JoinPoint joinPoint){//只能放在形参列表的第一个位置
}

③切入点(PointCut)
指声明的一个或多个连接点JoinPoint的集合,通过切入点指定一组方法

④目标对象(Target)
给哪个类的方法增加功能,这个类就是目标对象
⑤通知(Adivice)
表示切面功能执行的时间

切面的三个要素:
1)切面的功能代码,切面干什么—切面(Aspect)
2)切面的执行位置,使用Pointcut表示切面执行的位置—切入点(Pointcut)
3)切面的执行时间,使用Advice表示时间,在目标方法之前,还是目标方法之后。—通知

AOP的实现

AOP的技术实现框架:
1.Spring:Spring在内部实现了AOP规范,能做AOP的工作
2.aspectJ:一个专门做AOP的开源框架,SPring集成了aspectJ框架(常用)
AspectJ框架实现AOP的两种方式:
1.使用XML的配置文件实现:配置全局事务
2.使用注解实现(常用)

AspectJ基于注解的AOP实现

切面类(额外增加的功能)

/*
    @Aspect注解表示当前类是切面类
*/
@org.aspectj.lang.annotation.Aspect
    public class Aspect {
    /**
     * 定义方法,方法是实现切面功能的
     * 方法的定义要求:
     * 1方法是公共public的
     * 2.方法没有返回值
     */
    /**
     * @Before前置通知注解
     * 属性value,是切入点表达式,表示切面的功能执行的位置
     */
    @Before(value = "execution(public void com.whh.annotation1.SomeServiceImpl.doSome(String, Integer))")
    public void myBefore(){
        //就是切面要执行的功能代码
        System.out.println("前置通知,切面功能为当前时间:"+new Date());
    }

在这里插入图片描述
applicationContext.xml配置文件

<!--声明目标对象-->
<bean id="someServiceImpl" class="com.whh.annotation1.SomeServiceImpl"/>

<!--声明切面类对象-->
<bean id="aspect" class="com.whh.annotation1.Aspect"/>
<!--声明自动代理生成器:使用aspectj框架内部的功能,创建目标对象的代理对象。
        创建代理对象是在内存中实现的, 修改目标对象的内存中的结构。 创建为代理对象
        所以目标对象就是被修改后的代理对象.
        aspectj-autoproxy:会把spring容器中的所有的目标对象,一次性都生成代理对象。
-->
<aop:aspectj-autoproxy /> 自动代理扫描器

AspectJ 的通知类型

@Before前置通知

/**
 * 定义方法,方法是实现切面功能的
 * 方法的定义要求:
 * 1方法是公共public的
 * 2.方法没有返回值
 */
/**先执行切面功能再执行原方法
 * @Before前置通知注解
 * 属性value,是切入点表达式,表示切面的功能执行的位置
 */
@Before(value = "execution(public void com.whh.annotation1.SomeServiceImpl.doSome(String, Integer))")
public void myBefore(JoinPoint joinPoint){}

@AfterReturning后置通知

/**
 * 定义方法,方法是实现切面功能的
 * 方法的定义要求:
 * 1方法是公共public的
 * 2.方法没有返回值
 * 3.方法有参数,推荐是Object类型
 */
/**是在目标方法的后面增强功能
 * AfterReturning后置通知
 * 1.value 切入点表达式
 * 2.returning 自定义变量 表示目标方法的返回值 Object res = doOther()返回值
 * @param res
 */
@AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))",
                returning = "res")
public void myAfterReturning(Object res){}

@Around环绕通知

/**
 * 定义方法,方法是实现切面功能的
 * 方法的定义要求:
 * 1方法是公共public的
 * 2.方法有一个返回值,推荐是Object
 * 3.方法有参数,必须是参数ProceedingJoinPoint
 */
/**
 * @Around环绕通知
 * 1.是功能最强的通知
 * 2.在目标方法的前和后都能增强功能
 * 3.控制目标方法是否被调用执行
 * 4.可以修改原来的目标方法的执行结果
 * 等同于JDK动态代理InvocationHandler接口
 * 参数ProceedingJoinPoint等同于Method 用于执行目标方法
 * 返回值 就是目标方法的返回值
 * 环绕通知常用于事务的使用
 */
@Around(value = "execution(* *..SomeServiceImpl.doFirst(..))")
public Object myAround(ProceedingJoinPoint pj) throws Throwable {
    System.out.println("目标方法前的增强功能");
    //1.目标方法调用
    Object result = pj.proceed();//相对于Object result = doFirst()
    //2.在目标方法前或后增强功能
    System.out.println("目标方法前的增强功能");
    return result;
}

@AfterThrowing异常通知
@After最终通知
@Pointcut

@Around(value = "myPointcut()")
public Object myAround(ProceedingJoinPoint pj) throws Throwable {
    System.out.println("目标方法前的增强功能");
    //1.目标方法调用
    Object result = pj.proceed();//相对于Object result = doFirst()
    //2.在目标方法前或后增强功能
    System.out.println("目标方法前的增强功能");
    return result;
}
@Pointcut(value = "execution(* *..SomeServiceImpl.doFirst(..))")
public void myPointcut(){
    //无需代码
}

AspectJ框架的使用

①切面的执行时间Advice(通知、增强),在AspectJ框架中使用注解表示的,也可使用xml

②AspectJ的切入点表达式
AspectJ 定义了专门的表达式用于指定切入点。表达式的原型是:

execution(modifiers-pattern? ret-type-pattern 
    declaring-type-pattern?name-pattern(param-pattern) 
    throws-pattern?)

execution(访问权限 方法返回值 方法声明(参数) 异常类型)

modifiers-pattern 访问权限类型
ret-type-pattern 返回值类型
declaring-type-pattern 包名类名
name-pattern(param-pattern) 方法名(参数类型和参数个数)
throws-pattern 抛出异常类型
?表示可选的部分

在这里插入图片描述
execution(访问权限类型 方法返回值类型 方法声明(参数) 异常类型)

★Spring集成MyBatis

IoC能把mybatis和spring集成在一起,像一个框架, 是因为ioc能创建对象。可以把mybatis框架中的对象交给spring统一创建, 开发人员从spring中获取对象。开发人员就不用同时面对两个或多个框架了, 就面对一个spring

我们需要让spring(applicationContext.xml)创建以下对象
1.独立的连接池类的对象, 使用阿里的druid数据库连接池
2.SqlSessionFactory对象
3.创建出dao(dao接口)对象

MyBatis主配置文件mybatis.xml
1.主配置文件中不再需要数据源的配置了。因为数据源要交给 Spring 容器来管理了。(连接数据库)
现在连接数据库在applicationContext.xml中进行了,不在mybatis.xml中了
2.这里对 mapper 映射文件的注册,使用标签,即只需给出 mapper 映射文件 所在的包即可。因为 mapper 的名称与 Dao 接口名相同,可以使用这种简单注册方式。这种 方式的好处是,若有多个映射文件,这里的配置也是不用改变的。当然,也可使用原来的标签方式
mybatis.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <settings>
        <!--设置mybatis输出日志-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

    <!--设置别名-->
    <typeAliases>
        <!--name:实体bean/实体类所在的包名
            表示com.whh.bean包中的列名就是别名
            你可以使用Student表示com.whh.bean.Student
        -->
        <package name="com.whh.bean"/>
    </typeAliases>

    <mappers>
        <!-- sql mapper(sql映射文件)的位置-->
        <!--name:是包名,这个包中的所有mapper.xml一次都能加载-->
        <package name="com.whh.dao"/>
    </mappers>
</configuration>

Spring配置德鲁伊druid数据库连接池连接数据库
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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

        <!--声明数据源DataSource,作用是连接数据库的-->
        <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource"
              init-method="init" destroy-method="close">
                <!--set注入给DruidDataSource提供连接数据库信息-->
                <property name="url" value="jdbc:mysql://localhost:3306/ssm?serverTimezone=GMT&amp;allowPublicKeyRetrieval=true&amp;useSSL=false&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
                <property name="maxActive" value="20"/>
        </bean>

        <!--声明的是mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
                <property name="dataSource" ref="myDataSource"/>
                <!--mybatis主配置文件的位置-->
                <property name="configLocation" value="classpath:mybatis.xml"/>
        </bean>

        <!--创建dao对象,使用SqlSession的getMapper(StudentDao.class)
        MapperScannerConfigurer:在内部调用getMapper()生成每个dao接口的代理对象-->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
                <!--指定SqlSessionFactory对象的id-->
                <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
                <!--扫描这个包的所有接口,把每个接口都执行一次getMapper方法,得到每个接口的dao对象-->
                <property name="basePackage" value="com.whh.dao"/>
        </bean>
        
        <!--声明service-->
        <bean id="studentService" class="com.whh.service.impl.StudentServiceImpl">
                <property name="studentDao" ref="studentDao"/>
        </bean>
</beans>

★Spring事务

Spring的事务管理

事务原本是数据库中的概念,在Dao层。但一般情况下,需要将事务提升到业务层,因为在service层会调用多个dao方法,即多个sql语句。这样做是为了能够使用事务的特性来管理具体的业务。
在 Spring 中通常可以通过以下两种方式来实现对事务的管理:
(1)使用 Spring 的事务注解管理事务
(2)使用 AspectJ 的 AOP 配置管理事务

Spring提供了一种处理事务的统一模型,使用统一步骤、方式完成多种不同数据库访问技术(mybatis、jdbc、hibernate…)的事务处理
spring添加事务

事务管理器接口

事务管理器是 PlatformTransactionManager 接口和它的众多实现类,定义了事务的重要方法commit、rollback。其主要用于完成事务的提交、回滚,及获取事务的状态信息。
PlatformTransactionManager实现类:
DataSourceTransactionManager:使用 JDBC 或 MyBatis 进行数据库操作时使用。
HibernateTransactionManager:使用 Hibernate 进行持久化数据时使用。

并发事务

并发问题:
脏读:一个事务读取了其他事务还没有提交的数据,读到的是其他事务“更新”的数据
幻读:一个事务读取了其他事务还没有提交的数据,只是读到的是 其他事务“插入”的数据
不可重复读:一个事务多次读取,结果不一样
可以通过设置隔离级别来解决并发问题
事务隔离级别:
READ_UNCOMMITTED:读未提交。未解决任何并发问题。
READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。
REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读
SERIALIZABLE:串行化。不存在并发问题。

在这里插入图片描述

七个事务传播行为

PROPAGATION_REQUIRED
PROPAGATION_REQUIRES_NEW
PROPAGATION_SUPPORTS

PROPAGATION_MANDATORY
PROPAGATION_NESTED
PROPAGATION_NEVER
PROPAGATION_NOT_SUPPORTED

事务提交、回滚机制

1)当你的业务方法,执行成功,没有异常抛出,当方法执行完毕,spring在方法执行后提交事务。调用事务管理器commit
2)当你的业务方法抛出运行时异常或ERROR, spring执行回滚,调用事务管理器的rollback
运行时异常的定义: RuntimeException 和他的子类都是运行时异常, 例如NullPointException , NumberFormatException
3) 当你的业务方法抛出非运行时异常, 主要是受查异常时,提交事务
受查异常:在你写代码中,必须处理的异常。例如IOException, SQLException

使用 Spring 的事务注解管理事务

适合中小项目使用的,放在public方法的上面,表示当前方法具有事务。只对public有效,放在类上则说明该类的所有public方法都具有事务。
通过@Transactional 注解方式,可将事务织入到相应 public 方法中,实现事务管理。

使用@Transactional的步骤:
1.需要声明事务管理器对象(applicationContext.xml)

<!--使用Spring的事务管理-->

<!--1.声明事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--连接的数据库,指定数据源-->
        <property name="dataSource" ref="myDataSource"/>
</bean>

<!--2.开启事务注解驱动,告诉Spring使用注解管理事务,创建代理对象-->
        <!--transaction-manage:事务管理器对象的id-->
<tx:annotation-driven transaction-manager="transactionManager"/>

2.开启事务注解驱动, 告诉spring框架,我要使用注解的方式管理事务。(这步spring框架内部完成了,不需要我们写!)
spring使用aop机制,创建@Transactional所在的类代理对象,给方法加入事务的功能。
spring给业务方法加入事务:
在你的业务方法执行之前,先开启事务,在业务方法之后提交或回滚事务,使用aop的环绕通知
@Around(“你要增加的事务功能的业务方法名称”)
Object myAround(){
开启事务,spring给你开启
try{
buy(1001,10);
spring的事务管理器.commit();
}catch(Exception e){
spring的事务管理器.rollback();
}
}

3.在public方法(spring只扫描public的方法)的上面加入@Trancational注解

@Transactional(
        propagation = Propagation.REQUIRED,
        isolation = Isolation.DEFAULT,
        readOnly = false,
        rollbackFor = { //rollbackFor:表示发生指定的异常一定回滚
                NullPointerException.class,NotEnoughException.class
        }
)//这一段其实与只写@Transactional是一样的,内部都是默认的
@Override
public void buy(Integer goodsId, Integer nums) {}

使用 AspectJ 的 AOP 配置管理事务

适合大型项目,有很多的类,方法,需要大量的配置事务,使用aspectj框架功能,在spring配置文件中声明类,方法需要的事务。这种方式业务方法和事务配置完全分离,都是在xml配置文件中实现的

使用AspectJ的步骤:
1.要使用的是aspectj框架,需要加入pom依赖

   <dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-aspects</artifactId>
		<version>5.2.5.RELEASE</version>
    </dependency>

2.声明事务管理器对象

<!--1.声明事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="myDataSource"/>
</bean>

3.声明方法需要的事务类型(配置方法的事务属性【隔离级别,传播行为,超时】)

<!--2.声明业务方法它的事务属性(隔离级别,传播行为,超时时间)
          id:自定义名称,表示 <tx:advice> 和 </tx:advice>之间的配置内容的
          transaction-manager:事务管理器对象的id        -->
<tx:advice id="myAdvice" transaction-manager="transactionManager">
        <!--tx:attributes:配置事务属性-->
        <tx:attributes>
                <!--tx:method:给具体的方法配置事务属性,method可以有多个,分别给不同的方法设置事务属性
                    name:方法名称,1)完整的方法名称,不带有包和类。
                                  2)方法可以使用通配符,* 表示任意字符
                    propagation:传播行为,枚举值
                    isolation:隔离级别
                    rollback-for:你指定的异常类名,全限定类名。 发生异常一定回滚
                -->
                <tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"
                           rollback-for="java.lang.NullPointerException,com.whh.exception.NotEnoughException"/>

                <!--使用通配符,指定很多的方法-->
                <tx:method name="add*" propagation="REQUIRES_NEW" />
                <!--指定修改方法-->
                <tx:method name="modify*" />
                <!--删除方法-->
                <tx:method name="remove*" />
                <!--查询方法,query,search,find-->
                <tx:method name="*" propagation="SUPPORTS" read-only="true" />
        </tx:attributes>
</tx:advice>

4.配置aop:指定哪些哪类要创建代理

<!--配置aop-->
<aop:config>
        <!--配置切入点表达式:指定哪些包中类,要使用事务
            id:切入点表达式的名称,唯一值
            expression:切入点表达式,指定哪些类要使用事务,aspectj会创建代理对象

            com.whh.service
            com.crm.service
        -->
        <aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>

        <!--配置增强器:关联advice和pointcut
           advice-ref:通知,上面tx:advice哪里的配置
           pointcut-ref:切入点表达式的id
        -->
        <aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt" />
</aop:config>

Spring与web

在 Web 项目中使用 Spring 框架,首先要解决在 web 层(这里指 Servlet)中获取到 Spring 容器的问题。只要在 web 层获取到了 Spring 容器,便可从容器中获取到 Service 对象。
web项目中使用容器对象

ApplicationContext ac = new ClassPathXmlApplicationContext(config);

创建Spring容器只需创建一次即可,不该放在Servlet中
解决方法:使用Listener监听器,当全局作用域对象被创建时,创建容器,存入ServletContext
Listener监听器作用:
1.创建容器对象,执行 ApplicationContext ac= new ClassPathXmlApplicationContext(“applicationContext.xml”);
2.把容器对象放入到ServletContext, ServletContext.setAttribute(key,ac)

监听器可以自己创建,也可以使用框架中提供好的ContextLoaderListener

①配置web.xml

<!--配置监听器-->
<!--监听器创建后会读取/WEB-INF/applicationContext.xml,这是默认路径
可以使用context-param修改路径-->
<context-param>
  <!--contextConfigLocation表示配置文件路径-->
  <param-name>contextConfigLocation</param-name>
  <!--自定义配置文件路径--><!--classpath即target/classes/-->
  <param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

②修改创建Spring容器部分

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        Integer id = Integer.parseInt(request.getParameter("id"));
        String name = request.getParameter("name");
        String email = request.getParameter("email");
        Integer age = Integer.parseInt(request.getParameter("age"));
        //创建Spring的容器对象
//        String config = "applicationContext.xml";
//        ApplicationContext ac = new ClassPathXmlApplicationContext(config);

        //方式一:获取ServletContext中的容器对象
        WebApplicationContext ac = null;
//        String key = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
//        Object attribute = getServletContext().getAttribute("key");
//        if(attribute != null){
//            ac = (WebApplicationContext)attribute;
//        }

        //方式二:使用框架中的方法,获取容器对象
        ServletContext sc = getServletContext();
        ac = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);

        StudentService service = (StudentService) ac.getBean("studentServiceImpl");
        Student student = new Student(id,name,email,age);
        service.addStudent(student);
        request.getRequestDispatcher("/result.jsp").forward(request,response);
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值