Spring
-
IOC容器(IOC inversion of control 控制反转)
-
什么是IOC
- 控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理
- 使用IOC的目的:降低耦合度
-
IOC底层实现原理:
- XML解析、工厂模式、反射
-
IOC接口(BeanFactory)
- IOC思想基于IOC容器实现,IOC容器底层就是对象工厂
- Spring提供IOC容器实现两方式(两个接口):
- BeanFactory:IOC容器基本实现,是Spring内部的使用接口,不提供开发人员使用,加载配置文件时,不会创建对象,在获取对象时才会取创建对象
- ApplicationContext:BeanFactory接口的子接口,功能比父接口更加强大,一般由开发人员使用。加载配置 文件时就会把配置文件对象进行创建
- ApplicationContext的两个主要实现类:
- FileSystemXmlApplicationContext需要绝对路径
- ClassPathXmlApplicationContext需要相对路径
-
IOC操作Bean管理(基于xml):
-
<bean id="book" class="com.atguigu.spring5.Book"> <property name="bname"value="val1"></property> <property name="cname"value="val2"></property> // 字面值设置空值 <property name="dname"> <null/> </property> //属性值包含特殊字符(可以转义><或者CDATA) <property name="ename"> <value><![CDATA[内容]]></value> </property> </bean>
-
数组类型属性的注入(list map set等类似)
-
<bean id="book" class="com.atguigu.spring5.Book"> <property name="array"> <array> <value>book1</value> <value>book2</value> </array> </property> </bean>
-
-
spring全家桶:spring springmvc springboot springcloud
-
IOC (Inversion of control) : 控制反转,是一个理论、概念、思想
-
描述:把对象的创建,赋值,管理工作都交给代码之外的容器实现,也就是对象的创建是由其他外部资源完成
-
控制:创建对象,对象的属性赋值,对象之间的关系管理
-
反转:把原来的开发人员管理,创建对象的权限交给代码之外的容器实现,由容器代替开发人员管理对象,创建对象,给对象赋值。
-
正转:由开发人员在代码中,使用new构造方法创建对象,开发人员主动管理对象
-
容器:是一个服务器软件,一个框架(spring)
-
为什么要使用ioc?
- 目的就是减少代码的改动,也能实现不同的共能,实现解耦合
-
java中创建对象的方式有?
- 构造方法
- 反射
- 序列化
- 克隆
- ioc:容器创建对象
- 动态代理
-
什么样的对象放入到容器中?
- dao类、 service类、controller类、工具类
- 使用xml配置文件,使用
- 注解
- spring中的对象都是单例的,在容器中叫这个名称的对象只有一个
- dao类、 service类、controller类、工具类
-
不放到spring容器中的对象
- 实体类对象,实体类对象来自数据库
- servlet、listener、fliter等
-
IOC的技术实现:
- DI(Dependency Injection):依赖注入,只需要在程序中提供要使用的对象名称就行,至于对象如何在容器中创建,赋值,查找都交给容器实现
- spring使用di实现了ioc的功能,spring底层创建对象,使用的是反射机制。
-
spring的依赖:在maven的pom.xml中加入对应的依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.5.RELEASE</version> </dependency>
-
在resources目录下的bean.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="hello" class="com.javase.pojo.impl.HelloImpl"></bean> </beans> <!-- 告诉spring创建对象 声明bean,就是告诉spring要创建某个对象 id:对象的自定义名称,唯一值,spring要通过这个名称找到要创建的对象 class:类的全限定名称(不能是接口,因为spring是反射机制创建对象,必须使用类) -->
-
用spring创建java对象
public void test01() { String config = "beans.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(config); Hello hello = (Hello) applicationContext.getBean("hello"); hello.dosome(); }
-
spring默认创建对象的时间:在创建spring容器的时候,会创建配置文件中所有的对象
public void test02() { String config = "beans.xml"; ApplicationContext ac = new ClassPathXmlApplicationContext(config); // 获取容器中对象的数量 int count = ac.getBeanDefinitionCount(); System.out.println("spring容器中对象的数量为" + count); // 获取容器中所有对象的名字 String[] names = ac.getBeanDefinitionNames(); System.out.println("spring容器中对象的名字有:"); System.out.println(Arrays.toString(names)); }
-
在spring中规定java的基本数据类型和String都是简单类型
-
bean对象分为普通bean和工厂bean(可以改变xml中name定义的类型,需要对应的类实现Factory接口,更改对应的getbean方法)
-
在spring中默认时单实例对象,配置文件中配置的bean,一个id对用一个实例,多次创建获得的对象地址一样
- 如何设置单实例和多实例
- 在spring配置文件bean标签里面有属性scope用于设置单实例还是多实例
- 第一个值:singleton,单实例(默认)
- 第二个值:prototype,多实例
- 注意:设置scope值是singleton时候,加载sprng配置文件时就会创建单实例对象;设置scope值是prototype时,不是在加载配置文件时创建对象,而是在调用getBean方法的时候创建多实例对象
- 如何设置单实例和多实例
-
bean的生命周期
- 概念:从对象创建到销毁的过程
- bean的生命周期:(若加上后置处理器,有7步)
- 通过构造器创建bean实例(无参数构造)
- 为bean的属性设置值和对其他bean引用(调用set方法)
- 调用bean的初始化的方法(需要进行配置初始化的方法)xml文件bean的一个属性init-method中配置
- bean可以使用了(对象获取到了)
- 当容器关闭的时候,调用bean的销毁方法(需要进行配置销毁的方法)xml文件bean的一个属性destroy-method中配置,需要调用context.close()方法销毁实例
-
如何给对象赋值:
-
di:依赖注入,表示创建对象,给对象赋值
-
di的实现方法:
- 在spring的配置文件中,使用标签和属性完成,叫做基于XML的di实现
- 使用spring中的注解,完成属性赋值,叫做基于注解的di实现
-
di的语法分类:
- set注入:spring调用类的set方法,在set方法可以实现属性的赋值
- 构造注入:spring调用类的有参构造方法,创建对象,在构造方法中完成赋值
-
简单类型的set注入:
<bean id="student" class="com.javase.pojo.impl.Student"> <property name="age" value="18"></property> <property name="name" value="张三"></property> </bean>
-
引用类型的set注入:
<bean id="student" class="com.javase.pojo.impl.Student" > <property name="age" value="18"></property> <property name="name" value="张三"></property> <property name="home" ref="home"></property> </bean> <bean id="home" class="com.javase.pojo.impl.Home"> <property name="address" value="河南省登封市颖阳镇"></property> </bean>
-
数组、List、Map的注入
<bean id="student" class="com.javase.demo.Student"> <property name="courses"> <array> <value>""</value> </array> </property> <property name="list"> <ref bean=""></ref> </property> <property name="map"> <map> <entry key="" value=""></entry> </map> </property> </bean>
-
构造注入
<!--使用name属性进行赋值--> <bean id="student" class="com.javase.pojo.impl.Student"> <constructor-arg name="age" value="18"></constructor-arg> <constructor-arg name="name" value="李四"></constructor-arg> <constructor-arg name="home" ref="home"></constructor-arg> </bean> <bean id="home" class="com.javase.pojo.impl.Home"> <property name="address" value="河南省登封市颖阳镇"></property> </bean>
<!--使用index属性赋值--> <bean id="student" class="com.javase.pojo.impl.Student"> <constructor-arg index="0" value="18"></constructor-arg> <constructor-arg index="1" value="李四"></constructor-arg> <constructor-arg index="2" ref="home"></constructor-arg> </bean> <bean id="home" class="com.javase.pojo.impl.Home"> <property name="address" value="河南省登封市颖阳镇"></property> </bean>
<!--如果index和构造方法顺序一致,可以省略index--> <bean id="student" class="com.javase.pojo.impl.Student"> <constructor-arg value="18"></constructor-arg> <constructor-arg value="李四"></constructor-arg> <constructor-arg ref="home"></constructor-arg> </bean> <bean id="home" class="com.javase.pojo.impl.Home"> <property name="address" value="河南省登封市颖阳镇"></property> </bean>
-
引用类型的自动注入:spring框架根据某些规则可以给引用类型赋值,不用开发人员手动给引用类型赋值
- byName(按名称注入):java类中引用类型的属性名和spring容器中(配置文件)的id名称一样,且数据类型是一致的,这样容器中的bean,spring能赋值给引用类型
<!--byName--> <bean id="student" class="com.javase.pojo.impl.Student" autowire="byName"> <property name="age" value="18"></property> <property name="name" value="张三"></property> <!--<constructor-arg name="home" ref="home"></constructor-arg>--> </bean> <!--使用index属性赋值--> <bean id="home" class="com.javase.pojo.impl.Home"> <property name="address" value="河南省登封市颖阳镇"></property> </bean>
-
byType(按类型注入):java类中引用类型的数据类型和spring容器中(配置文件)的class属性是同源关系,这样的bean能够赋值给引用类型
- 同源:
- java类中引用类型的数据类型和bean的class的值是一样的
- java类中引用类型的数据类型和bean的class的是父子类关系
- java类中引用类型的数据类型和bean的class的值是接口和实现类关系
<bean id="student" class="com.javase.pojo.impl.Student" autowire="byType"> <property name="age" value="18"></property> <property name="name" value="张三"></property> </bean> <!--使用index属性赋值--> <bean id="myHome" class="com.javase.pojo.impl.Home"> <property name="address" value="河南省登封市颖阳镇"></property> </bean>
注意:在byType中,在xml配置文件中声明bean只能有一个符合条件的,多于一个是错误的
- 同源:
-
-
当项目的规模过于庞大时,建议使用多个配置文件来注入对象。
-
可以使用一个主配置文件并引入其他配置文件,来管理配置文件
-
在包含关系的配置文件中也可以使用通配符*(表示任意字符)
-
<import resource="profile.xml"></import> <import resource="classpath:ba06/spring-*"></import>
-
-
基于注解的DI
-
使用注解的步骤:
- 加入maven的依赖spring-context,在你加入spring-context的同时,间接加入spring-aop的依赖,使用注解必须使用spring-aop
- 在类中加入spring的注解(多个不同功能的注解)
- 在spring的配置文件中,加入一个组件扫描器的标签,说明注解在你的项目中的位置
-
主要学习的7个注解:
- @Component
- @Respotory
- @Service
- @Controller
- @Value
- @Autowired
- @Resource
-
<!-- 声明组件扫描器(component-scan),组件就是java对象 base-package:指定注解在你的项目中的包名 component-scan工作方式:spring会扫描遍历base-package指定的包,把包和子包中的所有类,找到类的注解,按照注解的功能创建对象,或给属性赋值 --> <context:component-scan base-package="com.javase.pojo"/>
-
@Compent(value = "user") 等同于:<bean id="user" class="com.javese.pojo.User"/> 也可以省略value @Compent("user")
-
spring中和@Component功能一致,创建对象的注解还有:
- @Repository(用在持久层类的上面):放在dao的实现类上面表示创建dao对象,dao对象是能访问数据库的。
- @Service(用在业务层上面):放在service的实现类上面,创建service对象,service对象是做业务处理的,可以有事务等功能
- @Controller(用在控制器的上面):放在控制器(处理器)类的上面,创建控制器对象的,控制器对象,能够接收用户提交的参数,显示请求处理的结果。
-
以上三个注解的使用语法和@Component一样,都能创建对象,但是这三个注解还有额外的功能,@Repository,@Service, @Controller都是给对象分层的
-
什么时候使用@Component呢?
- 当你无法区分这个类是属于业务层,持久层,控制层的时候使用
-
指定多个包的三种方式:
- 在组件扫描器的属性中可以配置use-default-filters来指定扫描的注解类型比如只扫秒@Controller
<!--第一种方式:使用多次组件扫描器,指定不同的包--> <context:component-scan base-package="com.javase.pojo.impl"/> <context:component-scan base-package="com.javase.dao"/> <!--第二种方式:使用分隔符;或者,分隔多个包--> <context:component-scan base-package="com.javase.pojo.impl;com.javase.dao"/> <!--第三种方式:指定父包--> <context:component-scan base-package="com.javase"/>
-
@Value 简单类型的属性赋值:
属性:value 是String类型的,表示简单类型的属性值 位置:1.在属性定义的上面,无需set方法,推荐使用 2.在set方法的上面
-
引用类型:
@Autowired:spring框架提供的注解,实现引用类型的赋值 spring中通过给引用类型赋值,使用的是自动注入原理,支持byName,byType @Autowired:默认使用的是byType 位置:1.在属性定义的上面,无需set方法,推荐使用 2.在set方法上使用 属性:required,是一个boolean类型的,默认是true required=true:表示引用类型赋值失败,程序报错,并终止执行 required=false:引用类型如果赋值失败,程序正常执行,引用类型是null 推荐设为true ---------------------------------------------------- 如果使用byName的方式; 1.在属性上面加入@Autowired 2.在属性上面加入@Qualilfier(value=“bean的id”):表示使用指定名称的bean完成赋值 3.两个注解没有先后顺序 ----------------------------------------------------- @Resource标签:(jdk中的标签) 1.先使用byName自动注入,如果byName赋值失败,再使用byType 2.如果只使用byName,则再标签的name属性指定对应类的name值
-
加载属性配置文件:
<context:property-placeholder location="classpath:test.properties"/> 加载配置文件后可以在类中使用${name}来获取对应的value值,除了类中配置文件中也可以
-
ioc实现解耦合:ioc能够实现业务对象之间的解耦合,例如service和dao对象之间的解耦合
-
-
-
动态代理:可以在程序执行过程中,创建代理对象
- 通过代理对象执行方法,给目标类的方法增加额外功能
- jdk动态代理实现步骤:
- 创建目标类,SomeServiceImpl目标类,给它的dosome方法,doother方法增加输出时间,事务
- 创建InvocationHandler接口的实现类,在这个类实现给目标方法增加功能
- 使用jdk类Proxy,创建代理对象,实现创建对象的能力
-
AOP(Aspect Orient Programming)面向切面编程
-
动态代理:
- 实现方式:jdk动态代理,使用jdk中的Proxy,Method, InvocationHandler创建代理对象, jdk动态代理要求目标类必须实现接口
- cglib动态代理:第三方的工具库,创建代理对象,原理是继承,通过继承目标类,创建子类。子类就是代理对象,要求目标类不能被final修饰,方法也不能被final修饰
-
动态代理的作用:
- 在目标类源代码不改变的情况下,增加功能
- 减少代码的重复
- 专注业务代码
- 解耦合,让业务功能和日志、事务等非业务功能分离
-
Aop:面向切面编程,基于动态代理的,可以使用jdk,cglib两种代理方式。Aop就是动态代理的规范化,把动态代理的实现步骤,方式都定义好了,让开发人员用一种统一的方式,使用动态代理。
-
切面
- 理解:给你的目标类增加的功能就是切面,想上面的日志、事务都是切面。
- 特点:一般都是非业务方法,独立使用的
-
怎么理解切面编程?
- 需要在分析项目功能时,找出切面
- 合理的安排切面的执行时间(在目标方法之前还是目标方法之后)
- 合理的安排切面执行的位置,在哪个类,哪个方法增加功能
-
术语:
- Aspect:切面,表示增强的功能,就是一堆代码,完成某一个功能,非业务功能,常见的切面功能有日志、事务、统计信息、参数检查、权限验证
- 是一个动作
- 把通知应用到切入点的过程
- JoinPoint:连接点,连接业务方法和切面的位置,就是某个业务方法(类里面哪些方法可以被增强,这些方法称为连接点)
- Pointcut:切入点,指多个连接点的方法的集合,多个方法(实际被真正增强的方法,称为切入点)
- 目标对象:给哪个类的方法增加功能,这个类就是目标对象
- Advice:通知,通知表示切面功能执行的时间
- 实际增强的逻辑部分称为通知(增强)
- 通知的类型:
- 前置通知
- 后置通知
- 环绕通知
- 异常通知
- 最终通知
- Aspect:切面,表示增强的功能,就是一堆代码,完成某一个功能,非业务功能,常见的切面功能有日志、事务、统计信息、参数检查、权限验证
-
切面的三个关键要素:
- 切面的功能代码,切面干什么
- 切面的执行位置,使用poingcut表示切面执行的位置
- 切面的执行时间,使用Advice表示时间,在目标方法之前,还是目标方法之后
-
Aop的实现
-
aop是一个规范,是动态代理的规范化,是一个标准
-
aop的技术实现框架:
- spring:spring在内部实现了aop规范,能做aop的工作,spring主要在事务处理的时候用aop。我们在项目开发中很少使用spring的aop实现,因为它比较笨重
- aspectJ:一个开源的专门做aop的框架,spring框架集成了aspectJ框架,通过spring就能使用aspectJ的功能
-
切入点表达式
- 切入点表达式作用:知道对哪个类的哪个方法进行增强
- 语法结构:execution (【权限修饰符】【返回值类型】【类全路径】【方法名称】【参数列表】)
- 返回值类型可以忽略
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-elWZLBPi-1683813062609)(C:\Users\12086\AppData\Roaming\Typora\typora-user-images\image-20221024082419847.png)]
-
aspectJ框架实现aop有两种方式:
- 使用xml的配置文件:配置全局事务
- 使用注解,我们在项目中要做aop的功能,一般都使用注解,aspectJ有5个注解
-
aspectJ框架的使用:
-
切面的执行时间(Advice 通知,增强)
- 在aspectJ框架中使用注解表示,也可以使用xml配置文件中的标签
- 常用的注解:
- @Before
- @AfterReturning
- @Around
- @AfterThrowing
- @After
-
使用aspectJ实现aop的基本步骤:
-
新建maven项目
-
加入依赖: spring 依赖 aspect依赖 junit单元测试依赖
-
创建目标类:接口和他的实现类
要做的是给类中的方法增减功能
-
创建切面类:普通类
- 在类的上面加入@Aspect
- 在类中定义方法,方法就是切面要执行的功能代码,在方法上面加入aspectj中的注解,例如@Before,还需要指定切入表达式execution()
-
创建spring的配置文件:声明对象,把对象交给容器统一管理,声明对象可以使用注解或者xml配置文件
-
声明目标对象(创建业务对象)
-
声明切面类对象(创建切面对象)
-
声明aspectJ框架中的自动代理生成器标签。
自动代理生成器:用来完成代理对象的自动创建功能的(绑定)
-
-
创建测试类,从spring容器中获取目标对象(实际上就是代理对象),通过代理执行方法,实现aop的功能增强
-
-
aspectJ的依赖:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.0.5.RELEASE</version> </dependency>
-
相关代码实现:
//连接点 @Component(value = "someService") public class SomeServiceImpl implements SomeService { @Override public void doSome() { System.out.println("-------------doSome方法执行了!----------------"); } } ------------------------------------------------------------------------- // 定义切面类 @Component(value = "myAspect") @Aspect public class MyAspect { @Before(value = "execution(public void com.javase.service.SomeServiceImpl.doSome())") public void aspect01() { System.out.println("切面方法执行了,现在时间是" + new Date()); } } -------------------------------------------------------------------------- // 配置文件 <context:component-scan base-package="com.javase.service"/> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> ------------------------------------------------------------------------- //测试类 @Test public void test05() { String config = "beans.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(config); SomeService proxy = (SomeService)applicationContext.getBean("someService"); proxy.doSome(); }
-
有多个增强类对同一个方法进行增强,设置增强优先级
- 在增强类上面添加注解@Order(数字类型值),数字类型越小优先级越高
-
什么时候考虑使用aop技术?
- 当你要给一个系统中存在的类修改功能,但是原有类的功能完善,但是你没有源代码,使用aop就能增加功能
- 你要给项目中的多个类,添加同一个功能,使用aop
- 给业务方法添加事务、日志输出等
-
通知指定方法的参数:JoinPoint
-
JoinPoint:业务方法,要加入切面方法的业务方法
-
作用:可以在通知方法中获取方法的执行信息,例如方法的名称、方法的实参
-
如果你的切面功能中需要用到方法的信息,就加入JoinPoint
-
这个JoinPoint参数的值是由框架赋予,必须是第一个位置的参数
@Test public void test05() { String config = "beans.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(config); SomeService proxy = (SomeService)applicationContext.getBean("someService"); proxy.doSome("jack", 18); }
-
-
@AfterReturning
-
后置通知
-
属性:
- value 切入表达式
- returning 自定义变量,表示目标方法的返回值的
-
位置:在方法定义的上面
-
特点:
- 在目标方法之后执行的
- 能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能
- 可以修改这个返回值(Object = doOther(); aspect02(res) ;)
-
代码:
@AfterReturning(returning = "res", value = "execution(public * com.javase.service.SomeServiceImpl.doOther(..))") public void aspect02(Object res) { User user = (User) res; user.setName("胡一帆"); user.setId(8); user.setWork("湖北省咸宁市"); }
-
-
@Around
-
环绕通知:
- public
- 必须有一个返回值,推荐使用Object
- 方法名称自定义
- 方法有参数,固定的参数ProceedingJoinPoint
-
特点:
- 它是功能最强的通知
- 在目标方式的前和后都能增强功能
- 控制目标方法是否被调用执行
- 修改原来的目标方法的执行结果,影响最后的调用结果
-
环绕通知等同于jdk动态代理的InvocationHandler接口
-
参数:ProceedingJoinPoint等同于Method(继承了JoinPoint)
作用:执行目标方法
-
返回值:就是目标方法的执行结果,可以被修改
-
环绕通知经常做事务:在目标方法之前开启事务,执行目标方法,在目标方法之后提交事务
-
代码:
@Override public User doOther(String name, int id, String work) { System.out.println("----------------doOther方法执行了!------------------"); return new User(name, id, work); } ---------------------------------------------------------------------------- @Around(value = "myCut()") public void aspect03(ProceedingJoinPoint pjp) throws Throwable { System.out.println("around在目标方法调用之前执行!"); Object[] args = pjp.getArgs(); String name = (String) args[0]; if (name.equals("黄豪杰")) { User user = (User) pjp.proceed(); user.setName("黄豪杰真帅!"); System.out.println(user); } System.out.println("around在目标方法之后执行!"); } ---------------------------------------------------------------------------- @Test public void test06() { String config = "beans.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(config); SomeService proxy = (SomeService) applicationContext.getBean("someService"); proxy.doOther("黄豪杰", 7, "四川内江"); System.out.println("test方法结束!"); }
-
@Pointcut:
-
定义和管理切入点,如果你的项目中有多个切入点表达式是重复的,可以复用的,可以使用@Pointcut
-
属性:value 切入点表达式
-
位置:在自定义的方法上
-
特点:
- 当使用@Pointcut定义在一个方法的上面,此时这个方法的名称就是切入点表达式的别名。其他的通知中,value属性就可以使用这个方法名称,代替切入点表达式了
-
代码:
@Pointcut(value = "execution(public * com.javase.service.SomeServiceImpl.do*(..))") public void myCut() { // 方法内不需要任何代码 } @Around(value = "myCut()") public void aspect03(ProceedingJoinPoint pjp) throws Throwable { // 相关代码 }
-
-
cglib动态代理:目标类没有接口(没有上面代码上的SomeServiceImpl),使用cglilb动态规划,spring框架会自动应用cglib动态代理
-
在已经有接口的情况下使用cglib动态代理:在applicationContext.xml文件配置
<aop:aspectj-autoproxy proxy-target-class="true"/>
-
-
-
-
-
Spring和MyBatis集成使用
-
使用的技术:ioc
-
为什么使用ioc:能把mybatis和spring集成在一起,像一个框架,是因为ioc能创建对象。可以把mybatis框架中的对象交给spring统一创建,开发人员从spring中获取对象,开发人员就不用同时面对两个或多个框架了,就面对一个spring
-
mybatis的使用步骤:
-
定义dao接口,StudentDao
-
在接口同一个目录下创建Dao对应的mapper文件
-
定义mybatis的主配置文件,mybatis.xml
-
创建dao的代理对象,StudentDao dao = SqlSession.getMapper(StudentDao.class);
List students = dao.selectStudents();
-
-
使用getMapper方法需要的条件:
- 获取SqlSession对象,需要使用SqlSessionFactory的openSession()方法
- 通过读取mybatis.xml 主配置文件,创建SqlSessionFactory对象
-
关于连接池:我们会使用独立的连接池类替换mybatis默认自己带的,把连接池类也交给spring创建
-
spring需要创建的对象:
- 独立的连接池对象,使用阿里的druid连接池
- SqlSessionFactory对象
- 创建对应的dao对象
-
-
步骤:
- 新建maven项目
- 加入maven依赖
- spring依赖
- mybatis依赖
- mysql驱动
- spring的事务依赖
- mybatis和spring集成的依赖:mybatis官方提供的,用来在spring项目中创建mybatis的sqlsessionfactory,dao对象
- 创建实体类
- 创建dao接口和mapper文件
- 创建mybatis主配置文件
- 创建service接口和实现类,属性是dao
- 创建spring的配置文件:声明mybatis的对象交给spring创建
- 数据源(数据库连接池)
- SqlSessionFactory
- dao对象
- 声明自定义的service
- 创建测试类:获取service对象,通过service调用dao完成数据的访问
-
需要加入的依赖:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.javase</groupId> <artifactId>spring01</artifactId> <version>1.0</version> <name>spring01</name> <!-- FIXME change it to the project's website --> <url>http://www.example.com</url> <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> <!--spring核心依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.5.RELEASE</version> </dependency> <!--下面两个是spring事务需要的依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.0.5.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.0.5.RELEASE</version> </dependency> <!--mybatis依赖--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.1</version> </dependency> <!--mybatis和spring集成的依赖--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.1</version> </dependency> <!--mysql驱动依赖--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.9</version> </dependency> <!--阿里数据库连接池需要的依赖--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.12</version> </dependency> </dependencies> <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> </build> </project>
-
配置连接池对象:
<bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="url" value="jdbc:mysql://localhost:3306/books"/> <property name="username" value="root"/> <property name="password" value="root"/> <property name="maxActive" value="20"/> </bean>
-
配置selSession对象
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!--把数据库连接池赋给dataSource属性--> <property name="dataSource" ref="myDataSource"/> <!--mybatis主配置文件的位置 configLocation属性时Resource属性,读取配置文件 它的赋值,使用value,指定文件的路径,使用classpath:表示文件的位置--> <property name="configLocation" value="classpath:mybatis.xml"/> </bean>
-
创建dao对象,使用sqlSession的getMapper(UserDao.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的默认名称是dao名字的首字母小写 多个包可以使用逗号分隔 --> <property name="basePackage" value="com.javase.dao"/> </bean>
- spirng和mybatis整合使用的时候,事务是自动提交的
-
将jdbc.properties配置文件导入spring文件中
头部的beans标签内需要加入context相关的三个内容 <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-4.0.xsd"> <context:property-placeholder location="classspath:jdbc.properties"/> 用${}获取对应属性值
-
jdbc.properties(注意要加上jdbc.这个前缀避免与mysql冲突)
jdbc.url=jdbc:mysql://localhost:3306/books jdbc.username=root jdbc.password=root driverClassName=com.mysql.jdbc.Driver initialSize=10 jdbc.maxActive=50 minIdle=5 maxWait=6000
-
-
spring的事务
-
什么是事务?
- 事务指的是一组sql语句的集合,集合中有多条sql语句,可能时insert、update、select、delete等,我们希望这些sql语句同时成功,或者同时失败,这些sql语句的执行是一致的,作为一个整体来执行
-
在java代码中写程序,控制事务,此时事务应该放在哪里?
- service类的业务方法上,因为业务方法会调用多个dao方法,执行多个sql语句
-
通常使用jdbc访问数据库和mybatis访问数据库,是怎么处理事务的?
- jdbc:connection.commit(); connection.rollback();
- mybatis:SqlSession.commit(); SqlSession.rollback();
-
事务的处理方式,有什么不足?
-
不同的数据库访问技术,处理事务的对象,方法不同
需要了解不同数据库访问技术使用事务的原理
-
掌握多种数据库中事务的处理逻辑,什么时候提交事务,什么时候回滚事务
-
处理事务的多种方法
- 总结:就是数据库的访问技术,有不同的事务处理机制,对象和方法,增加了开发难度。
-
-
-
spring是如何处理数据的?
-
spring处理事物的模型,使用的步骤都是固定的,把事务使用的信息提供给spring就可以了
-
spring内部提交、回滚事务,使用的是事务管理对象,代替你完成commit、rollback,事务管理器是一个接口和它的众多实现类。接口:PlatfromTransactionManager,定义了事务的重要方法commit、rollback
实现类:spring把每一种数据库访问技术对应的事务处理类都创建好了。
- mybatis访问数据库—DataSourceTransactionManager
- hibrenate访问数据库—HibrenateTransactionManager
-
如何使用:你需要告诉spring,你使用的是哪一种数据库访问技术,
声明数据库访问技术对应的事务管理器实现类,在spring的配置文件中使用声明就可以了
-
-
你的业务方法需要什么样的事务,说明需要事务的类型
-
说明方法需要的事务:
-
事务的隔离级别:
default:采用db默认的事务隔离级别,mysql的默认为读已提交,Oracle默认为可重复读
- READ_UNCOMMITTED:读未提交,未解决任何并发问题
- READ_COMMITED:读已提交,解决脏读,存在不可重复读和幻读
- REPEATABLE_READ:可重复读,解决脏读、不可重复读,存在幻读
- SERIALIZARBLE:串行化,不存在并发问题
-
事务的超时时间:表示一个方法最长的执行时间,如果方法执行时间超过设定时间,事务就回滚。单位是秒,整数值,默认是-1
-
事务的传播行为:控制业务方法是不是有事务,是什么类型的事务,7个传播行为,表示你的业务方法调用时,事务在方法之间是如何使用的
PROPAGATION_REQUIERED
PROPAGATION_REQUIRES_NEW
PROPAGATION_SUPPORTS
-
-
-
提交事务和回滚事务的时机:
- 当你的业务方法,执行成功,没有异常抛出,当方法执行完毕,spring在方法执行后提交事务,事务管理器commit
- 当你的业务方法抛出运行时异常或者error,spring执行回滚,调用事务管理器的rooback,运行时异常:RuntimeException
- 但你的业务方法抛出非运行时异常时,提交异常
-
总结spring事务:
- 管理事物的是:事务管理器及其实现类
- spring的事务是一个统一模型
- 指定要使用的事务管理器实现类,使用
- 指定哪些类,哪些方法需要加入事物的功能
- 指定方法需要的隔离级别,传播行为,超时
-
-
spring框架中提供的事务处理方案:
-
适合中小项目使用的:注解方案
spring框架自己用aop实现给业务方法增加事务的功能,使用@Transactional注解增加事务。@Transactionnl注解是spring框架自己使用注解,放在public方法的上面,表示当前方法具有事务。可以给注解的属性赋值,表示具体的隔离界别,传播行为,异常信息等等
-
使用@Transactional的步骤:
-
需要声明事务管理器对象
-
开启事务注解驱动,告诉spring框架,我要使用注解的方式管理事务
spring使用aop机制,创建@Transactional所在的类代理对象,给方法加入事物的功能
spring给业务方法加入事务:
-
在你的业务方法执行之前,先开启事务,在业务方法之后提交或回滚事务,使用aop的环绕通知
@Around("你要增加的事务功能的业务方法") Object myAround() { //开启事务,spring帮你开启 try { buy(1002, 10); spring的事务管理.commit(); } catch (Exception e) { spring的事务管理.rollback(); } }
<!--使用spring的事务处理--> <!--1.声明事务管理器--> <bean id="=transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--连接数据库,指定数据源--> <property name="dataSource" ref="myDataSource"/> </bean> <!--2.开启事务注解,告诉spring使用注解管理事务,创建代理对象 transaction-manager:事务管理器对象的id --> <!--注意下面这个dirven要使用tx的--> <tx:annotation-driven transaction-manager="=transactionManager"/>
@Transactional( // 传播行为 propagation = Propagation.REQUIRED, // 隔离级别 isolation = Isolation.DEFAULT, // 设置只读 readOnly = false, // 发生指定异常时回滚 rollbackFor = { NullPointerException.class } ) /** 上面的值都是默认的,可以只使用@Transactional,也可以达到相同的效果,下面的方法必须时 * rollback的处理逻辑: * 1)spring框架会首先检查方法抛出的异常是不是在rollbackFor的属性中,如果异常在 rollbackFor列表中,不管时什么类型的异常,一定回滚 2)如果你抛出的异常不在rollbackFor列表中,spring会判断异常是不是RuntimeException, 如果是,一定回滚。 */ public @Override public void buy(Integer id, Integer account) { }
-
-
适合大型项目,有很多的类,需要大量的配置事务,使用aspectj框架功能,在spring配置文件中声明类,方法需要的事务。这种方式业务方法和事务配置完全分离。
-
实现步骤:
- 要使用aspectj框架,需要加入依赖(spring-aspects)
- 声明事务管理器对象
- 声明方法需要的事务类型(配置方法的事务属性【隔离级别、传播行为、超时】)
- 配置aop:指定哪些类的方法要创建代理
-
声明式的事务
-
<bean id="=transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--连接数据库,指定数据源--> <property name="dataSource" ref="myDataSource"/> </bean> <!--声明事务方法和事务属性(隔离级别,传播途径,超时时间) 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标签 --> <tx:method name="buy" isolation="DEFAULT" propagation="REQUIRED" read-only="false" rollback-for="java.lang.NullPointerException"/> </tx:attributes> </tx:advice> <aop:config> <!-- 配置切入点表达式:指定哪些包中的类,要使用事务 id:切入点表达式的名称,唯一值 expression:切入点表达式,指定那些类要使用事务,aspectj会创建对象 --> <aop:point id="servicePt" expression="execution(* *..service..*.*(..))"/> <!-- 配置增强器:关联advice和pointcut advice-ref:通知,上面tx:advice那里的配置 pointcut-ref:切入点表达式的id --> <aop:adviser advice-ref:"myAdvice" pointcut-ref="servicePt"/> </aop:config>
-
-
-
-
事务传播特性的介绍
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mZvO7GMQ-1683813062609)(C:\Users\12086\AppData\Roaming\Typora\typora-user-images\image-20221024121517859.png)]
-
web项目中怎么使用容器对象(在spirng-mybatis项目的依赖基础上加入jsp和servlet的依赖,还要解决jsp版本较老的问题)
-
做javase项目由main方法,执行代码是执行main方法的,在main方法中创建容器对象
-
web项目是在tomcat服务器上运行的,tomcat一启动,项目一直运行
-
需求:web项目中容器对象只需要创建一次,把容器对象放入到全局作用域servletContext中
-
怎么实现?
-
监听器的作用:
- 创建容器对象 ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
- 把容器对象放到servletContext中,ServletContext.setAttribute(key, ctx);
-
监听器可以自己创建,也可以使用框架中提供好的ContextLoaderLilstener
-
如何使用spring中提供的监听器?
-
maven的pom文件中加入依赖
<denpendency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.2.5.REALEASE</version> </denpendency>
<!--在web.xml文件中配置ContextLoaderListener 监听器创建对象后,会读取/WEB-INF/applicationContext.xml 为什么要读取文件:因为监听器中要创建ApplicationContext对象,需要加载配置文件 /WEB-INF/applicationContext.xml就是监听器默认读取的spring配置文件路径 可以修改默认的文件位置,使用context-param重新指定文件的位置 --> <context-param> <!--contextConfigLocation:表示配置文件的路径--> <param-name>contextConfigLocation</param-name> <!--为了避免冲突,将原来的applicationContext.xml改名为spring.xml--> <!--自定义的文件路径--> <param-value>classpath:spring.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
在servlet中使用框架的方法获取容器对象
WebApplicationContext ctx = null; ServletContext sc = getServletContext(); ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(sc); // 这样就获取了容器对象ApplicationContext的子类WebApplicationContext
-
-
-
-
@scope
- singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
- prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收
动态代理与静态代理
-
动态代理的角色和静态代理的一样 .
-
动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的
为什么要读取文件:因为监听器中要创建ApplicationContext对象,需要加载配置文件
/WEB-INF/applicationContext.xml就是监听器默认读取的spring配置文件路径
可以修改默认的文件位置,使用context-param重新指定文件的位置
–>
contextConfigLocation
classpath:spring.xml
org.springframework.web.context.ContextLoaderListener
```在servlet中使用框架的方法获取容器对象 ```java WebApplicationContext ctx = null; ServletContext sc = getServletContext(); ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(sc); // 这样就获取了容器对象ApplicationContext的子类WebApplicationContext ```
@scope
- singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
- prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收
动态代理与静态代理
- 动态代理的角色和静态代理的一样 .
- 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的