Spring5
文章目录
一、概述
Spring是一款轻量级、开源的JavaEE框架,解决了企业应用开发的复杂性,两个核心部分:IOC和AOP
特点:
- 方便解耦,简化开发
- 支持AOP编程
- 方便程序的测试,集成Junit
- 方便整合各种其他优秀框架
- 声明式事务的支持
- 降低JavaEE API的使用难度
二、IOC
概述
IOC(Inversion of Control)即控制反转。
控制反转,是面对对象编程的一种设计原则,可以用来减低计算机代码之间的耦合度。最常见的方式叫做依赖注入(Dependency Injection,简称DI)。
目的是把创建对象的过程交给Spring管理。
IOC小案例:
-
创建实体类
public class Hello { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public void show(){ System.out.println("hello"+name); } }
-
编写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.jcy.pojo.Hello"> <property name="name" value="澳狗狗"></property> </bean> </beans>
-
测试
@Test public void HelloTest(){ ApplicationContext context = new ClassPathXmlApplicationContext("hello.xml"); //BeanFactory context1 = new ClassPathXmlApplicationContext("hello.xml"); Hello hello = (Hello) context.getBean("hello"); hello.show(); }
IOC接口:
-
BeanFactory
IOC容器的基本实现,是spring内部的使用接口
特点:加载配置文件时,不会创建对象,获取对象时才会创建
-
ApplicationContext
BeanFactory的子接口,提供了更多更强大的功能
特点:加载配置文件时,就会创建对象
IOC操作Bean管理
Bean管理就是通过Spring创建对象和注入属性
1.基本描述
在Spring配置文件中,使用bean标签,在标签中添加相应属性,就可以完成对象的创建.
创建对象时默认是执行无参构造方法完成对象的创建
2.常用属性及其子元素
属性及其子元素 | 作用 |
---|---|
id | 唯一标识,不能添加特殊符号 |
name | 定义对象的标识,可以加特殊符号 |
class | 类的全路径 |
scope | 用来设定Bean实例的作用域 |
contructor-arg | 的子元素,可以通过此元素传入构造参数进行实例化 |
property | 的子元素,调用Bean实例的setter方法完成属性赋值 |
ref | 的子元素,引用Bean工厂的某个实例 |
value | 的子元素,赋值 |
list | 用于封装List或数组类型的依赖注入 |
set | 用于封装Set的依赖注入 |
map | 用于封装Map的依赖注入 |
entry |
实例:实体类
public class Student {
private String name;
private Address address;
private String[] book;
private List<String> hobbys;
private Set<String> anime;
private Map<String,String> games;
private Properties info;
//这里有get set 方法和toString
}
xml配置
<bean id="student" class="com.jcy.pojo.Student">
<property name="name" value="王狗狗"></property>
<property name="address" ref="address"></property>
<property name="book">
<array>
<value>java从入门到入土</value>
<value>1天让你精通所有holle world</value>
<value>linux删库跑路</value>
</array>
</property>
<property name="anime">
<set>
<value>樱花庄的宠物女孩</value>
<value>从零开始的异世界生活</value>
<value>冰菓</value>
</set>
</property>
<property name="hobbys">
<list>
<value>抽烟</value>
<value>喝酒</value>
<value>烫头</value>
</list>
</property>
<property name="games">
<map>
<entry key="王者荣誉" value="最强青铜"></entry>
<entry key="LOL" value="不屈黑铁"></entry>
<entry key="阴阳师" value="八段萌新"></entry>
</map>
</property>
<property name="info">
<props>
<prop key="学号">12138</prop>
<prop key="姓名">王狗狗</prop>
<prop key="性别">男</prop>
</props>
</property>
</bean>
<bean id="address" class="com.jcy.pojo.Address">
<property name="address" value="天堂"></property>
</bean>
测试类
@Test
public void StudentTest(){
ApplicationContext context = new ClassPathXmlApplicationContext("student.xml");
Student s = (Student) context.getBean("student");
System.out.println(s.toString());
}
默认是无参构造方法创建对象,使用set赋值
contructor-arg是有参构造方法创建(三种方式)
<bean id="hello" class="com.jcy.pojo.Hello">
<constructor-arg index="0" value="澳狗狗"></constructor-arg>//下标
<constructor-arg name="name" value="澳狗狗"></constructor-arg>//名字
<constructor-arg type="java.lang.String" value="澳狗狗"></constructor-arg>//类型
</bean>
Bean的实例化
-
构造器实例化(普通bean)
-
静态工厂实例化(工厂bean)
-
实例工厂实例化(工厂bean)
普通bean:在配置文件中bean类型和返回类型一致
工厂bean:在配置文件中bean类型和返回类型可以不同
静态工厂实例化
实体类
public class Hello {
private String name;
public Hello() {
System.out.println("实例化中");
}
public Hello(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("hello"+name);
}
}
//工厂类:
public class StaticFactory {
public static Hello getHello(){
return new Hello();
}
}
xml
<bean id="staticFactory" class="com.jcy.controller.StaticFactory" factory-method="getHello">//factory-method指定工厂中的方法
<property name="name" value="狗牌的"></property>
</bean>
测试类
@Test
public void StaticTest(){
ApplicationContext context = new ClassPathXmlApplicationContext("hello.xml");
Hello s = (Hello) context.getBean("staticFactory");
s.show();
}
实例工厂实例化
工厂类
public class StaticFactory {
public Hello getHello(){
return new Hello();
}
}
xml
<bean id="hello" class="com.jcy.pojo.Hello" factory-bean="staticFactory" factory-method="getHello">//factory-bean指定工厂factory-method指定工厂的方法
<property name="name" value="ao"></property>
</bean>
<bean id="staticFactory" class="com.jcy.controller.StaticFactory"></bean>
@Test
public void StaticTest(){
ApplicationContext context = new ClassPathXmlApplicationContext("hello.xml");
Hello s = (Hello) context.getBean("hello");
s.show();
}
Bean的作用域(重点)
通过bean标签里的scope属性配置,总共七种,最常用的有singleton和prototype
作用域名称 | 作用描述 |
---|---|
singleton | 单例模式 |
prototype | 多例,每获取一个Bean,就会创建一个Bean实例 |
request | 每一次HTTP请求就会创建一个实例,对不同的HTTP请求则会产生新的Bean,而且该Bean仅在当前HTTP Request中生效 |
session | 每一次Session请求就会创建一个实例,对不同的HTTP请求则会产生新的Bean,而且该Bean仅在当前Session Request中生效 |
globalSession | 在一个全局的HTTP Session中,容器会返回该Bean的同一个实例,仅在使用portlet上下文时有效 |
application | 为每一个ServletContext创建个实例,仅在Web相关的ApplicationContext中生效 |
websocket | 为每一个websocket创建个实例,仅在Web相关的ApplicationContext中生效 |
xml
<bean id="hello" class="com.jcy.pojo.Hello" scope="singleton">
<!--<bean id="hello" class="com.jcy.pojo.Hello" scope="prototype">-->
<property name="name" value="小白"></property>
</bean>
test
@Test
public void HelloTest(){
ApplicationContext context = new ClassPathXmlApplicationContext("hello.xml");
Hello hello = (Hello) context.getBean("hello");
Hello hello1 = (Hello) context.getBean("hello");
System.out.println(hello);
System.out.println(hello1);
}
测试结果
//单例:
com.jcy.pojo.Hello@5f3a4b84
com.jcy.pojo.Hello@5f3a4b84
//多例:
com.jcy.pojo.Hello@7113b13f
com.jcy.pojo.Hello@45820e51
Bean的生命周期(重点)
- 调用构造方法或者工厂方法对Bean实例化;
- 对Bean注入属性;
- 如果bean实现了BeanNameAware接口,Spring调用setBeanName()方法传入bean的名称;
- 如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory实例传进来;
- 如果bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext()方法,传入当前的ApplicationContext到bean中;
- 如果BeanPostProcessor和Bean关联,则Spring 将调用该接口的预初始化方法postProcessBeforeInitialzation()对Bean进行加工处理,Spring Aop就是用它实现的;
- 如果bean实现了InitializingBean接口,则Spring将调用它的afterPropertiesSet()接口方法;
- 如果bean使用了init-method属性指定了初始化方法,则该方法被调用;
- 如果bean实现了BeanPostProcessor接口,则Spring调用该接口的初始化方法postProcessAfterInitialization()此时bean已经可以被应用程序使用了;
- 如果在 中指定了该Bean的作用范围为scope='singleton", 则将该Bean放入Spring loC的缓存池中,将触发Spring对该Bean的生命周期管理;如果在中指定了该Bean的作用范围为scope='prototype",则将该Bean交给调用者,调用者管理该Bean的生命周期,Spring不再管理该Bean(意思就是说不会帮你销毁)。
- 如果Bean实现了DisposableBean接口,则Spring会调用destory()方法将Spring中的Bean销毁;如果在配置文件中通过destory- method属性指定了Bean 的销毁方法,则Spring将调用该方法进行销毁。
装配方式
-
基于xml配置
使用注入:
因为是无参构造,必须有个无参构造方法和对应setter方法
使用注入:
必须有有参构造方法
- 基于注解
- 自动装配
常用注解
@Component:把普通pojo实例化到spring容器中,相当于配置文件中的id默认为类名首字母小写,class就是注解的类
@Repository:用于数据访问的Dao层
@Service:用于业务层
@Controller:用于控制层
@Autowired:按照属性类型装配
@Resource:按照属性名称装配(name修改属性名,type:修改属性类型)
@Qualifier:与@Autowired联合使用用来修改实例名称,不能单独使用
@Scope:设置作用域
使用注解步骤:
pom导入aop的包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.9</version>
</dependency>
首先在spring配置文件中引入context头文件
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"
开启注解支持及扫描包
<context:annotation-config/>
<context:component-scan base-package="com.jcy"/>
实例:
实体类
@Component("user")//相当于xml中的(<bean id="user" class="com.jcy.pojo.User"/>)如果注解内不写默认就是user(首字母小写)
public class User {
@Autowired//装配了dog -->autowire="byName"--> <property name="dog" ref="dog"/>
private Dog dog;
@Autowired
private Cat cat;
@Value("aogougou")//<property name="str" value="aogougou"/>
private String str;
//get set 无参构造
}
-
自动装配
1.组件扫描(component scanning):spring会自动发现应用上下文所创建的bean;
2.自动装配(autowiring):spring自动满足bean之间的依赖,也就是我们常说的IOC/DI
普通:
<bean id="cat" class="com.jcy.pojo.Cat"/> <bean id="dog" class="com.jcy.pojo.Dog"/> <bean id="user" class="com.jcy.pojo.User"> <property name="str" value="aogou"/> <property name="cat" ref="cat"/> <property name="dog" ref="dog"/> </bean>
自动装配:
<bean id="cat" class="com.jcy.pojo.Cat"/> <bean id="dog" class="com.jcy.pojo.Dog"/> <bean id="user" class="com.jcy.pojo.User" autowire="byName"> <property name="str" value="aogou"/> </bean>
autowire
-
byName:
根据属性名称装配,查询名称一致的Bean(如果属性对应的Bean名字不为首字母小写报错)
-
byType:
根据属性的数据类型装配
完全注解开发
@Configurable//代表这是一个配置类
public class MyConfig {
@Bean//通过方法注册一个bean,这里的返回值就是Bean的类型,方法名就是bean的id
public User user(){
return new User();
}
@Bean
public Dog dog(){
return new Dog();
}
@Bean
public Cat cat(){
return new Cat();
}
}
测试类:
@Test
public void MyConfigTest(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
Dog dog = (Dog) context.getBean("dog");
dog.shout();
}
三、AOP
1.概述,什么是AOP
AOP全称是Aspect-Oriented Programming,即面向切面编程
通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型.
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度减低,提高程序的可重用性,同时提高了开发效率.
主要意图:
将日志记录,性能统一,安全控制,事务处理,异常代理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码.
思想:不通过修改源代码增加新功能
2.AOP术语
-
横切关注点:跨越应用程序多个模块的方法和功能.即是,与我们业务逻辑无关的,但是需要关注的部分,例如日志,安全,缓存,事务
-
切面(Aspect):横切关注点被模块化的特殊对象,即是一个类
-
目标(Target):被通知对象
-
代理(Proxy):向目标对象应用通知之后创建的对象
-
切入点(Pointcut):切面通知 执行的地点
-
连接点(Joinpoint):与切入点匹配的执行点
-
通知(Advice):切面必须要完成的工作,它是类中的方法
通知类型 连接点 实现接口 注解 前置通知 方法前 org.springframework.aop.MethodBeforeAdvice @Before 后置通知 方法后 org.springframework.aop.AfterReturningAdvice @After 引介通知 在目标类添加新的方法和属性 org.springframework.aop.IntroductionInterceptor @DeclareParents 环绕通知 方法前后 org.aopalliance.intercept.MethodInterceptor @Around 异常通知 方法抛出异常 org.springframework.aop.ThrowsAdvice @AfterThrowing
3.AOP底层原理实现
底层原理:动态代理
- 被代理的对象有接口:JDK动态代理
- 被代理的对象没有接口:CGLIB动态代理
- 还有一个是javassist
先说说代理模式:
静态代理
- 抽象对象:一般是接口和抽象类来实现
- 真实对象:被代理的对象
- 代理对象:代理真实对象,并添加一些新功能
- 客户:使用代理对象来进行一些操作
抽象对象:租房
public interface House {
//虚拟对象 出租房子
void Rental();
}
真实对象:房东
public class Landlord implements House{
//真实对象 房东
@Override
public void Rental() {
System.out.println("出租房子");
}
}
代理对象:中介
//代理对象,中介
public class Proxy {
private Landlord landlord;
public Proxy(Landlord landlord) {
this.landlord = landlord;
}
public void Rental() {
seeHouse();
landlord.Rental();
fare();
}
private void seeHouse(){
System.out.println("看房子");
}
private void fare(){
System.out.println("收中介费");
}
}
客户:
//客户
public class Client {
public static void main(String[] args) {
Landlord l = new Landlord();//房东要租房
Proxy p = new Proxy(l);//中介代理
p.Rental();//客户找中介租房
}
}
这是模仿现实租房子的例子。那么在项目中,用户觉得你的代码不行不满足它的需求,那么该怎么办呢?你总不能把用户打一顿(解决不了问题,就解决给你出问题的人),那就可以用代理模式。
抽象对象:
//增删改查等业务
public interface UserService {
void add();
void delete();
void update();
void query();
}
真实对象:
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("add方法");
}
@Override
public void delete() {
System.out.println("delete方法");
}
@Override
public void update() {
System.out.println("update方法");
}
@Override
public void query() {
System.out.println("query方法");
}
}
代理对象
//增加新功能(如日志功能)
public class UserServiceProxy {
private UserServiceImpl userServiceImpl;
public void setUserServiceImpl(UserServiceImpl userServiceImpl) {
this.userServiceImpl = userServiceImpl;
}
public void add() {
log();
userServiceImpl.add();
}
public void delete() {
log();
userServiceImpl.delete();
}
public void update() {
log();
userServiceImpl.update();
}
public void query() {
log();
userServiceImpl.query();
}
private void log(){
System.out.println("日志功能");
}
}
测试访问类:
public class Client {
public static void main(String[] args) {
//真实业务
UserServiceImpl serviceImlp = new UserServiceImpl();
//代理类
UserServiceProxy us = new UserServiceProxy();
us.setUserServiceImpl(serviceImlp);
//使用代理类实现日志功能
us.add();
}
}
核心:在不改变原来的代码上,实现了对原有功能的增强。
静态代理的好处:
- 使我们的真实对象更加纯粹,不用管注公共的业务
- 公共的业务由代理完成
- 公共业务发生扩展时变的更加集中和方便
缺点:
- 类多了,多了个代理类,工作量加大了,开发效率降低
为啥要用代理模式?我直接在真实对象上改代码不行吗?
- 代理模式在一定程度了降低了耦合度
- 保护了真实对象(你直接在原代码上改?好家伙,出错了,你连原来的代码都没了,而且如果客户是个”天使“,他最后觉得你加的这个功能不行还是不要了,代理模式可以方便我们对增加的方法进行管理)。
动态代理
- 静态代理的代理类是我们写好的,而动态代理是动态生成的
抽象对象
public interface House {
//虚拟对象 出租房子
void Rental();
}
真实对象
public class Landlord implements House{
//真实对象 房东
@Override
public void Rental() {
System.out.println("出租房子");
}
}
代理对象
public class ProxyInvocationHandler implements InvocationHandler {
private House house;
public void setHouse(House house) {
this.house = house;
}
//生成代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),house.getClass().getInterfaces(),this);
}
//proxy:代理类method:代理类的调用处理程序的方法对象
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
//利用反射实现
Object result = method.invoke(house, args);
fare();
return result;
}
private void seeHouse(){
System.out.println("看房");
}
private void fare(){
System.out.println("收中介费");
}
}
测试
//客户
public class Client {
public static void main(String[] args) {
Landlord landlord = new Landlord();
ProxyInvocationHandler p = new ProxyInvocationHandler();
p.setHouse(landlord);
House proxy = (House) p.getProxy();
proxy.Rental();
}
}
修改一下,动态代理可以编写一个通用的动态代理实现的类,所有的代理对象设置成Object即可
public class ProxyInvocationHandler implements InvocationHandler {
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
//proxy:代理类method:代理类的调用处理程序的方法对象
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
//利用反射实现
Object result = method.invoke(target, args);
fare();
return result;
}
private void seeHouse(){
System.out.println("看房");
}
private void fare(){
System.out.println("收中介费");
}
}
JDK的动态代理用到了
InvocationHandler接口()
-
参数proxy:
调用该方法的实例
-
method:
所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。 -
args :
包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{}
Proxy类
- 提供创建动态代理和实例的静态方法。
动态代理的好处(与静态相比):
- 一个动态代理,用来代理某一类业务
- 一个动态代理可以代理多个类,代理的是接口。
4.使用Spring实现AOP
方式一
1.先导入依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
抽象对象
public interface UserService {
void add();
void delete();
void update();
void query();
}
真实对象
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("add方法");
}
@Override
public void delete() {
System.out.println("delete方法");
}
@Override
public void update() {
System.out.println("update方法");
}
@Override
public void query() {
System.out.println("查询方法");
}
}
增强类 加功能(这里写了两个,一个前置一个后置)
public class Log implements MethodBeforeAdvice {
//method:要执行的目标对象的方法
//args:被调用方法的参数
//target:目标对象
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
}
}
@Component//注解注册Bean
public class AfterLog implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
}
}
xml配置
<bean id="userServiceImpl" class="com.jcy.proxy.demo05.UserServiceImpl"></bean>
<bean id="log" class="com.jcy.proxy.demo05.Log"/>
<context:annotation-config/>//注解驱动
<context:component-scan base-package="com.jcy.proxy.demo05"/>//扫描包
//aop配置
<aop:config>
//切入点 切入点表达式
<aop:pointcut id="pointcut" expression="execution(* com.jcy.proxy.demo05.UserServiceImpl.*(..))"/>
//增强的功能
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="log" pointcut-ref="pointcut" />
</aop:config>
测试
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("demo05.xml");
UserService us = (UserService) context.getBean("userServiceImpl");
us.add();
}
}
expression=“execution(* com.jcy.proxy.demo05.UserServiceImpl.*(…))”
切入点表达式
语法结构:execution(权限修饰符 返回类型 类全路径 方法名称 (参数列表))
第二种方式
自定义类来实现AOP
第一种方式加一个功能就要写一个类,还要实现那些懵B的接口太麻烦,那种看看第二种方式吧
public class DiyPointcut {
public void before(){
System.out.println("------在方法之前-------");
}
public void after(){
System.out.println("-------在方法之后-----");
}
}
配置文件
<bean id="userServiceImpl" class="com.jcy.proxy.demo05.UserServiceImpl"></bean>
<bean id="diy" class="com.jcy.proxy.demo05.DiyPointcut"/>
<aop:config>
//切面 就是我们自定义的类
<aop:aspect ref="diy">
//切入点 添加新功能到切入点
<aop:pointcut id="pointcut" expression="execution(* com.jcy.proxy.demo05.UserServiceImpl.*(..))"/>
//前置通知
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
再来看看注解的
方式三
xml
<aop:aspectj-autoproxy/>
通过aop命名空间的<aop:aspectj-autoproxy />声明自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面。当然,spring 在内部依旧采用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,但具体实现的细节已经被<aop:aspectj-autoproxy />隐藏起来了
<aop:aspectj-autoproxy />有一个proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强,当配为<aop:aspectj-autoproxy poxy-target-class=“true”/>时,表示使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。
@Aspect//这是个切面
public class DiyPointcut {
@Before("execution(* com.jcy.proxy.demo05.UserServiceImpl.*(..))")
public void before(){
System.out.println("------在方法之前-------");
}
@After("execution(* com.jcy.proxy.demo05.UserServiceImpl.*(..))")
public void after(){
System.out.println("-------在方法之后-----");
}
}
四、spring整合Mybatis
spring框架对JDBC进行封装,使用JDBCTemplate可以实现对数据库的操作!但是,学过mybatis,就交给mybatis来做这件事
先导包
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
回忆Mybatis
1.编写实体类User
2.编写接口UserMapper
3.编写mybatis-config.xml(核心配置文件)
4.编写UserMapper.xml(记得映射到核心配置文件)
5.测试(先获得sqlSessionFactory对象可以写成一个工具类)
//这是工具类,不是测试
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}catch (Exception e){
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession(true);
}
}
spring集成mybatis
1.编写实体类 Book
@Data//这个是lombok,偷一下懒
public class Book {
private int bookID;
private String bookName;
private int bookCounts;
private String detail;
}
2.编写接口BookMapper
public interface BookMapper {
int addBook(Book book);
int deleteBook(int id);
int updateBook(Book book);
Book queryBook(int id);
List<Book> queryAllBook();
}
3.编写mybatis-config.xml(核心配置文件)
<configuration>
<typeAliases>
<package name="com.jcy.mapper"/>
<package name="com.jcy.pojo"/>
</typeAliases>
</configuration>
是不是觉得空空荡荡有点不舒服(其实都不写这个配置文件,spring都能做,这是给mybatis一点面子(mybatis:那我这?))
4.编写UserMapper.xml(这里一定不要映射到mybatis核心配置文件,否则你会经历到一杯茶,一根烟,一个bug改一天)
<mapper namespace="com.jcy.mapper.BookMapper">
<select id="queryAllBook" resultType="book">
select * from ssmbuild.books;
</select>
</mapper>
5.编写spring配置文件
<!--配置数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="password" value="root"/>
<property name="username" value="root"/>
</bean>
<!--配置SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/jcy/mapper/BookMapper.xml"/>
</bean>
<!--注册sqlSessionTemplate,关联sqlSessionFactory-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<bean id="bookMapper" class="com.jcy.mapper.BookMappperImpl">
<property name="session" ref="sqlSession"/>
</bean>
配置数据源:
重点来了,咱一点一点来看你看这个数据源像不像mybatis里的这个(这里的代码我直接拷贝别的,value="${driver},引用的外部的db.properties(不要在意这些细节))
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
配置SqlSessionFactory:
绑定mybatis-config.xml之后,就可以在这里配置原本在mybatis-config.xml的东西。
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--绑定mybatis-config.xml-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/jcy/mapper/BookMapper.xml"/>
</bean>
然后有了SqlSessionFactory,还少了一个SqlSession,spring中用sqlSessionTemplate代替了它
<!--注册sqlSessionTemplate,关联sqlSessionFactory-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>//这里只能有参构造,因为他没有set方法
</bean>
6.编写BookMappperImpl(别忘了还需要去spring里注入一下,代码上面有)
public class BookMappperImpl implements BookMapper{
private SqlSessionTemplate sqlSession;
public void setSession(SqlSessionTemplate session) {
this.sqlSession = session;
public List<Book> queryAllBook() {
BookMapper mapper = sqlSession.getMapper(BookMapper.class);
return mapper.queryAllBook();
}
}
7.测试类
@Test
public void queryTest(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
BookMapper bean = context.getBean(BookMapper.class);
List<Book> books = bean.queryAllBook();
for (Book book : books) {
System.out.println(book);
}
}
注:
这只是一种整合mybatis的方式,还是最难的(理解了这个在理解其他的就好一点了)
五、事务管理
事务是数据库操作的基本单位,逻辑上的一组操作,要么都成功,要么都失败。
事务的四大特性ACID
- 原子性:不可分割,要么都成功,要么都失败
- 一致性:操作前后总量不变(比如说银行转钱,A有1000给B有1000 A给B转500 A剩500,B剩1500 转来转去A和B总共还是2000)
- 隔离性:多个事务处理相同的数据会产生影响,因此每个事务应该与其他食物隔离开来,防止数据损坏
- 持久性:事务一旦提交后,数据会发生持久性的变化
Spring实现事务管理
-
事务要加到三层架构的Service层中
-
Spring进行事务操作的两种方式
- 编程式事务(不建议使用)
- 声明式事务:Sping进行声明式事务管理,底层使用AOP原理
-
声明式事务管理实现有两种方式
xml配置和注解
xml配置
在spring中配置文件中配置事务管理器
这里要注意tx所需的头文件
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--配置JDBC事务-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务的通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--name为所配置的方法名,propagation为所配置的传播特性-->
<tx:method name="queryAllBook" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
配置aop植入事务
<!--配置aop织入事务-->
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.kuang.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
然后就可以测试一下(在queryAllBook中调用mapper.deleteBook(1);然后在mapper.xml中故意写错看看会不会修改成功)
public class BookMapperImpl implements BookMapper{
private SqlSessionTemplate sessionTemplate;
public void setSessionTemplate(SqlSessionTemplate sessionTemplate) {
this.sessionTemplate = sessionTemplate;
}
public Book queryBook(int id) {
return null;
}
public List<Book> queryAllBook() {
Book book = new Book(4,"java",10,"从入门到入土")
BookMapper mapper = sessionTemplate.getMapper(BookMapper.class);
List<Book> books = mapper.queryAllBook();
mapper.deleteBook(1);
mapper.updateBook(book);
return books;
}
public int addBook(int id) {
return 0;
}
public int deleteBook(int id) {
return 0;
}
public int updateBook(Book book) {
return 0;
}
}
注解配置
<!--配置事务的通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--name为所配置的方法名,propagation为所配置的传播特性-->
<tx:method name="queryAllBook" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
这一步改为
在Spring增加命名空间,开启事务的注解
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
在所需要的类或方法上增加事务注解@Transactional(然后在注解里配置属性)
- 在类上:表明事务在该类的所有方法上生效
- 在方法上:表明事务在该方法上生效
Transactional属性
propagation 事务的传播特性,描述了某一个事务方法被嵌套另一个事务如何传播
- requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
- supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
- mandatory:使用当前事务,如果没有当前事务,就抛出异常。
- required_new:新建事务,如果当前存在事务,把当前事务挂起。
- not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- never:以非事务方式执行操作,如果当前事务存在则抛出异常。
- nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作
Spring 默认的事务传播行为是 PROPAGATION_REQUIRED
isolation:隔离级别
更新丢失 | 脏读 | 不可重复读 | 幻读 | ||
---|---|---|---|---|---|
Read Uncommited | 读未提交 | × | √ | √ | √ |
Read Commited | 读已提交 | × | × | √ | √ |
Repeatable Read | 可重复读 | × | × | × | √ |
Serializable | 串行化 | × | × | × | × |
timeout
超时时间,事务要在一个规定时间内提交,如果不提交会超时,事务回滚,默认为-1,表示不超时,以秒为单位
readOnly
是否只读,默认值false,设置为true,只能查询不能增删改
rollbackFor
回滚,出现哪些异常进行事务回滚
noRollbackFor
不回滚,出现哪些异常不进行事务回滚
ctionManager">
tx:attributes
<tx:method name=“queryAllBook” propagation=“REQUIRED”/>
</tx:attributes>
</tx:advice>
这一步改为
在Spring增加命名空间,开启事务的注解
```xml
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
在所需要的类或方法上增加事务注解@Transactional(然后在注解里配置属性)
- 在类上:表明事务在该类的所有方法上生效
- 在方法上:表明事务在该方法上生效
Transactional属性
propagation 事务的传播特性,描述了某一个事务方法被嵌套另一个事务如何传播
- requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
- supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
- mandatory:使用当前事务,如果没有当前事务,就抛出异常。
- required_new:新建事务,如果当前存在事务,把当前事务挂起。
- not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- never:以非事务方式执行操作,如果当前事务存在则抛出异常。
- nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作
Spring 默认的事务传播行为是 PROPAGATION_REQUIRED
isolation:隔离级别
更新丢失 | 脏读 | 不可重复读 | 幻读 | ||
---|---|---|---|---|---|
Read Uncommited | 读未提交 | × | √ | √ | √ |
Read Commited | 读已提交 | × | × | √ | √ |
Repeatable Read | 可重复读 | × | × | × | √ |
Serializable | 串行化 | × | × | × | × |
timeout
超时时间,事务要在一个规定时间内提交,如果不提交会超时,事务回滚,默认为-1,表示不超时,以秒为单位
readOnly
是否只读,默认值false,设置为true,只能查询不能增删改
rollbackFor
回滚,出现哪些异常进行事务回滚
noRollbackFor
不回滚,出现哪些异常不进行事务回滚