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&allowPublicKeyRetrieval=true&useSSL=false&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);
}