Spring5
中文文档: https://www.cntofu.com/book/95/index.html
框架概述
- 轻量框架
- AOP与IOC为核心部分
- 解决企业开发的复杂性
- 特点
- 方便解除耦合, 简化开发
- AOP编程支持
- 方便测试
- 方便和其他框架一起整合使用
- 方便进行事物操作
- 方便使用API
- 源码是Java开发经典示范
下载代码使用Jar包: https://repo.spring.io/
入门案例
SpringDemo1
XML文件中配置对象标签
1 | <bean id="user" class="com.springdemo.spring5.User"></bean> |
测试加载配置文件
1 |
|
IOC容器
IOC底层原理
IOC: Inversion of control 控制反转
- 把对象创建和对象之间和调用过程都交给Spring管理
- 降低耦合
- 入门案例就是IOC实现
IOC底层原理
XML解析, 工厂模式, 反射
原始模式
工厂模式 (耦合度依旧高 )
IOC过程 (使用XML和反射进一步降低耦合)
IOC接口(BeanFactory)
IOC底层就是对象工厂
Spring提供IOC容器实现两种方式(两个接口)
Bean Factory: IOC容器基本实现, 是Spring内部的是使用接口, 不提供开发人员使用
加载配置文件的时候不会创建对象, 使用是时候才创建对象
ApplicatonContext: BeanFactory的子接口, 更多强大的功能, 开发人员使用
加载配置文件的时候就会创建对象
- ApplicationContext借口有实现类
IOC操作Bean管理(基于XML)
什么是Bean管理
- Spring创建对象
- Spring注入属性
Bean管理有两种方式
基于XML方式创建
1
<bean id="user" class="com.springdemo.spring5.User"></bean>
- 在Spring中使用bean配置标签, 添加属性就可以创建对象
- bean标签的常用基本属性
- id: 唯一标识
- class: 类的全路径(包类路径)
- name: 跟id差不多, 但是可以加特殊字符
- 默认也是执行无参数构造(Constructor)
基于XML方式注入属性
DI: 依赖注入, 就是注入属性
为IOC中一种具体实现, 需要在创建对象基础之上完成
第一种注入方式: set方式注入
1
2
3
4
5
6<bean id="book" class="com.springdemo.spring5.Book">
<!-- 使用property标签实现属性注入-->
<property name="bname" value="易筋经"></property>
<property name="bauthor" value="达摩"></property>
</bean>第二种注入方式: 用有参数构造器注入
如果不输入constructor-arg标签会报错, 因为bean默认调用无参构造器, 如果没有就会报错
1
2
3
4<bean id="orders" class="com.springdemo.spring5.Orders">
<constructor-arg name="oname" value="电脑"></constructor-arg>
<constructor-arg name="address" value="China"></constructor-arg>
</bean>constructor-arg标签中还有 index标签
可以用索引值找对象中的值, 比如 index=”0” 在这里代表”oname”
使用p名称空间注入
可以简化xml配置
先在配置文件中添加一个xmlns:p的名称空间
1
2
3
4<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">然后进行属性注入
1
2<bean id="book" class="com.springdemo.spring5.Book" p:bname="九阳神功" p:bauthor="无名氏">
</bean>
IOC操作Bean管理(基于注解)
字面量
null值
使用null标签
1
2
3<property name="baddress">
<null/>
</property>属性值包含特殊符号
<> <> 转义字符
把带特殊符号内容写到CDATA
1
2
3
4
5<property name="baddress">
<value>
<![CDATA[<<南京>>]]>
</value>
</property>
注入属性-外部Bean
1
2
3
4<bean id="userService" class="com.springdemo.spring5.service.UserService">
<property name="userDao" ref="userDaoImpl"></property>
</bean>
<bean id="userDaoImpl" class="com.springdemo.spring5.dao.UserDaoImpl"></bean>测试一下
1
2
3
4
5
6ApplicationContext context =
new ClassPathXmlApplicationContext("bean2.xml");
UserService userService = context.getBean("userService", UserService.class);
// System.out.println(userService);
userService.add();注入属性-内部Bean和级联赋值
一对多关系: 部门和员工
这就是内部Bean, 使用嵌套
1
2
3
4
5
6
7
8
9<bean id="emp" class="com.springdemo.spring5.bean.Emp">
<property name="ename" value="lucy"></property>
<property name="gender" value="girl"></property>
<property name="dept">
<bean id="dept" class="com.springdemo.spring5.bean.Dept">
<property name="dname" value="安保部"></property>
</bean>
</property>
</bean>级联赋值
第一种写法
1
2
3
4
5
6
7
8
9<bean id="emp" class="com.springdemo.spring5.bean.Emp">
<property name="ename" value="lucy"></property>
<property name="gender" value="girl"></property>
<!-- 级联赋值-->
<property name="dept" ref="dept"></property>
</bean>
<bean id="dept" class="com.springdemo.spring5.bean.Dept">
<property name="dname" value="财务部"></property>
</bean>第二种写法
需要先在Emp中生成dept的get方法
1
2
3
4
5
6
7
8
9
10<bean id="emp" class="com.springdemo.spring5.bean.Emp">
<property name="ename" value="lucy"></property>
<property name="gender" value="girl"></property>
<!-- 级联赋值-->
<property name="dept" ref="dept"></property>
<property name="dept.dname" value="技术部"></property>
</bean>
<bean id="dept" class="com.springdemo.spring5.bean.Dept">
<property name="dname" value="财务部"></property>
</bean>
IOC操作Bean管理(xml注入集合属性)
- 注入数组类型属性
- 注入List集合类型属性
- 注入Map集合类型属性
- 注入Set集合类型属性
1 | <bean id="stu" class="com.springdemo.spring5.collectiontype.Stu"> |
在集合里面设置对象类型值
先创建多个对象
1
2
3
4
5
6<bean id="course1" class="com.springdemo.spring5.collectiontype.Course">
<property name="cname" value="Spring5"></property>
</bean>
<bean id="course2" class="com.springdemo.spring5.collectiontype.Course">
<property name="cname" value="MyBatis"></property>
</bean>然后引入到list里的值
1
2
3
4
5
6<property name="courseList">
<list>
<ref bean="course1"></ref>
<ref bean="course2"></ref>
</list>
</property>
把集合注入部分提取出来
先在xml配置文件配置一个名称空间
1
2
3
4
5
6<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">用util标签完成list集合注入提取
1
2
3
4
5
6
7
8
9
10<!-- 提取-->
<util:list id="bookList">
<value>易筋经</value>
<value>九阴真经</value>
<value>九阳神功</value>
</util:list>
<!-- 注入使用-->
<bean id="book" class="com.springdemo.spring5.collectiontype.Book">
<property name="list" ref="bookList"></property>
</bean>
IOC操作Bean管理(FactoryBean)
Spring里面有两种Bean
普通Bean和FactoryBean
普通Bean–定义什么类型就返回什么类型
1 | <bean id="book" class="com.springdemo.spring5.collectiontype.Book"> |
工厂Bean–定义类型可以不和返回类型一致
创建一个类作为factoryBean, 实现接口FactoryBean
1 | <bean id="myBean" class="com.springdemo.spring5.factorybean.MyBean"></bean> |
1 | public class MyBean implements FactoryBean<Course> { |
1 |
|
定义类型(MyBean)和返回(Course)不一样
IOC操作Bean管理(作用域)
在Spring里面, 可以设置创建的bean实例是单实例还是多实例
默认情况下, 默认是但实例对象
1 | ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml"); |
以上测试代码结果为
com.springdemo.spring5.collectiontype.Book@b2c9a9c
com.springdemo.spring5.collectiontype.Book@b2c9a9c
地址相同表示是但实例对象
设置多实例对象
1 | <bean id="book" class="com.springdemo.spring5.collectiontype.Book" scope="prototype"> |
bean 标签的scope属性值
1. singeton(默认值)
2. prototype
输出为:
com.springdemo.spring5.collectiontype.Book@b2c9a9c
com.springdemo.spring5.collectiontype.Book@4c178a76
设置为singleton, 加载spring配置文件的时候, 就创建了实例对象
设置为prototype, 在调用getBean方法都时候才创建多实例对象
还有其他属性值
- request
- session
IOC操作Bean管理(生命周期)
生命周期: 从对象的创建到对象销毁的过程
Bean的生命周期
1. 通过构造器创建bean实例(无参构造)
2. 为bean的属性设置值和对其他bean的引用(调用set方法)
3. 调用bean的初始化方法(需要进行配置)
4. bean可以使用了(对象获取到了)
5. 当容器关闭, 调用bean的销毁方法(需要配置)
初始化方法和销毁方法分别是bean标签中的两个属性init-method 和 destroy-method
1 | <bean id="orders" class="com.springdemo.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod"> |
一下为测试生命周期代码
1 |
|
Bean的后置处理器, 更完整的生命周期实际有7步
通过构造器创建bean实例(无参构造)
为bean的属性设置值和对其他bean的引用(调用set方法)
把bean实例传递到bean的后置处理器的方法
- ```java
public Object postProcessBeforeInitialization(Object bean, String beanName)1
2
3
4
5
6
7
4. 调用bean的初始化方法(需要进行配置)
5. **把bean实例传递到bean的后置处理器的另外一个方法**
1. ```java
public Object postProcessAfterInitialization(Object bean, String beanName)
- ```java
bean可以使用了(对象获取到了)
当容器关闭, 调用bean的销毁方法(需要配置)
演示后置处理器
1 | <bean id="myBeanPost" class="com.springdemo.spring5.bean.MyBeanPost"></bean> |
配置之后控制台输出:
1–无参调用了
2–调用set
初始化之前
3–执行初始化
初始化之后
4–获取实例对象
com.springdemo.spring5.bean.Orders@765d7657
5–执行销毁方法
IOC操作Bean管理(XML自动装配)
根据指定装配规则, Spring会自动匹配属性值注入
autowire标签属性
1. byName
1 | <bean id="emp" class="com.springdemo.spring5.autowire.Emp" autowire="byName"> |
2. byType
1 | <bean id="emp" class="com.springdemo.spring5.autowire.Emp" autowire="byType"> |
实际开发用的较少, 一般使用注解
IOC操作Bean管理(引用外部属性文件)
直接配置数据库信息
配置德鲁伊(druid)连接池
jar包引入
1
2
3
4
5
6
7
8
9
10
11
12<!-- 配置连接池 -->
<!-- DruidDataSource dataSource = new DruidDataSource(); -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!-- dataSource.setDriverClassName("com.mysql.jdbc.Driver");
set方法注入
-->
<!-- 获取properties文件内容,根据key获取,使用spring表达式获取 -->
<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>1
2
3
4
5
6
7
8
9
10
11
12<!-- 配置连接池 -->
<!-- DruidDataSource dataSource = new DruidDataSource(); -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!-- dataSource.setDriverClassName("com.mysql.jdbc.Driver");
set方法注入
-->
<!-- 获取properties文件内容,根据key获取,使用spring表达式获取 -->
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/"></property>
<property name="username" value="root"></property>
<property name="password" value="333"></property>
</bean>
引入外部配置文件配置
1 | com.mysql.jdbc.Driver = |
引入Spring配置文件中
引入context名称空间
1
2
3
<beans xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation=" http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">在配置文件中引入
1
2
3
4
5
6
7
8<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${prop.driverClass}"></property>
<property name="url" value="${prop.url}"></property>
<property name="username" value="${prop.userName}"></property>
<property name="password" value="${prop.password}"></property>
</bean>
IOC操作Bean管理(基于注解方式)
注解就是特殊标记(annotation)
格式: @注解名称(属性名=属性值, 属性名=属性值)
目的: 简化xml配置
Spring 针对Bean管理(创建对象)提供注解
1. @Component
2. @Service (Service)
3. @Controller (Web)
4. @repository (DAO)
功能一样, 都可以创建bean实例, 只是建议用在不同地方, 也可以混用
基于注解的实例创建
需要引入AOP依赖的jar包
开启组件扫瞄
首先新建context名称空间
1 |
|
第一种添加多个package扫描的方法为逗号隔开
1 | <context:component-scan base-package="com.springdemo.spring5.dao, com.springdemo.spring5.service"></context:component-scan> |
第二种方法是直接写共同点上层目录
1 | <context:component-scan base-package="com.springdemo"></context:component-scan> |
然后使用注解创建对象
1 | import org.springframework.stereotype.Service; |
组件扫描的细节问题
可以配置大目录哪些扫描那些不扫描
use-default-filter =”false” 代表不是默认的全部扫描
context:include-filter 设置哪些扫描
expression=…..Service 代表只扫描带Service注解的方法
1 | <context:component-scan base-package="com.springdemo" use-default-filters="false"> |
context:exclude-filter 设置哪些不扫描
1 | <context:component-scan base-package="com.springdemo"> |
基于注解方式实现属性注入
@AutoWired 根据属性类型注入
创建service和dao的对象, 然后添加创建对象的注解
1
2
3
4
5
6
7
8
9
10
public class UserService {
//不需要set方法
private UserDao userDao;
public void add(){
System.out.println("service add.......");
}
}@Qualifier 根据属性名称注入
需要和@AutoWired 一起使用
实现场景: 如果UserDao接口有多个实现类, AutoWired会不知道找那个进行注入, 加上@Qualifier可以让它找到对应名称的实现类
1
2
3
4
5
6
7
8
9
10
11
12
public class UserService {
//不需要set方法
//为该类中@Repository的默认值, 可在@Repository的注解value中修改,需要对应相同名称
private UserDao userDao;
public void add(){
System.out.println("service add.......");
userDao.add();
}
}@Resource 可以根据类型注入 也可以根据名称注入
这是javax里的注解, 所以Spring还是建议前两个
1
2
3
4
5
6
7// @Resource //根据类型注入
//根据名称注入
private UserDao userDao;
public void add(){
System.out.println("service add.......");
userDao.add();
}@Value 根据普通类型属性注入
1
2
3
4
5
6
7
8
9//根据名称注入
private UserDao userDao;
private String name;
public void add(){
System.out.println("service add......."+name);
userDao.add();
}
完全注解开发
创建配置类
1 | //作为配置类可以替代xml |
编写测试类
1 |
|
以上代码调用了
1 | new AnnotationConfigApplicationContext(SpringConfig.class) |
来使用配置类, 与之前调用xml文件的方法不一样
实际开发中是基于SpringBoot
AOP面向切面(方面)
AOP概念
可以对业务逻辑的各个部分进行隔离, 从而降低耦合度
通俗描述, 不通过修改源代码的方式, 在主干内添加新功能
例子: 登录界面业务
AOP底层原理
AOP底层使用动态代理
动态代理是JavaSE基础里的内容:
相关有用的链接: https://www.cnblogs.com/whirly/p/10154887.html
尚硅谷视频教程里也有(p659-686): https://www.bilibili.com/video/BV1Kb411W75N?p=659
有接口的情况, 使用JDK的动态代理
- 创建接口实现类代理对象, 增强类的方法
没有接口的情况, 使用CGLIB动态代理
- 创建子类代理对象, 增强类里的方法
JDK动态代理
使用Proxy类里面的方法创建代理对象
调用newProxyInstance, 有三个参数:
- ClassLoader
- 增强方法所在的类, 这个类的实现接口, 支持多个接口
- InvocationHandler 实现里面的接口, 创建代理对象的增强方法
*******代码实现(底层代码):
创建interface
创建实现类
1
2
3
4
5
6
7
8
9
10
11
12
13public class UserDaoImpl implements UserDao{
public int add(int a, int b) {
System.out.println("added ");
return a+b;
}
public String update(String id) {
System.out.println("updated");
return id;
}
}使用proxy类创建接口代理对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35public class JDKProxy {
public static void main(String[] args) {
//创建接口实现类的对象
/* Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
//匿名内部类
}
});*/
Class[] interfaces = {UserDao.class};
UserDaoImpl userDao = new UserDaoImpl();
UserDao dao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
int result = dao.add(1, 2);
System.out.println("Result::"+result);
}
}
class UserDaoProxy implements InvocationHandler{
//1 把创建的是谁的代理对象, 把谁传递过来
//有参数构造传递
private Object obj;
public UserDaoProxy(Object obj){
this.obj = obj;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before method: "+ method.getName()+ " arguments passed: "+ Arrays.toString(args));
Object res = method.invoke(obj, args);
System.out.println("after method");
return res;
}
}最终运行main方法的输出为
1
2
3
4before method: add arguments passed: [1, 2]
added
after method
Result::3
AOP操作术语
连接点
类里面哪些方法可以被增强, 这些方法称为连接点
切入点
实际被真正增强的方法, 称为切入点
通知 (增强)
实际增强的逻辑部分称为通知 (增强)
- 前置通知
- 后置通知
- 环绕通知
- 异常通知
- 最终通知
切面
是动作
- 把通知应用到切入点的过程就叫切面
AOP操作(准备)
Spring框架中一般基于AspectJ实现AOP操作
- AspectJ不是Spring组成部分, 是独立AOP框架, 一般和Spring一起使用
基于AspectJ实现AOP
- 基于XML配置文件实现
- 基于注解方式实现(使用)
在工程项目里面引入AOP相关依赖
切入表达式
切入表达式作用: 知道哪个累里面的哪个方法进行增强
语法结构
execution([权限修饰符][返回类型][类全路径][方法名]([参数列表]))
例子:
对com.atguigu.dao.BookDao类里的add进行增强
execution(* com.atguigu.dao.BookDao.add(..))
对com.atguigu.dao.BookDao类里的所有方法进行增强
execution(* com.atguigu.dao.BookDao.* (..))
对com.atguigu.dao里的所有类和类里的所有方法进行增强
execution(* com.atguigu.dao.*.*(..))
AOP操作(AspectJ注解)
创建类, 在里面定义方法
创建增强类(编写增强的逻辑)
在增强类中创建方法, 让不同方法代表不同通知类型
1
2
3
4
5
6
7//增强类
public class UserProxy {
//前置通知
public void before(){
System.out.println("before.....");
}
}
进行通知的配置
在spring配置文件中开启注解扫描
先添加命名空间context和aop
然后使用xml配置
1
2
3
4
5
6
7
8
9
10
11
12
13
<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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.springdemo.spring5.aopanno"></context:component-scan>
</beans>使用注解创建User和UserProxy对象
1
2
3
4
5
6
public class User {
public void add(){
System.out.println("add....");
}
}1
2
3
4
5
6
7
8//增强类
public class UserProxy {
//前置通知
public void before(){
System.out.println("before.....");
}
}在增强类上面添加注解@Aspect
1
2
3
4
5
6
7
8
9//增强类
//表示生成代理对象
public class UserProxy {
//前置通知
public void before(){
System.out.println("before.....");
}
}在spring配置文件中开启生成代理对象
1
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
配置不同类型的通知
在增强类里面, 作为通知的方法上面, 添加通知类型注解, 使用切入点表达式配置
1
2
3
4
5
6
7
8
9
10
11
12//增强类
//表示生成代理对象
public class UserProxy {
//前置通知
//Before 注解表示作为前置通知
public void before(){
System.out.println("before.....");
}
}
最终测试一下
1 |
|
测试结果
1 | before..... |
所有类型的通知通过注解实现
1 | //前置通知 |
最终执行顺序可以通过测试结果看出
1 | Before Around..... |
公共(相同的)切入点抽取
1
(value = "execution(* com.springdemo.spring5.aopanno.User.add(..))")
方法如下, 使用@Pointcut注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14//相同切入点抽取
public void pointdemo(){
}
//前置通知
//Before 注解表示作为前置通知
public void before(){
System.out.println("before.....");
}
public void after(){
System.out.println("after.....");
}有多个增强类对同个方法增强, 设置增强优先级
在增强类上面添加注解@Order, 值越小优先级越高
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class PersonProxy {
//相同切入点抽取
public void pointdemo(){
}
//前置通知
//Before 注解表示作为前置通知
public void before(){
System.out.println("Person Before.....");
}
}与UserProxy 设置为@Order(2)后, 测试执行输出为
1
2
3
4
5
6
7
8Person Before.....
Before Around.....
before.....
add....
After Around.....
after.....
AfterReturning.....
AOP操作(AspectJ配置文件)
创建类, 增强类和被增强类
1
2
3
4
5public class Book {
public void buy(){
System.out.println("buy....");
}
}1
2
3
4
5
6public class BookProxy {
// @Before()
public void before(){
System.out.println("before......");
}
}在spring配置中创建两个类对象
1
2<bean id="book" class="com.springdemo.spring5.aopxml.Book"></bean>
<bean id="bookProxy" class="com.springdemo.spring5.aopxml.BookProxy"></bean>在spring配置文件中配置切入点
1
2
3
4
5
6
7
8
9
10
11<!-- 配置aop增强-->
<aop:config>
<!-- 切入点-->
<aop:pointcut id="p" expression="execution(* com.springdemo.spring5.aopxml.Book.buy(..))"/>
<!-- 配置切面-->
<aop:aspect ref="bookProxy">
<aop:before method="before" pointcut-ref="p"/>
</aop:aspect>
</aop:config>输出:
1
2before......
buy....如果想完全使用注解开发
创建配置类
使用三个注解
@Configuration
@ComponentScan(basePackages = {“com.springdemo”})
@EnableAspectJAutoProxy(proxyTargetClass = true)1
2
3
4
5
public class ConfigAop {
}
JdbcTemplate
准备工作
配置spring的xml配置文件
1 | <!-- 数据库连接池 --> |
通过注解, 然后创建service类, 创建dao类, 在dao中注入jdbcTemplate对象
1 |
|
1 |
|
添加修改删除操作
创建一个Book实体类之后, 直接调用注入后的jdbcTemplate的update方法就可以了
1 |
|
测试service里面的调用
1 |
|
然后一样的逻辑使用update为DaoImpl加入update修改和delete删除操作
1 |
|
查询操作
返回某个值
查询表里有多少条记录:
1 |
|
使用queryForObject, 第二个参数是需要返回值得类型的参数
返回对象
查询图书详情
1 |
|
注意: 如果需要输出不为null, 数据库中的对应column名字需要和对象的属性名一致(原理是get方法名字一致)
返回集合
使用query方法
1 | @Override |
JdbcTemplate批量操作
批量添加
把每个数组集合中的值添加到数据库
1 | String sql = "insert into t_book values(?,?,?)"; |
返回结果是个数组 int[]
1 | [1,1,1] |
批量修改删除也和批量添加差不多, 只是sql参数不同
Spring事物操作
事务一般添加到JavaEE三层架构里的service层上
Spring中有两种事务管理操作
- 编程式事务管理(通过代码实现)
- 声明式事务管理(通过配置实现)
声明式事务管理
- 基于注解
- 基于xml
Spring中进行声明事务管理, 底层使用AOP原理
Spring事务管理API:
- PlatformTransctionManager—针对不同的框架有不同的实现类
- DataSourceTranscationManager
- HibernateTransactionManager
基于注解
在Spring配置文件中配置事务管理器
1
2
3<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSourcze"></property>
</bean>开启事务注解
引入tx名称空间
```xml
<tx:annotation-driven transaction-manager=”dataSourceTransactionManager”/>1
2
3
4
5
6
7
```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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx/spring-tx.xsd">
在service类上面加上事务注解
@Transctional — 在类上面可以, 也可以添加在方法上面
1
2
3
4
5@Service
@Transcational
public class UserService(){
}
@Transcational() 的参数配置
重要参数:
- propagation – 事务传播行为
- 有事务方法调用其他方法(有事务或无事务)
- REQUIRED
- REQUIRED_NEW
- …
- isolation – 隔离级别
- 三个问题: 赃读, 不可重复读, 幻读. 分别对应: 读未提交, 读已提交, 可重复读
- timeout – 超时时间
- 一定时间内不提交就会回滚
- 默认值-1, 表示不超时. 值以秒为单位
- readOnly – 是否只读
- 默认值false — 表示可以增删改查
- true — 只能查询
- rollbackFor – 回滚
- 出现哪些异常进行回滚
- noRollBackFor – 不回滚
- 出现哪些异常不回滚
基于xml
- 配置事务管理器
- 配置通知
- 配置切入点和切面
1 | <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> |
完全注解
创建配置类替代xml配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//配置类
//组件扫描
//开启事务
public class TxConfig{
//创建数据库连接池
public DuridDataSource getDruidDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///user_db");
dataSource.setUsername("root");
dataSource.setPassword("333");
return dataSource;
}
//创建jdbcTemplate对象
public JdbcTemplate getJdbcTeamplate(DataSource dataSource){
JdbcTemplate = jdbcTemplate = new JdbcTemplate();
//注入dataSource
jdbcTemplalte.setDataSource(dataSource):
return jdbcTemplate;
}
//创建事务管理器
public DataSourceTranscationManager getDataSourceTranscationManager(DataSource dataSource){
DataSourceTranscationManager dataSourceTranscationManager = new DataSourceTranscationManager();
dataSourceTranscationManager.setDataSource(dataSource);
return dataSourceTranscationManager;
}
}
Spring5新功能
整个Spring5框架基于Java8, 运行时兼容jdk9, 许多不建议使用的类在代码中删除
自带了通用的日志封装
可以整合其他的第三方日志
Spring5 以及移除了Log4jConfigListener, 官方建议使用Log4j2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,可以看到log4j2内部各种详细输出-->
<configuration status="INFO">
<!--先定义所有的appender-->
<appenders>
<!--输出日志信息到控制台-->
<console name="Console" target="SYSTEM_OUT">
<!--控制日志输出的格式-->
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</console>
</appenders>
<!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
<!--root:用于指定项目的根日志,如果没有单独指定Logger,则会使用root作为默认的日志输出-->
<loggers>
<root level="info">
<appender-ref ref="Console"/>
</root>
</loggers>
</configuration>1
2
3
4
5
6
7
8public class UserLog{
private static final Logger log = LoggerFactory.getLogger(UserLog.class);
public static void main (String[] args){
log.info("Hello log4j2");
log.warn("");
}
}
核心容器
- @Nullable注解
- 可以用在方法上, 属性上, 参数上, 表示对应的内容可以为空
- 方法返回值可以为空
- 属性值可以为空
- 参数可以为空
- 可以用在方法上, 属性上, 参数上, 表示对应的内容可以为空
- 支持lambda表达式
- @Nullable注解
函数式风格创建对象
GenericApplicationContext 函数式风格创建对象, 交给Spring管理
lambda表达式
调用context的方法注册对象
1 | GenericApplicationContext context = new GenericApplicationContext(); |
测试改进
整合JUnit4
引入依赖 spring-test
创建测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(“classpath:bean1.xml”)
```java
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(“classpath:bean1.xml”)
public class test{@AutoWired private UserService userService; @Test public void test1(){ userService.accountMoney(); }
}
1
2
3
4
5
6
7
8
9
10
11
12
### 整合JUnit5
1. 引入JUnit5依赖
2. 创建测试类, 使用注解
- ```java
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:bean1.xml")
使用复合注解@SpringJUnitConfig(location=”classpath:bean1.xml”) 可以代替上面两个注解
WebFlux
SpringWebFlux介绍
用于web开发, 功能与SpringMVC类似, 使用当前一种比较流行的响应式编程而出现的框架
传统框架比如SpringMVC, 都是基于Servlet. 而Webflux是一种异步非阻塞的框架, 在Servlet3.1以上才支持
核心基于Reactor的相关API实现的
什么事异步非阻塞
异步同步
非阻塞阻塞
↑上面都是针对对象不一样
异步同步针对调用者, 调用者发送请求, 如果等着对方回应才去做其他事情就是同步, 不等就是异步
阻塞和非阻塞被调用者收到请求, 做完请求任务之后才反馈就是阻塞, 收到请求之后后马上反馈在做其他事就是非阻塞
WebFlux的优势
非阻塞有什么好处:
- 可以在有限资源下, 提高系统吞吐量和伸缩性, 处理跟多请求, 以Reactor作为基础实现响应式编程
函数式编程: 基于java8, webflux使用java8中的函数式编程方式来实现路由请求
比较SpringMVC
都可以使用注解, 都可以运行在Tomcat等容器中
SpringMVC基于命令式编程, WebFlux使用异步响应式编程
响应式编程
电子表格程序就是响应式编程的一个例子。单元格可以包含字面值或类似”=B1+C1”的公式,而包含公式的单元格的值会依据其他单元格的值的变化而变化。
示例:
Java8及其之前版本
- 提供的观察者模式, 两个类Observer和Observable