Spring
spring的第一个核心功能 ioc
IoC (Inversion of Control) : 控制反转, 是一个理论,概念,思想。
描述的:把对象的创建,赋值,管理工作都交给代码之外的容器实现, 也就是对象的创建是有其它外部资源完成。
控制: 创建对象,对象的属性赋值,对象之间的关系管理。
反转: 把原来的开发人员管理,创建对象的权限转移给代码之外的容器实现。 由容器代替开 发人员管理对象。创建对象, 给属性赋值。
正转:由开发人员在代码中,使用new 构造方法创建对象, 开发人员主动管理对象。
public static void main(String args[]){
Student student = new Student(); // 在代码中, 创建对象。--正转。
}
容器:是一个服务器软件, 一个框架(spring)
为什么要使用 ioc : 目的就是减少对代码的改动, 也能实现不同的功能。 实现解耦合。
java中创建对象有哪些方式:
- 构造方法 , new Student()
- 反射
- 序列化
- 克隆
- ioc :容器创建对象
- 动态代理
ioc的体现:
servlet 1: 创建类继承HttpServelt
2: 在web.xml 注册servlet , 使用<servlet-name> myservlet </servlet-name>
<servelt-class>com.bjpwernode.controller.MyServlet1</servlet-class>
3. 没有创建 Servlet对象, 没有 MyServlet myservlet = new MyServlet()
4. Servlet 是Tomcat服务器它能你创建的。 Tomcat也称为容器
Tomcat作为容器:里面存放的有Servlet对象, Listener , Filter对象
IoC的技术实现 ,
DI 是ioc的技术实现,
DI(Dependency Injection) :依赖注入, 只需要在程序中提供要使用的对象名称就可以, 至于对象如何在容器中创建,赋值,查找都由容器内部实现。
spring是使用的di实现了ioc的功能, spring底层创建对象,使用的是反射机制。
spring是一个容器,管理对象,给属性赋值, 底层是反射创建对象。
实现步骤
- 创建maven项目
- 加入maven的依赖 spring的依赖,junit依赖
- 创建类(接口和他的实现类)和没有使用框架一样,就是普通的类。
- 创建spring需要使用的配置文件,声明类的信息,这些类由spring创建和管理
- 测试spring创建的对象。
<properties>
<project.build.sourceEncoding>UTF8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<!--spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.9</version>
</dependency>
基于注解的DI
设值注入
<bean id="mystudent" class="com.ysh.pojo.Student">
<property name="name" value="张曼玉"/>
<property name="age" value="22"/> //执行setAge方法,参数值是22
<property name="school" ref="myschool"/>
</bean>
<bean id="myschool" class="com.ysh.pojo.School">
<property name="name" value="Gongxueyuan"/>
<property name="address" value="神火大道"/>
</bean>
@Test
public void NewStudentTest(){
String config="beans.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
Student mystudent = (Student) ac.getBean("mystudent");
System.out.println(mystudent);
}
构造注入
spring调用类有参构造方法,在创建对象的同时,在构造方法中给属性赋值
构造注入使用< constructor-arg > 标签
name:表示构造方法的形参名
index:表示构造方法的参数的位置,参数从左往右是 0,1,2 的顺序
value:构造方法的形参类型是简单类型,使用value
ref: 构造方法的形参类型是引用类型的,使用ref
<!--使用name属性实现构造注入-->
<bean id="mystudent" class="com.ysh.pojo.Student">
<constructor-arg name="name" value="张三"/> //name值是形参
<constructor-arg name="age" value="20"/>
<constructor-arg name="myschool" ref="myschool"/>//这里的myschool是实例化后的对象 是school类的bean的id值
</bean>
<!--使用index属性-->
<bean id="mystudent2" class="com.ysh.pojo.Student">
<constructor-arg index="0" value="张三"/>
<constructor-arg index="1" value="20"/>
<constructor-arg index="2" ref="myschool"/>
</bean> //可以省略index只写value 默认0,1,.. t
自动注入ByName
byType 方式自动注入
当配置文件中被调用者 bean 的 id 值与代码中调用者 bean 类的属性名相同时,可使用 byName 方式,让容器自动将被调用者 bean 注入给调用者 bean。容器是通过调用者的 bean 类的属性名与配置文件的被调用者 bean 的 id 进行比较而实现自动注入的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CNGyisPB-1632043710637)(https://i.loli.net/2021/09/12/fosFPYS8lDwEe6N.png)]
byType 方式自动注入
使用 byType 方式自动注入,要求:配置文件中被调用者 bean 的 class 属性指定的类, 要与代码中调用者 bean 类的某引用类型属性类型同源。即要么相同,要么有 is-a 关系(子 类,或是实现类)。但这样的同源的被调用 bean 只能有一个。多于一个,容器就不知该匹配 哪一个了。
配置文件包含
- 多个配置优势
1.每个文件的大小比一个文件要小很多。效率高
2.避免多人竞争带来的冲突。
如果你的项目有多个模块(相关的功能在一起) ,一个模块一个配置文件。
学生考勤模块一个配置文件, 张三
学生成绩一个配置文件, 李四
多文件的分配方式:
- 按功能模块,一个模块一个配置文件
- 按类的功能,数据库相关的配置一个文件配置文件, 做事务的功能一个配置文件, 做service功能的一个配置文件等
3. 使用通配符时候主配置文件不能含在统配符的范围内
基于注解的DI
使用注解的步骤:
1.加入maven的依赖 spring-context ,在你加入spring-context的同时, 间接加入spring- aop的依赖。 使用注解必须使用spring-aop依赖
2.在类中加入spring的注解(多个不同功能的注解)
3.在spring的配置文件中,加入一个组件扫描器的标签,说明注解在你的项目中的位置
实现步骤
- 加入依赖
- 创建类,在类中加入注解
- 创建spring的配置文件,声明组件扫描器的标签,指明注解在你的项目中的位置。
- 使用注解创建对象,创建容器ApplicationContext
@Component
@Data
@AllArgsConstructor
@NoArgsConstructor
/*
* @Component:创建对象的,等同于<bean>的功能
* 属性:value 就是对象的名称,也就是bean的id值
* value 的值是唯一的,创建的对象在整个spring容器中就一个
* 位置:在类的上面
* @Component(value = "mystudent")等同于
* <bean id="mystudent" class="com.ysh.Dome1.Student>
* */
//不指定对象名称,由spring提供默认名称:类名的首字母小写
//最常用方法省略value @Component("mystudent")
@Component(value = "mystudent")
public class Student {
private String name;
private Integer age;
}
组件扫描器
<!--声明组件扫描器
bae-package:指定注解在你项目中的包名。
component-scan工作方式:spring会扫描遍历base-package指定的包
把包中和子包中的所有类,找到类中的注解,按照注解的功能创建对象,或给属性赋值
-->
<context:component-scan base-package="com.ysh.Dome1"/>
spring中和@Component功能一致,创建对象的注解还有:
- @Repository (用在持久层类的上面):放在 dao的实现类上面,表示创建的dao对象,dao对象是能访问数据库的。
- @Service (用在业务类的上面):放在service的实现类上面,创建service对象,service对象是做业务处理,可以有事务功能的。
- @Controller(用在控制器的上面):放在控制器(处理器)类的上面,创建控制器对象的,控制器对象能够接受 用户提交的参数,显示请求的处理结果。
以上三个注解的使用语法和@Component一样的。都能创建对象,但是这三个注解还有额外的功能。
@Repository @Service @Controller是给项目对象分层的。
简单类型的属性赋值
@Data
@AllArgsConstructor
@NoArgsConstructor
/*
* @Value:简单类型的属性赋值
* 属性:value 是String类型的,表示简单类型的属性值
* 位置:1.在属性定义的上面,无需set方法,推荐使用。
* 2.在set方法的上面*/
@Component("mystudent")
public class Student {
@Value("张飞")
private String name;
@Value("25")
private Integer age;
}
引用类型赋值
@Autowired
@Data
@AllArgsConstructor
@NoArgsConstructor
/*
* @Value:简单类型的属性赋值
* 属性:value 是String类型的,表示简单类型的属性值
* 位置:1.在属性定义的上面,无需set方法,推荐使用。
* 2.在set方法的上面*/
/*
* 引用类型
* @Autowired:spring框架提供的注解,实现引用类型的赋值
* spring中通过注解给引用类型赋值,使用的是自动注入原理,支持byName,byType
* @Autowired:默认使用的是byType自动注入
* */
@Component("mystudent")
public class Student {
@Value("张飞")
private String name;
@Value("25")
private Integer age;
@Autowired
private School school;
}
/*
* 如果要使用byName方式,需要做的是:
* 1.在属性上面加入@Autowired
* 2.在属性上面加入@Qualifier(value="bean的id)表示指定名称的bean*/
@Component("mystudent")
public class Student {
@Value("张飞")
private String name;
@Value("25")
private Integer age;
@Autowired
@Qualifier("myschool")
private School school;
}
属性:required:是一个boolean类型的,默认true
true表示引用类型赋值失败,程序报错,并终止执行
false:引用类型赋值失败,程序正常运行,引用类型为空
@Autowired(required=false)
@Qualifier("myschool")
private School school;
Aop
1.动态代理
实现方式:jdk动态代理,使用jdk中的Proxy,Method,InvocaitonHanderl创建代理对象。
jdk动态代理要求目标类必须实现接口
cglib动态代理:第三方的工具库,创建代理对象,原理是继承。 通过继承目标类,创建子类。
子类就是代理对象。 要求目标类不能是final的, 方法也不能是final的
2.动态代理的作用:
1)在目标类源代码不改变的情况下,增加功能。
2)减少代码的重复
3)专注业务逻辑代码
4)解耦合,让你的业务功能和日志,事务非业务功能分离。
3.Aop:面向切面编程, 基于动态代理的,可以使用jdk,cglib两种代理方式。
Aop就是动态代理的规范化, 把动态代理的实现步骤,方式都定义好了,
让开发人员用一种统一的方式,使用动态代理。
- AOP(Aspect Orient Programming)面向切面编程
Aspect: 切面,给你的目标类增加的功能,就是切面。 像上面用的日志,事务都是切面。
切面的特点: 一般都是非业务方法,独立使用的。
Orient:面向, 对着。
Programming:编程
oop: 面向对象编程
怎么理解面向切面编程 ?
1)需要在分析项目功能时,找出切面。
2)合理的安排切面的执行时间(在目标方法前, 还是目标方法后)
3)合理的安全切面执行的位置,在哪个类,哪个方法增加增强功能
术语:
1)Aspect:切面,表示增强的功能, 就是一堆代码,完成某个一个功能。非业务功能,
常见的切面功能有日志, 事务, 统计信息, 参数检查, 权限验证。
2)JoinPoint:连接点 ,连接业务方法和切面的位置。 就某类中的业务方法
3)Pointcut : 切入点 ,指多个连接点方法的集合。多个方法
4)目标对象: 给哪个类的方法增加功能, 这个类就是目标对象
5)Advice:通知,通知表示切面功能执行的时间。
5.aop的实现
aop是一个规范,是动态的一个规范化,一个标准
aop的技术实现框架:
1.spring:spring在内部实现了aop规范,能做aop的工作。
spring主要在事务处理时使用aop。
我们项目开发中很少使用spring的aop实现。 因为spring的aop比较笨重。
2.aspectJ: 一个开源的专门做aop的框架。spring框架中集成了aspectj框架,通过spring就 能使用aspectj的功能。
aspectJ框架实现aop有两种方式:
1.使用xml的配置文件 : 配置全局事务
2.使用注解,我们在项目中要做aop功能,一般都使用注解, aspectj有5个注解。
Aop实现步骤
使用aspectj实现aop的基本步骤:
1.新建maven项目
2.加入依赖
1)spring依赖
2)aspectj依赖
3)junit单元测试
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.9</version>
</dependency>
3.创建目标类:接口和他的实现类。
要做的是给类中的方法增加功能
//目标类
public class SomeServiceImpl implements SomeService {
@Override
public void doSome(String name,Integer age) {
//给doSome方法增加一个功能,在doSome()执行之前,输出方法的执行时间
System.out.println("---目标方法doSome---");
}
}
4.创建切面类:普通类
1)在类的上面加入 @Aspect
2)在类中定义方法, 方法就是切面要执行的功能代码
在方法的上面加入aspectj中的通知注解,例如@Before
有需要指定切入点表达式execution()
@Before:前置通知
/**
* @Aspect:是框架中的注解
* 作用:表示当前类是切面类。
* 切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码
* 位置:在类定义的上面
*/
@Aspect
public class MyAspect {
/**
* 定义方法,方法是实现切面功能的。
* 方法的定义要求:
* 1.公共方法 public
* 2.方法没有返回值
* 3.方法名称自定义
* 4,方法可以有参数,也可以没有参数
* 如果有参数,参数不是自定义的,有几个参数可以使用
*/
/**
* @Before:前置通知注解
* 属性:value,是切入点表达式,表示切面的功能执行的位置
* 位置:在方法的上面
* 特点
* 1.在目标方法之前先执行的
* 2.不会改变目标方法的执行结果
* 3.不会影响目标方法的执行
*/
@Before(value = "execution(public void com.ysh.ba01.SomeServiceImpl.doSome(String,Integer))")
public void myBefore(){
//就是你切面要执行的功能代码
System.out.println("切面功能:在目标方法之前输出执行时间"+ new Date());
}
}
5.创建spring的配置文件:声明对象,把对象交给容器统一管理
声明对象你可以使用注解或者xml配置文件< bean>
1)声明目标对象
2)声明切面类对象
3)声明aspectj框架中的自动代理生成器标签。
自动代理生成器:用来完成代理对象的自动创建功能的。
<!--把对象交给spring容器,由spring容器统一创建,管理对象-->
<!--声明目标对象-->
<bean id="someService" class="com.ysh.ba01.SomeServiceImpl" />
<!--声明切面类对象-->
<bean id="myAspect" class="com.ysh.ba01.MyAspect" />
<!--声明自动代理生成器:使用aspectj框架内部的功能,创建目标对象的代理对象。
创建代理对象是在内存中实现的, 修改目标对象的内存中的结构。 创建为代理对象
所以目标对象就是被修改后的代理对象.
aspectj-autoproxy:会把spring容器中的所有的目标对象,一次性都生成代理对象。
-->
<!--<aop:aspectj-autoproxy />-->
<!--
如果你期望目标类有接口,使用cglib代理
proxy-target-class="true":告诉框架,要使用cglib动态代理
-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
6.创建测试类,从spring容器中获取目标对象(实际就是代理对象)。
通过代理执行方法,实现aop的功能增强。
@Test
public void test(){
String config="applicationContext.xml";
ApplicationContext ctx=new ClassPathXmlApplicationContext(config); //切面表达式
//从容器中获取目标对象
SomeService proxy=(SomeService) ctx.getBean("someService");
//通过代理对象执行方法
proxy.doSome("lisi", 23);
}
JoinPoint
/**
* 指定通知方法中的参数 : JoinPoint
* JoinPoint:业务方法,要加入切面功能的业务方法
* 作用是:可以在通知方法中获取方法执行时的信息, 例如方法名称,方法的实参。
* 如果你的切面功能中需要用到方法的信息,就加入JoinPoint.
* 这个JoinPoint参数的值是由框架赋予, 必须是第一个位置的参数
*/
@Before(value = "execution(void *..SomeServiceImpl.doSome(String,Integer))")
public void myBefore(JoinPoint jp){
//获取方法的完整定义
System.out.println("方法的签名(定义)="+jp.getSignature());
System.out.println("方法的名称="+jp.getSignature().getName());
//获取方法的实参
Object args [] = jp.getArgs();
for (Object arg:args){
System.out.println("参数="+arg);
}
//就是你切面要执行的功能代码
System.out.println("2=====前置通知, 切面功能:在目标方法之前输出执行时间:"+ new Date());
}
执行结果
@AfterReturning :后置通知
/**
* 定义方法,方法是实现切面功能的。
* 方法的定义要求:
* 1.公共方法 public
* 2.方法没有返回值
* 3.方法名称自定义
* 4,方法有参数
* 推荐是Object,参数名自定义
*/
/**
* @AfterReturning :后置通知
* 属性:1.value 切入点表达式
* 2,returning 自定义的变量,表示目标方法的返回值。
* 位置:在方法定义的上面
* 特点:
* 1.在目标方法之后执行的。
* 2,能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能
* Object res=doOther()
* 3.可以修改这个返回值
*/
@AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))",returning = "res")
public void myBefore2(Object res){
//res:是目标方法的返回值
System.out.println("=====后置通知, 切面功能:在目标方法之后"+res);
}
}
@Around:环绕通知
/**
* 环绕通知方法的定义格式
* 1.public
* 2.必须要有一个返回值,推荐使用Object
* 3.方法名称自定义
* 4,方法有参数,固定的参数ProceedingJoinPoint
*/
/**
* @Around:环绕通知
* 属性:value 切入点表达式
* 位置:在方法的定义什么
* 特点:
* 1.它是功能最强的通知
* 2.在目标方法的前和后都能增强功能
* 3.控制方法是否被调用执行
* 4,修改原来的目标方法的执行结果。影响最后的调用结果
*
* 环绕通知,等同于jdk动态代理的,InvocationHandler接口
*
* 参数:ProceedingJoinpoint 就等同于 Method
* 作用:执行目标方法的
*
* @param pij
* @return 返回值:就是目标方法的执行结果,可以被修改
*/
@Around(value = "execution(* *..SomeServiceImpl.doFirst(..))")
public Object myAround(ProceedingJoinPoint pji) throws Throwable {
//实现环绕通知
Object result=null;
System.out.println("环绕通知在目标方法之前,输出时间:" +new Date());
//1.目标方法调用
result=pji.proceed();//等同于method.incoke(); Object result=doFirst()
System.out.println("环绕通知在目标方法之后,输出时间:" +new Date());
return result;
}
如果你希望目标类接口,使用cglib代理 在主配置文件加入
<aop:aspectj-autoproxy proxy-target-class="true"/>
Spring 整合MyBatis
把mybatis和spring集成在一起,像一个框架一样使用。用的技术是:ioc
可以把mybatis框架中的对象交给spring统一创建,开发人员从spring中获取对象
通过以上的说明,我们需要让spring创建以下对象
1.独立的连接池类的对象, 使用阿里的druid连接池
2.SqlSessionFactory对象
3.创建出dao对象
步骤:
1.新建maven项目
2.加入maven的依赖
1)spring依赖
2)mybatis依赖
3)mysql驱动
4)spring的事务的依赖
5)mybatis和spring集成的依赖: mybatis官方体用的,用来在spring项目中创建mybatis
的SqlSesissonFactory,dao对象的
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.9</version>
</dependency>
<!--spring做事务的-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.9</version>
</dependency>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!--mybatis和spring集成的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
<!--阿里巴巴数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.6</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory><!--所在的目录-->
<includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
3.创建实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
//属性名和列名一样
private Integer id;
private String name;
private String email;
private Integer age;
}
4.创建dao接口和mapper文件
public interface StudentDao {
int insertStudent(Student student);
List <Student> selectStudent();
}
<insert id="insertStudent">
insert into student values (#{id},#{name},#{email},#{age})
</insert>
<select id="selectStudent" resultType="com.ysn.dao.StudentDao">
select id,name,email,age from student order by id desc
</select>
5.创建mybatis主配置文件
<!--settings:控制日志-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--设置别名-->
<typeAliases>
<!--实体类所在的包名-->
<package name="com.ysh.daomain"/>
</typeAliases>
<!--sql mapper (sql映射文件)的位置-->
<mappers>
<!--
一个mapper标签指定一个文件的位置
从类路径开始的路径信息,target/clasess(类路径)
-->
<!--<mapper resource="com/ysh/dao/StudentDao.xml"/>-->
<!--name:是包名 ,这个包中所有的mapper.xml一次都能加载-->
<package name="com.ysh.dao"/>
</mappers>
6.创建Service接口和实现类,属性是dao。
public interface StudentService {
int addStudent (Student student);
List<Student> queryStudent();
}
//引用类型
private StudentDao studentDao;
//使用set注入,赋值
public void setStudentDao(StudentDao studentDao) {
this.studentDao = studentDao;
}
@Override
public int addStudent(Student student) {
int nums=studentDao.insertStudent(student);
return nums;
}
@Override
public List<Student> queryStudent() {
List <Student> students=studentDao.selectStudent();
return students;
}
7.创建spring的配置文件:声明mybatis的对象交给spring创建
1)数据源DataSource
<!--声明数据源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"/>
<property name="username" value="root"/>
<property name="password" value="ysh"/>
<property name="maxActive" value="20" />
</bean>
2)SqlSessionFactory
<!--声明的是mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory的
SqlSessionFactory sqlSessionFactory = new ..
-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--set注入,把数据库连接池付给了dataSource属性-->
<property name="dataSource" ref="myDataSource" />
<!--mybatis主配置文件的位置
configLocation属性是Resource类型,读取配置文件
它的赋值,使用value,指定文件的路径,使用classpath:表示文件的位置
-->
<property name="configLocation" value="classpath:mybatis.xml" />
</bean>
- Dao对象
<!--创建dao对象,使用SqlSession的getMapper(StudentDao.class)
MapperScannerConfigurer:在内部调用getMapper()生成每个dao接口的代理对象。
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定SqlSessionFactory对象的id-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
<!--指定包名, 包名是dao接口所在的包名。
MapperScannerConfigurer会扫描这个包中的所有接口,把每个接口都执行
一次getMapper()方法,得到每个接口的dao对象。
创建好的dao对象放入到spring的容器中的。 dao对象的默认名称是 接口名首字母小写
-->
<property name="basePackage" value="com.ysh.dao"/>
</bean>
- 4)声明自定义的service
<!--声明service-->
<bean id="studentService" class="com.ysh.service.impl.StudentServiceImpl">
<property name="studentDao" ref="studentDao" />
</bean>
8.创建测试类,获取Service对象,通过service调用dao完成数据库的访问
@Test
public void testServiceInsert(){
String config="applicationContext.xml";
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
//获取spring容器中的dao对象
StudentService service = (StudentService) ctx.getBean("studentService");
Student student = new Student();
student.setId(1015);
student.setName("李胜利");
student.setEmail("zhoufeng@qq.com");
student.setAge(26);
int nums = service.addStudent(student);
//spring和mybatis整合在一起使用,事务是自动提交的。 无需执行SqlSession.commit();
System.out.println("nums="+nums);
}
Spring事务的处理
电商购买商品演示事务的处理
1.创建实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Sale { //商品的销售订单
private Integer id;
private Integer gid;
private Integer nums;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Goods { //商品表
private Integer id;
private String name;
private Integer amount;
private Float price;
}
2.创建dao和mybatis映射文件
商品
public interface GoodsDao {
//更新库存
//goods表示本次用户购买的商品信息,id,购买数量
int updateGoods(Goods goods);
//查询商品的信息
Goods selectGoods(Integer id);
}
<select id="selectGoods" resultType="com.ysh.domain.Goods">
select id,name,amount,price from goods where id=#{gid}
</select>
<update id="updateGoods">
update set amount =amount-#{amount} where id={id}
</update>
订单
public interface SaleDao {
//增加销售记录
int insertSale(Sale sale);
}
<insert id="insertSale">
insert into sale(gid,nums) values (#{gid},#{nums})
</insert>
配置文件和上面基本一致
测试方法
@Test
public void test01(){
String config="applicationContext.xml";
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
//从容器获取service
BuyGoodService service = (BuyGoodService) ctx.getBean("buyService");
//调用方法
service.buy(1001,10);
}
spring框架中提供事务处理方案
1.首先我们要在spring配置文件中声明事务的处理
<!--使用spring的事务处理-->
<!--1.声明事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--连接的数据库,指定数据源-->
<property name="dataSource" ref="myDataSource"/>
</bean>
<!--2.开启事务注解驱动,告诉spring使用注解管理事务,创建代理对象transaction-manager:事务管理器对象的id-->
<tx:annotation-driven transaction-manager="transactionManager"/>
执行事务的方法
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
readOnly = false,
rollbackFor = {NullPointerException.class,NotEnoughException.class}
)
@Override
public void buy(Integer goodsId, Integer nums) {
大型项目的事务
适合大型项目,有很多的类,方法,需要大量的配置事务,使用aspectj框架功能,在spring配置文件中
声明类,方法需要的事务。这种方式业务方法和事务配置完全分离。
实现步骤: 都是在xml配置文件中实现。
1)要使用的是aspectj框架,需要加入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
2)声明事务管理器对象
<bean id="xx" class="DataSourceTransactionManager">
- 声明方法需要的事务类型(配置方法的事务属性【隔离级别,传播行为,超时】)
<!--使用spring的事务处理-->
<!--1.声明事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--连接的数据库,指定数据源-->
<property name="dataSource" ref="myDataSource"/>
</bean>
<!--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.ysh.excep.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.bjpowernode.service
com.crm.service
com.service
-->
<aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>
<!--配置增强器:关联adivce和pointcut
advice-ref:通知,上面tx:advice哪里的配置
pointcut-ref:切入点表达式的id
-->
<aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt" />
</aop:config>
Web项目中使用容器对象
和上面的项目基本一样就是利用监听器来创建spring容器
添加依赖
<!--为了使用监听器对象,加入依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.9</version>
<!--注册监听器ContextLoaderListener
监听器被创建对象后,会读取/WEB-INF/spring.xml
为什么要读取文件:因为在监听器中要创建ApplicationContext对象,需要加载配置文件。
/WEB-INF/applicationContext.xml就是监听器默认读取的spring配置文件路径
可以修改默认的文件位置,使用context-param重新指定文件的位置
配置监听器:目的是创建容器对象,创建了容器对象, 就能把spring.xml配置文件中的所有对象都创建好。
用户发起请求就可以直接使用对象了。
-->
<context-param>
<!-- contextConfigLocation:表示配置文件的路径 -->
<param-name>contextConfigLocation</param-name>
<!--自定义配置文件的路径-->
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf8");
System.out.println("开始执行");
String strId=request.getParameter("id");
String strName=request.getParameter("name");
String strEmail=request.getParameter("email");
String strAge=request.getParameter("age");
//创建spring容器对象
//String config ="applicationContext.xml";
//ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
WebApplicationContext ctx=null;
ServletContext sc=getServletContext();
ctx=WebApplicationContextUtils.getWebApplicationContext(sc);
//获取service
StudentService studentService = (StudentService) ctx.getBean("studentService");
Student student=new Student(Integer.parseInt(strId),strName,strEmail,Integer.parseInt(strAge));
studentService.addStudent(student);
// 给一个页面
request.getRequestDispatcher("/result.jsp").forward(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf8");
System.out.println("开始执行");
String strId=request.getParameter("id");
String strName=request.getParameter("name");
String strEmail=request.getParameter("email");
String strAge=request.getParameter("age");
//创建spring容器对象
//String config ="applicationContext.xml";
//ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
WebApplicationContext ctx=null;
ServletContext sc=getServletContext();
ctx=WebApplicationContextUtils.getWebApplicationContext(sc);
//获取service
StudentService studentService = (StudentService) ctx.getBean("studentService");
Student student=new Student(Integer.parseInt(strId),strName,strEmail,Integer.parseInt(strAge));
studentService.addStudent(student);
// 给一个页面
request.getRequestDispatcher("/result.jsp").forward(request,response);
}