Spring5学习笔记(整理)
涉及的spring模块:
- spring-context(包含了aop、bean、core、expression)
- spring-aspects
- spring-orm(包含了spring-jdbc、spring-tx)
Spring框架概述
- Spring是轻量级的开源的JavaEE框架
- Spring可以解决企业应用开发的复杂性
- Spring有两个核心部分:IOC和AOP
- IOC(Inversion of Control),控制反转,把创建对象过程交给Spring进行管理
- AOP(Aspect Oriented Programming),面向切面编程,不修改源代码进行功能增强
- Spring特点:
- 方便解耦,简化开发
- AOP编程支持
- 方便程序测试
- 方便和其他框架整合
- 方便进行事务操作
- 降低API开发难度
Spring源码设计精妙,是提高Java编程技术的经典学习范例。
jar包和源码下载地址:在官网选择Spring Framework
,点击右上角GitHub图标,进入GitHub源码地址,在Access to Binaries
,进入Spring Framework Artifacts
wiki page,最下面Downloading a Distribution
,进入Spring仓库https://repo.spring.io
,选择Artifacts
-> release/org/springframework/spring/
即可找到对应版本
或者直接进入:https://repo.spring.io/release/org/springframework/spring/
入门案例
创建maven工程,引入spring-context
依赖(包含了其他依赖,core,beans,aop,expression,spring-jcl),spring使用了jcl日志门面,spring-jcl是spring的桥接器(桥接器是干啥的 忘了)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
创建一个普通类,有一个普通方法
public class User {
public void sayHello() {
System.out.println("hello...");
}
}
创建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="user" class="org.example.beans.User"></bean>
</beans>
获取对象:使用spring的context加载配置文件,并从context中获取这个对象
public class MainHello {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("user.xml");
User user = context.getBean("user", User.class);
System.out.println("user = " + user);
user.sayHello();
}
}
输出:
user = org.example.beans.User@367ffa75
hello...
IOC容器
概念和原理
IOC(Inversion of Control):控制反转,把对象创建和对象直接的调用过程交给Spring进行管理;使用IOC的目的是为了降低耦合度;入门案例就是IOC的实现
IOC底层原理:xml解析、工厂模式、反射,在a类中调用b类对象的方法时,a类通过调用工厂类的方法获得b类对象,进而调用方法,在工厂类中,通过xml解析获取到类的class,通过反射创建b类对象,降低了a类和b类的耦合,当b类的类路径改变时,工厂类一样可以正常返回b类对象
IOC的BeanFactory接口
IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
Spring提供IOC容器的两种实现方式(两个接口)
- BeanFactory,IOC容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用,加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象
- ApplicationContext,BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用,加载配置文件时候就会把在配置文件对象进行创建
ApplicationContext有接口实现类
- FileSystemXmlApplicationContext
- ClassPathXmlApplicationContext
IOC操作: Bean管理(创建对象,注入属性)
Bean管理:指两个操作,Spring创建对象、Spring注入属性
Bean管理的两种方式:基于xml配置文件方式实现,基于注解方式实现
Bean的创建和属性注入:(xml方式)
- 创建对象:使用
bean
标签(注意有两种:普通bean和工厂bean) - 注入属性:
- 使用构造器或者set方法注入,直接写属性注入或者使用p名称空间(需要引入p名称空间)
- 注入null和特殊符号
- 注入内部bean:创建bean时,bean的某个属性是一个对象,直接创建为一个bean
- 注入外部bean:使用ref标签
- 级联赋值:注入外部bean,使用ref标签,可以直接设置ref对象的属性的值(需要有该对象的get方法)
- 注入集合,数组、List、Map、Set(可以使用util标签单独创建,被其他多个bean使用,需要引入util名称空间)
Bean的类型:Spring有两种bean,一种普通bean,另外一种工厂bean(实现接口FactoryBean)
- 普通bean:在配置文件中定义bean类型就是返回类型
- 工厂bean:在配置文件定义bean类型可以和返回类型不一样,实现FactoryBean接口,重写方法,定义返回类型,这里一般使用工厂模式加反射返回指定类型
Bean的作用域:
- 在Spring里面,设置创建bean实例是单实例还是多实例,默认情况下,bean是单实例对象
- 创建对象时使用
scope标签
配置,singleton,prototype,request,session - singleton,prototype区别:1,一个单例,一个多例,2,单例在加载配置文件时就已经创建好对象了,多例是在getBean时创建,每次创建都是一个新的对象
- request,session:会将对象放在对应域对象中
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!--基于xml方式 创建对象
id: 唯一标识 不能重复
class: 类的全限定名
创建对象时使用无参构造方法
-->
<bean id="user" class="org.example.beans.User"></bean>
<!--基于xml方式 注入属性(先创建对象 再注入属性)
使用DI: 依赖注入(注入属性) 是IOC的一种实现
使用有参构造或者set方法
-->
<bean id="person" class="org.example.beans.Person">
<!--使用set方法注入 类必须有对应属性的set方法-->
<property name="name" value="zhangsan"></property>
<property name="age" value="10"/>
<property name="male" value="0"/><!--0和1可以表示false和true-->
</bean>
<!--使用构造方法注入-->
<bean id="student" class="org.example.beans.Student">
<constructor-arg name="name" value="lisi"></constructor-arg>
<constructor-arg index="1" value="12"></constructor-arg>
</bean>
<!--使用p名称空间(set方法注入的简化)-->
<bean id="personP" class="org.example.beans.Person" p:name="lisiP" p:age="10" p:male="true">
</bean>
<!--注入null和特殊符号-->
<bean id="personNull" class="org.example.beans.Person">
<!-- <property name="name"><null/></property>-->
<property name="name">
<value><![CDATA[<<xxx]]></value><!--或者使用<>转义的<>-->
</property>
</bean>
<!--注入其他bean 外部bean-->
<bean id="userServiceImpl" class="org.example.service.UserServiceImpl">
<property name="userDao" ref="userDaoImpl"></property>
</bean>
<bean id="userDaoImpl" class="org.example.dao.UserDaoImpl"></bean>
<!--级联赋值1 - 注入其他bean 内部bean 同时给其他bean的字段赋值-->
<bean id="employee" class="org.example.beans.Employee">
<property name="name" value="zhansan"></property>
<property name="department"><!--注入employee的时候直接new一个Department-->
<bean id="department" class="org.example.beans.Department">
<property name="name" value="财务"></property>
</bean>
</property>
</bean>
<!--级联赋值2 - 注入其他bean 内部bean 同时给其他bean的字段赋值-->
<bean id="employee1" class="org.example.beans.Employee">
<property name="name" value="lisi"></property>
<property name="department" ref="department"></property>
<property name="department.name" value="保安"></property><!--需要employee类有该属性的get方法-->
</bean>
<bean id="department" class="org.example.beans.Department"></bean>
<!--注入集合属性-->
<bean id="student1" class="org.example.beans.Student">
<property name="array">
<array>
<value>s1</value>
<value>s2</value>
</array>
</property>
<property name="list">
<list>
<value>v1</value>
<value>v2</value>
</list>
</property>
<property name="courseList">
<list>
<ref bean="course1"></ref>
<ref bean="course2"></ref>
</list>
</property>
<property name="map">
<map>
<entry key="k1" value="v1"/>
<entry key="k2" value="v2"/>
</map>
</property>
</bean>
<bean id="course1" class="org.example.beans.Course">
<property name="name" value="Chinese"></property>
</bean>
<bean id="course2" class="org.example.beans.Course">
<property name="name" value="English"></property>
</bean>
<!--提取公共部分 使用util空间-->
<util:list id="commonStringList">
<value>abc</value>
<value>abc</value>
</util:list>
<bean id="student2" class="org.example.beans.Student">
<property name="list" ref="commonStringList"></property><!--引用公共list bean-->
</bean>
<!--创建工厂bean 该bean实际返回一个Course-->
<bean id="myFactoryBean" class="org.example.beans.MyFactoryBean"/>
<!--bean作用域 scope-->
<bean id="studentScope1" class="org.example.beans.Student" scope="singleton"/>
<bean id="studentScope2" class="org.example.beans.Student" scope="prototype"/>
</beans>
Bean的生命周期:(7个步骤)
- 调用构造器创建bean实例
- 调用set方法设置bean的属性值和其他bean引用
- 调用后置处理器的
postProcessBeforeInitialization()
方法 - 调用初始化方法(
init-method
标签指定) - 调用后置处理器的
postProcessAfterInitialization()
方法 - 获取到bean实例进行使用
- 调用销毁方法(
destroy-method
标签指定),当ioc容器关闭时调用
注意:后置处理器是全局的,所有bean的创建都会调用,需要向ioc注入BeanPostProcessor接口
的实现类对象
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!--bean生命周期-->
<bean id="beanLife" class="org.example.beans.BeanLife" init-method="initBean" destroy-method="destroyBean">
<constructor-arg name="name" value="first"></constructor-arg>
<property name="otherField" value="set property"></property>
</bean>
<bean id="postProcessor" class="org.example.config.MyPostProcessor"/>
</beans>
Bean的自动装配:(xml方式)
- 使用
autowire
标签指定装配规则(名称或者类型),选择byName
或byType
- 按名称,必须属性名称与id相同;按类型必须该类型的只有一个bean或者多个但其中一个
primary="true"
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!--bean生命周期-->
<bean id="employeeAutowire" class="org.example.beans.Employee" autowire="byName">
<property name="name" value="one"/>
</bean>
<!--by name-->
<bean id="department" class="org.example.beans.Department" primary="false">
<property name="name" value="first"/>
</bean>
<!--by type and is primary -->
<bean id="departmentAutowire" class="org.example.beans.Department" primary="true">
<property name="name" value="second"/>
</bean>
</beans>
xml中引入外部properties配置文件:
- 需要引入context名称空间
- 使用
context:property-placeholder
引入配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
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/util http://www.springframework.org/schema/util/spring-util.xsd>
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--直接配置连接池-->
<!-- <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">-->
<!-- <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>-->
<!-- <property name="url" value="jdbc:mysql://localhost:3306/userDb"></property>-->
<!-- <property name="username" value="root"></property>-->
<!-- <property name="password" value="root"></property>-->
<!-- </bean>-->
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!--配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClass}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
</beans>
Bean的创建和属性注入:(注解方式)
- 使用注解的目的:简化xml配置
- Spring对创建对象提供4个注解:@Component,@Service,@Controller,@Repository,功能一样
- 分别代表:普通组件,业务层组件,控制层组件,持久层组件
- 需要开启注解扫描:
<context:component-scan base-package="org.example"></context:component-scan>
,默认扫描所有注解,多个包使用逗号隔开,也可以使用include或exclude过滤 - 属性注入:
- @Autowired:先根据类型自动装配,如果存在多个同类型的则使用属性名称匹配
- @Qualifier:跟@Autowired一起使用,指定名称进行注入
- @Resource:可以根据类型注入,也可以根据名称注入,默认先根据名称匹配,如果没有找到,再根据类型匹配,这个是javax扩展包中的注解,需要导入依赖
- @Value:注入普通类型属性,可以使用
${a.b:c}
取变量a.b的值,默认值为c,或者使用#{}
里面写表达式
完全注解开发,使用配置类替代xml配置文件中的组件扫描配置:
@Configuration //作为配置类 代替xml配置文件中的配置
@ComponentScan(basePackages = {"org.example.annotation"})
public class SpringConfig {
}
启动方式修改为new AnnotationConfigApplicationContext(SpringConfig.class)
总结
- 创建对象:
- 使用xml配置文件
- 使用注解:@Component,@Service,@Controller,@Repository
- 注入属性:
- 使用xml配置文件
- 使用注解: @Autowired,@Qualifier,@Resource,@Value(可以使用#{},${})
- bean的类型:工厂bean,普通bean
- Bean的作用域:
scope
标签,singleton,prototype,request,session - bean生命周期:
BeanPostProcessor
接口注入后置处理器(在初始化方法的前后执行),每个bean可以设置初始化方法(bean初始化后执行,也即是执行完构造方法和一些属性set后)和销毁方法(容器关闭时执行)- 执行顺序:bean构造器,bean其他注入属性的方法,后置处理器before方法,bean自己指定的init方法,后置处理器的post方法,容器关闭时bean自己指定的销毁方法
- 如果使用完全注解开发,初始化方法和销毁方法在
@Bean
注解中可以指定
- IOC容器基本实现:BeanFactory,子接口:ApplicationContext,几个实现:
- FileSystemXmlApplicationContext
- ClassPathXmlApplicationContext
- AnnotationConfigApplicationContext
AOP
概念
- 面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
- 通俗描述:不通过修改源代码方式,在主干功能里面添加新功能
- 术语:
- 连接点,类里面可以被增强的方法称为连接点
- 切入点,实际被增强的方法称为切入点
- 通知(增强),实际增强的逻辑部分称为通知,前置通知,后置通知,环绕通知,异常通知,最终通知
- 切面,将通知应用到切入点的过程称为切面
AOP底层使用动态代理,有接口情况,使用JDK动态代理,创建接口实现类代理对象,增强类的方法;没有接口情况,使用CGLIB(Code Generation Library)动态代理,创建子类的代理对象,增强类的方法。
JDK动态代理主要是使用Proxy.newInstance()创建代理类对象,每次调用代理类的对象的方法时,都会调用参数中InvocationHandler的invoke()方法,该方法的参数中包含被代理对象、调用的方法、方法的参数。
AOP操作(准备工作)
- Spring框架一般是基于AspectJ实现AOP操作,AspectJ不是Spring组成部分,独立AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作。
引入依赖:
<!--aop依赖-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/net.sourceforge.cglib/com.springsource.net.sf.cglib -->
<dependency>
<groupId>net.sourceforge.cglib</groupId>
<artifactId>com.springsource.net.sf.cglib</artifactId>
<version>2.2.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aopalliance/com.springsource.org.aopalliance -->
<dependency>
<groupId>org.aopalliance</groupId>
<artifactId>com.springsource.org.aopalliance</artifactId>
<version>1.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/com.springsource.org.aspectj.weaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>com.springsource.org.aspectj.weaver</artifactId>
<version>1.6.8.RELEASE</version>
</dependency>
基于AspectJ实现AOP操作:xml配置文件方式、注解方式
切入点表达式:
- 作用:知道对哪个类里面的哪个方法进行增强
- 语法结构:
execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]))
举例1:对com.atguigu.dao.BookDao类里面的add进行增强
execution(* com.atguigu.dao.BookDao.add(..))
举例2:对com.atguigu.dao.BookDao类里面的所有的方法进行增强
execution(* com.atguigu.dao.BookDao.* (..))
举例3:对com.atguigu.dao包里面所有类,类里面所有方法进行增强
execution(* com.atguigu.dao.*.* (..))
使用AOP
- 对于任意类的任意方法,都可以配置一个增强类,对被增强的类的方法进行增强(前置通知
@Before
、后置通知@After
、环绕通知@Around
、异常通知@AfterThrowing
、返回通知@AfterReturning
) - 通常使用注解方式开发,增强类使用
@Aspect
,启动类开启自动代理@EnableAspectJAutoProxy
,增强类和被增强类都要放入ioc容器中 - 使用配置文件配置aop的话,不用配置开启自动代理
使用注解:
@Aspect //该类为增强类
@Order(1) //配置增强的优先级 越小优先级越高
@Component
public class UserDaoImplAop {
/**
* 抽取切入点 后面的切入点就可以写这个抽取方法的方法名称()
*/
@Pointcut("execution(* org.example.dao.UserDaoImpl.updateUser(..))")
public void pointDemo() {
}
//前置通知
@Before(value = "pointDemo()")
public void before() {
System.out.println("before...");
}
//@After表示最终通知 方法有没有异常都会执行 方法执行完但在return前执行
@After(value = "execution(* org.example.annotation.dao.UserDaoImplx.add(..))")
public void after() {
System.out.println("after...");
}
//后置通知 增强方法return后执行
@AfterReturning(value = "execution(* org.example.annotation.dao.UserDaoImplx.add(..))")
public void afterReturning() {
System.out.println("afterReturning...");
}
//异常通知 方法出现异常后执行
@AfterThrowing(value = "execution(* org.example.annotation.dao.UserDaoImplx.add(..))")
public void afterThrowing() {
System.out.println("afterThrowing...");
}
//环绕通知 在方法执行前后执行
@Around(value = "execution(* org.example.annotation.dao.UserDaoImplx.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("around before..");
proceedingJoinPoint.proceed();
System.out.println("around after..");
}
}
使用配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop"
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/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--测试使用配置文件配置切面-->
<bean id="userDaoImpl" class="org.example.dao.UserDaoImpl"/>
<bean id="userDaoImplAop" class="org.example.aop.UserDaoImplProxy2"/>
<aop:config>
<!--这个相当于抽取的切入点-->
<aop:pointcut id="pointDemo" expression="execution(* org.example.dao.UserDaoImpl.updateUser(..))"/>
<!--配置切面 使用哪个增强类-->
<aop:aspect ref="userDaoImplAop">
<!--配置增强到具体的方法上-->
<aop:before method="before" pointcut-ref="pointDemo"></aop:before>
<!--这里的切入点没有抽取-->
<aop:after method="after" pointcut="execution(* org.example.dao.UserDaoImpl.updateUser(..))"></aop:after>
</aop:aspect>
</aop:config>
</beans>
总结
- 启动类开启自动代理
@EnableAspectJAutoProxy
- 切入点表达式的写法
- 增强类上使用注解
@Aspect
,增强方法上使用注解(@Before
、@After
、@Around
、@AfterThrowing
、@AfterReturning
) - 增强类和被增强类都要放入ioc容器中
- 目的是在被执行的方法执行前后、返回前后、异常时增强一些逻辑
使用Spring JdbcTemplate操作数据库(增删改查)
Spring框架对jdbc进行的封装,使用JdbcTemplate方便实现对数据库的操作。
需要引入的数据库操作相关依赖:
<!--整合其他mybatis等需要的依赖
包含了spring-jdbc spring-tx spring-beans spring-core
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
</dependency>
<!--mysql jdbc-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
使用:在创建JdbcTemplate时,传入一个DataSource对象即可,比如druid的连接池,然后就可以JdbcTemplate进行数据库操作了
@Component
public class UserDaoImp implements UserDao {
// @Autowired
@Resource
private JdbcTemplate jdbcTemplate;
//插入数据
@Override
public void add(User user) {
String sql = "insert into book values (?,?,?)";
Object[] args = {user.getId(), user.getName(), user.getStatus()};
int add = jdbcTemplate.update(sql, args);
System.out.println("add = " + add);
}
//更新数据
@Override
public void update(User user) {
String sql = "update book set name=?, status=? where id=?";
Object[] args = {user.getName(), user.getStatus(), user.getId()};
int update = jdbcTemplate.update(sql, args);
System.out.println("update = " + update);
}
//删除数据
@Override
public void deleteById(String id) {
String sql = "delete from book where id=?";
int delete = jdbcTemplate.update(sql, id);
System.out.println("delete = " + delete);
}
//查询总数
@Override
public long count() {
String sql = "select count(*) from book";
Long aLong = jdbcTemplate.queryForObject(sql, Long.class);
System.out.println("aLong = " + aLong);
return aLong;
}
//查询对象
//BeanPropertyRowMapper 这个默认mapper是怎么匹配bean字段和表字段的?
@Override
public User selectById(String id) {
String sql = "select * from book where id=?";
User user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), id);
System.out.println("user = " + user);
return user;
}
//查询多个对象
@Override
public List<User> selectAll() {
String sql = "select * from book";
List<User> userList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));
System.out.println("userList = " + userList);
return userList;
}
//批量插入与单条插入类似 只是多次执行单条语句
@Override
public int[] batchAdd(List<User> userList) {
String sql = "insert into book values (?,?,?)";
List<Object[]> args = userList.stream().map(x -> new Object[]{x.getId(), x.getName(), x.getStatus()})
.collect(Collectors.toList());
int[] ints = jdbcTemplate.batchUpdate(sql, args);
System.out.println("Arrays.toString(ints) = " + Arrays.toString(ints));
return ints;
}
//批量更新 与单条更新类似 只是多次执行单条语句
@Override
public int[] batchUpdate(List<User> userList) {
String sql = "update book set name=?, status=? where id=?";
List<Object[]> args = userList.stream().map(x -> new Object[]{x.getName(), x.getStatus(), x.getId()})
.collect(Collectors.toList());
int[] ints = jdbcTemplate.batchUpdate(sql, args);
System.out.println("Arrays.toString(ints) = " + Arrays.toString(ints));
return ints;
}
//批量删除 与单条删除类似 只是多次执行单条语句
@Override
public int[] batchDelete(List<String> idList) {
String sql = "delete from book where id=?";
List<Object[]> args = idList.stream().map(x -> new Object[]{x})
.collect(Collectors.toList());
int[] ints = jdbcTemplate.batchUpdate(sql, args);
System.out.println("Arrays.toString(ints) = " + Arrays.toString(ints));
return ints;
}
}
Spring事务管理
事务概念
- 事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败
- 四个特性(ACID):(1)原子性(2)一致性(3)隔离性(4)持久性
- 事务的隔离级别:读未提交、读已提交、可重复读、串行化。后面三个分别解决了脏读、不可重复读、幻读的问题。MySQL默认是可重复级别
- 脏读:未提交的事务a读取了另一个未提交事务b插入的数据,b事务回滚了,导致a事务读到了脏数据,是一个很严重的问题
- 不可重复读:未提交的事务a读取到了另一个已提交事务b修改的数据(在b提交前读到了),a事务在b事务提交前后读取到的数据不一样,是一个现象
- 幻读:未提交的事务a读取到了另一个已提交事务b插入的数据(在b提交前没读到),a事务在b事务提交后读到了
隔离级别、解决的问题 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交 Read Uncommitted | n | n | n |
读已提交 Read Committed | y | n | n |
可重复读 Read Repeatly | y | y | n |
串行化 Serialization | y | y | y |
Spring事务操作
- Spring事务管理是基于AOP实现的
- 事务一般添加到JavaEE三层结构里面Service层(业务逻辑层)
- 管理事务的两种方式:编程式事务管理和声明式事务管理(一般使用声明式)
- 声明式事务管理:(1)基于注解方式(2)基于xml配置文件方式。
- Spring事务管理API,提供一个接口
PlatformTransactionManager
,代表事务管理器,针对不同的框架提供不同的实现类(如Jpa,Hibernate,使用jdbc则用DataSourceTransactionManager)
声明式事务管理:(使用注解配置)
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"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--开启注解扫描
使用注解
-->
<context:component-scan base-package="org.example"></context:component-scan>
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!--配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClass}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--使用构造器和set注入都可以 也有set方法-->
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>
<!--
测试事务
-->
<!--配置事务管理器 这里配置为jdbc的事务管理器DataSourceTransactionManager 它的顶级接口有其他各个框架的事务管理器实现-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--使用构造器和set注入都可以 也有set方法-->
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>
<!--配置注解驱动的事务管理器
-->
<!-- <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>-->
</beans>
使用注解@Transactional,可以加在类上面,也可以加在方法上面
//测试事务
//如果方法内发生异常 所有操作都会回滚
//如果放在类上 表示所以方法都是事务方法
@Transactional(propagation = Propagation.REQUIRED, //有7种,常用的有两种REQUIRED和REQUIRED_NEW,考虑a方面调用b方法,对于前者,如果a有事务,调用b后使用a里面的事务,如果a没有事务,创建新事务;对于后者,不管a有没有事务都创建新事务
isolation = Isolation.REPEATABLE_READ, //事务隔离级别,有4种,对应三个读问题(脏读,不可重复读,幻读)
timeout = -1, //超时时间,规定事务提交时间,未提交则回滚
readOnly = false, //是否只读,默认false,可以读可以写,如果只读则事务内只读数据
rollbackFor = {}, //回滚,设置出现哪些异常进行回滚
rollbackForClassName = {},
noRollbackFor = {},//不回滚,设置出现那里写异常不回滚
noRollbackForClassName = {})
public void testTx(String id1, String id2) {
userDao.deleteById(id1);
int i = 1 / 0;
userDao.deleteById(id2);
}
声明式事务管理:(使用注解xml配置)
aop的expression需要依赖包:
<!--xml方式配置事务需要aop的这个依赖 使用注解就不需要(aop中的expression需要依赖)
Caused by: java.lang.ClassNotFoundException: org.aspectj.weaver.reflect
-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>com.springsource.org.aspectj.weaver</artifactId>
</dependency>
配置aop实现事务:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--开启注解扫描
使用注解
-->
<!-- <context:component-scan base-package="org.example"></context:component-scan>-->
<!--不与完全注解开发的包内bean重复-->
<context:component-scan base-package="org.example.dao"></context:component-scan>
<context:component-scan base-package="org.example.service"></context:component-scan>
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!--配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClass}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--使用构造器和set注入都可以 也有set方法-->
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>
<!--
测试事务
使用配置文件配置事务
-->
<!--1 配置事务管理器 这里配置为jdbc的事务管理器DataSourceTransactionManager 它的顶级接口有其他各个框架的事务管理器实现-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--使用构造器和set注入都可以 也有set方法-->
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>
<!--配置使用什么事务管理器 如果用xml方式配置事务 这里就不需要配置了-->
<!-- <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>-->
<!--2 配置事务通知(增强部分)-->
<tx:advice id="myTxAdvice">
<!--配置事务的参数-->
<tx:attributes>
<!--准确配置哪个方法上面添加事务(只是配置名字)-->
<tx:method name="testTx2" isolation="REPEATABLE_READ" propagation="REQUIRED"></tx:method><!--其他属性配置-->
<!--也可以使用模糊匹配的方式配置-->
<!-- <tx:method name="testTx*"/>-->
</tx:attributes>
</tx:advice>
<!--3 配置切入点和切面(配置哪个地方需要增强 切面是指增强的过程 也即是将通知配置到切入点的过程)-->
<aop:config>
<!--配置切入点 可以将切入点抽出来作为一个ref进行配置
expression这个需要com.springsource.org.aspectj.weaver依赖!!!
-->
<aop:pointcut id="pointRef" expression="execution(* org.example.service.UserService.testTx2(..)))"/>
<!--配置切面-->
<aop:advisor advice-ref="myTxAdvice" pointcut-ref="pointRef"></aop:advisor>
<!--配置切面 这里直接写了切入点-->
<!-- <aop:advisor advice-ref="myTxAdvice" pointcut="execution(* org.example.service.UserService.testTx(..)))"></aop:advisor>-->
</aop:config>
</beans>
事务操作(完全使用注解配置,将上面注解实现时xml里面的内容放在配置类里面)
@Configuration //配置类
@ComponentScan(basePackages = {"org.example"}) //包扫描
@EnableTransactionManagement(mode = AdviceMode.PROXY) //开启事务管理 Proxy是代理模式(实现有jdk和cglib) 还有一个aspectj模式(?)
public class TxConfig {
//注入dataSource
@Bean //方法名为bean的名称
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/user_db_spring");
dataSource.setUsername("root");
dataSource.setPassword("999999");
return dataSource;
}
//注入JdbcTemplate
@Bean
public JdbcTemplate jdbcTemplate(@Autowired @Qualifier("dataSource") DataSource dataSource) { //autowired自动注入
return new JdbcTemplate(dataSource);
}
//注入事务管理器transactionManager
@Bean
public TransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
总结
- 开启事务管理,注入事务管理对象,使用
@Transactional
注解
Spring5新特性
- 整个Spring5框架的代码基于Java8,运行时兼容JDK9,许多不建议使用的类和方法在代码库中删除
- Spring5框架自带了通用的日志封装,移除Log4jConfigListener,官方建议使用Log4j2
- 日志配置文件:log4j2.xml
- 使用sl4j日志门面,需要使用log4j2的适配器
private static final Logger log = LoggerFactory.getLogger(MainApplication.class);
<!--log4j2适配slf4j的适配器(log4j的日志打到slf4j里面) 这里是log4j的2.17.1版本log4j2
包含了log4j-api和log4j-core slf4j-api
= slf4j + log4j2
-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.17.1</version>
</dependency>
- Spring5框架核心容器支持
@Nullable
,@NonNull
注解,可以使用在方法、属性、参数上面,表示可以为空、不为空(只是标注,并不影响编译和运行) - Spring5核心容器支持函数式风格GenericApplicationContext,将对象注入context
@Test
public void testGenericApplicationContext() {
//1 创建GenericApplicationContext对象
GenericApplicationContext context = new GenericApplicationContext();
//2 调用context的方法对象注册
context.refresh();
context.registerBean(User.class, () -> new User("1", "a", "1")); //没有指定beanName那么就是类的全限定名名称
context.registerBean(User.class, () -> new User("2", "a", "1")); //覆盖了上面的bean
context.registerBean("user",User.class, () -> new User("3", "a", "1")); //新注册的
//3 获取在spring注册的对象
String[] names = context.getBeanNamesForType(User.class);
System.out.println("names = " + Arrays.toString(names)); //names = [org.example.beans.User, user]
int count = context.getBeanDefinitionCount(); //获取bean的数量
System.out.println("count = " + count); //2
//get bean
User user = (User)context.getBean("org.example.beans.User");
System.out.println("user = " + user); //user = User(id=2, name=a, status=1)
User user1 = context.getBean("user", User.class);
System.out.println("user1 = " + user1); //user1 = User(id=3, name=a, status=1)
System.out.println("user == user1 = " + (user == user1)); //false
}
- Spring5支持整合JUnit5
使用junit4:需要引入junit4依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<!-- <scope>test</scope>-->
</dependency>
@RunWith(SpringJUnit4ClassRunner.class) //spring使用junit4框架测试
@ContextConfiguration(value = "classpath:tx.xml") //代替了使用xml文件获取context
//@ContextConfiguration(classes = TxConfig.class) //代替了使用配置类获取context
public class JUnit4Test {
@Autowired
UserService userService; //直接注入
@Test
public void testTx() {
userService.truncateTable("book");
userService.addUser(new User("1001", "a", "a"));
userService.addUser(new User("1002", "b", "b"));
userService.testTx("1001", "1002");
}
}
使用JUnit5:需要引入junit5的依赖
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.0-M1</version>
<!--在src下也使用测试 去掉test scope-->
<!-- <scope>test</scope>-->
</dependency>
//@ExtendWith(SpringExtension.class)
//@ContextConfiguration(value = "classpath:tx.xml") //代替了使用xml文件获取context
//@ContextConfiguration(classes = TxConfig.class) //代替了使用配置类获取context
@SpringJUnitConfig(locations = "classpath:tx.xml") //复合注解 相当于上面两个
//@SpringJUnitConfig(classes = TxConfig.class) //复合注解 相当于上面两个
public class JUnit5Test {
@Autowired
UserService userService; //直接注入
@Test //使用junit5
void testTx() { //可以不写public
userService.truncateTable("book");
userService.addUser(new User("1001", "a", "a"));
userService.addUser(new User("1002", "b", "b"));
userService.testTx("1001", "1002");
}
}
Spring5框架新功能(Webflux)
需要基础知识:SpringMVC,SpringBoot,Maven,Java8新特性
本文来自博客园,作者:Bingmous,转载请注明原文链接:https://www.cnblogs.com/bingmous/p/15643689.html