Spring5
Spring入门
Spring安装
下载地址
https://repo.spring.io/ui/repos/tree/General/release%2Forg%2Fspringframework%2Fspring%2F5.2.6.RELEASE
将jar包导入到工程 其中4个是Spring的核心包 在下载文件的lib目录下
Spring入门
如何使用Spring创建一个对象?
- 创建一个Bean
在这里我创建了一个User类
public class User {
public void add() {
System.out.println("add");
}
}
- 创建Spring配置文件
使用bean标签 其中有两个属性
id 表示该对象的id值
class 表示该对象类所在的位置
<?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">
<!--配置User类的创建-->
<bean id="user" class="com.chenxiii.spring.User"></bean>
</beans>
- 创建对象
package com.chenxiii.spring;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestUser {
@Test
public void testAdd() {
// 1.加载Spring的配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
// 2.获取配置文件创建的对象
User user = context.getBean("user", User.class);
System.out.println(user);
// 3.调用对象的方法
user.add();
}
}
IOC容器
IOC容器介绍
-
什么是IOC(控制反转)
- 把对象创建和对象之间的调用过程,交给Spring进行管理
- 使用IOC目的:为了降低耦合度
-
IOC底层
- xml解析、工厂模式、反射
-
Spring提供的IOC容器实现的两种方式(两个接口)
BeanFactory接口:IOC容器基本实现是Spring内部接口的使用接口,不提供给开发人员进行使用(加载配置文件时候不会创建对象,在获取对象时才会创建对象。)ApplicationContext接口:BeanFactory接口的子接口,提供更多更强大的功能,提供给开发人员使用(加载配置文件时候就会把在配置文件对象进行创建)推荐使用!
-
ApplicationContext接口的实现类
- new ClassPathXmlApplicationContext(“bean1.xml”); 内部填写src目录下的路径
- new FileSystemXmlApplicationContext(); 内部填写盘符下的路径
为什么要使用IOC?
首先我们考虑对象之间相互引用的原始方式
如 我们现在有一个UserService类 需要在其中调用UserDao类中的方法
class UserService {
public void add() {
UserDao dao = new UserDao();
dao.add();
}
}
class UserDao {
public void add() {
System.out.println("add....");
}
}
可以看出此时两个类之间的耦合度是非常高的
如果我们将UserDao类的包移动了位置 那么UserService内引用UserDao就需要重新引入新的包路径 如果有其它的类也引用了UserDao 那么它们也需要修改引入的路径
其次是工厂模式
我们可以通过工厂类来获取UserDao的类对象
class UserFactorty {
public static UserDao getDao() {
return new UserDao();
}
}
class UserService {
public void add() {
UserDao dao = UserFactorty.getDao();
dao.add();
}
}
此时我们可以看出UserDao类和UserService类之间的耦合度减少了 只需要修改工厂类的包路径即可
采用IOC的方式再次降低耦合度
首先我们会在xml文件中配置创建的对象
<!--配置User类的创建-->
<bean id="user" class="com.chenxiii.spring.User"></bean>
其次在通过工厂类加载
class UserFactorty {
public static UserDao getDao() {
String className = class属性值; //1. 通过xml解析获取class路径
Class cls = Class.forName(className); // 2.通过反射创建对象
return (UserDao)cls.newInstance();
}
}
此时 我们只需要修改xml中class的属性即可修改Dao的路径
Bean管理
- 创建对象
- 注入属性
基于XML方式
创建对象
- 在spring配置文件中 使用bean标签 标签里添加相应的属性 就可以实现对象创建
- 在bean标签内有很多属性
- id属性:唯一标识
- class:类全路径
- 创建对象的时候 默认执行无参构造方法
注入属性
DI:依赖注入,就是注入属性。DI是IOC中的一种实现,需要在创建对象的基础上实现
-
使用set方法进行注入
-
创建对象类
public class Book { //创建属性 private String name; private int price; //创建对应的set方法 public void setName(String name) { this.name = name; } public void setPrice(int price) { this.price = price; } } -
配置xml文件
<bean id="book" class="com.chenxiii.spring.Book"> <!--property注入属性 name:属性名称 value:属性值 --> <property name="name" value="快看漫画"></property> <property name="price" value="1"></property> </bean> -
查看注入属性
// 1.加载Spring的配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); // 2.获取配置文件创建的对象 Book book = context.getBean("book", Book.class); System.out.println(book); System.out.println(book.getName() + ", " + book.getPrice());//为了查看 在类内添加了get方法
-
-
使用有参构造函数注入
-
创建类
public class Order { private String name; private String address; //有参构造 public Order(String name, String address) { this.name = name; this.address = address; } } -
配置xml文件
<bean name="order" class="com.chenxiii.spring.Order"> <!--constructor-arg 有参构造注入属性 name:属性名称 value:属性值 --> <constructor-arg name="name" value="订单1"></constructor-arg> <constructor-arg name="address" value="hdu"></constructor-arg> </bean>也可以通过索引的方式传参
<constructor-arg index="0" value="订单2"></constructor-arg> <constructor-arg index="1" value="hdu"></constructor-arg> -
查看注入属性
// 1.加载Spring的配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); // 2.获取配置文件创建的对象 Order order = context.getBean("order", Order.class); System.out.println(order); System.out.println(order.getName() + ", " + order.getAddress());//为了查看 在类内添加了get方法
-
-
p名称空间注入(了解)
-
添加p名称空间
在xml文件开头加入
xmlns:p后面内容复制xmlns后修改最后一项为p<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"> -
配置对象
<bean id="book" class="com.chenxiii.spring.Book" p:name="哔哩哔哩" p:price="2"></bean>
-
注入空值和特殊符号
在Book类中新增属性address,接下来为它设置一个空值
private String address;
修改xml文件 为Book对象的address传入null
<bean id="book" class="com.chenxiii.spring.Book">
<!--property注入属性
name:属性名称
value:属性值
-->
<property name="name" value="快看漫画"></property>
<property name="price" value="1"></property>
<property name="address">
<null></null>
</property>
</bean>
如果传入的值包含特殊符号怎么办?如<>,会被解析为标签
<!--使用转义字符-->
<property name="address" value="<hdu>"></property>
<!--使用CDATE-->
<property name="address">
<value><![CDATA[hdu]]></value>
</property>
注入外部bean
首先创建Service类和Dao类 我们会在在Service类中调用Dao的方法
public class UserService {
//创建userDao属性
UserDao userDao;
//设置set方法
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void add() {
System.out.println("service add ...");
userDao.update();
}
}
public interface UserDao {
public void update();
}
public class UserDaoImpl implements UserDao{
@Override
public void update() {
System.out.println("userDao update...");
}
}
在xml文件中注入userDao
<!--1. service和dao对象创建-->
<bean id="userService" class="com.chenxiii.service.UserService">
<!--2. 注入userDao
name:类里面的属性名称
ref:创建UserDao对象bean标签的id值
-->
<property name="userDao" ref="userDaoImpl"></property>
</bean>
<bean id="userDaoImpl" class="com.chenxiii.dao.UserDaoImpl"></bean>
最后进行测试
public class TestService {
@Test
public void testService() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
}
注入内部bean
创建Dept类和Employee类
public class Dept {
private String name;
public void setName(String name) {
this.name = name;
}
}
public class Employee {
private String name;
private String gender;
//员工属于某个部门 用对象的形式表示
private Dept dept;
public void setDept(Dept dept) {
this.dept = dept;
}
public void setName(String name) {
this.name = name;
}
public void setGender(String gender) {
this.gender = gender;
}
}
在xml文件中配置内部bean
<!--内部bean-->
<bean id="emp" class="com.chenxiii.bean.Employee">
<!--设置普通属性-->
<property name="name" value="lucy"></property>
<property name="gender" value="女"></property>
<!--设置对象类型属性-->
<property name="dept">
<bean id="dept" class="com.chenxiii.bean.Dept">
<property name="name" value="安保部"></property>
</bean>
</property>
</bean>
级联赋值
写法一:
<bean id="emp" class="com.chenxiii.bean.Employee">
<!--设置普通属性-->
<property name="name" value="lucy"></property>
<property name="gender" value="女"></property>
<!--级联赋值-->
<property name="dept" ref="dept"></property>
</bean>
<bean id="dept" class="com.chenxiii.bean.Dept">
<property name="name" value="财务部"></property>
</bean>
写法二:
<!--内部bean-->
<bean id="emp" class="com.chenxiii.bean.Employee">
<!--设置普通属性-->
<property name="name" value="lucy"></property>
<property name="gender" value="女"></property>
<!--级联赋值-->
<property name="dept" ref="dept"></property>
<property name="dept.name" value="技术部"></property>
</bean>
<bean name="dept" class="com.chenxiii.bean.Dept"></bean>
写法二需要注意的点:
-
调用
dept.name的时候一定要先有<property name="dept" ref="dept"></property> -
调用
dept.name之前一定要在emp类中设置dept的get方法//员工属于某个部门 用对象的形式表示 private Dept dept; public Dept getDept() { return dept; } public void setDept(Dept dept) { this.dept = dept; }
注入集合属性
- 注入字面量
首先我们创建一个Student类 在内部定义数组、List、Map、Set属性并分为创建set方法
public class Student {
// 1.数组类型属性
private String[] courses;
// 2.list集合属性
private List<String> list;
// 3.map集合属性
private Map<String, String> map;
// 4.set集合属性
private Set<String> set;
public void setSet(Set<String> set) {
this.set = set;
}
public void setList(List<String> list) {
this.list = list;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public void setCourses(String[] courses) {
this.courses = courses;
}
public void show() {
System.out.println(Arrays.toString(courses));
System.out.println(list);
System.out.println(map);
System.out.println(set);
}
}
在xml中配置
<bean id="stu" class="com.chenxiii.collection.Student">
<!--数组类型的属性注入-->
<property name="courses">
<array>
<value>java</value>
<value>c</value>
<value>c++</value>
</array>
</property>
<!--list集合属性注入-->
<property name="list">
<list>
<value>python</value>
<value>c#</value>
<value>go</value>
</list>
</property>
<!--map类型属性注入-->
<property name="map">
<map>
<entry key="HTML" value="html"></entry>
<entry key="CSS" value="css"></entry>
</map>
</property>
<!--set类型属性注入-->
<property name="set">
<set>
<value>Mysql</value>
<value>Redis</value>
</set>
</property>
</bean>
最后进行测试
public class TestStudent {
@Test
public void testStudent() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean5.xml");
Student stu = context.getBean("stu", Student.class);
stu.show();
}
}
在集合中设置对象类型值
例如学生会有多门课程
我们为课程创建一个Course类
public class Course {
private String cname;
public void setCname(String cname) {
this.cname = cname;
}
}
并在Student类中添加Course集合
//学生所学多门课程
private List<Course> courseList;
public void setCourseList(List<Course> courseList) {
this.courseList = courseList;
}
接下来在xml文件中对集合进行配置
<!--这里只列出了Cousrse集合的注入-->
<!--注入list集合类型 值是对象-->
<property name="courseList">
<list>
<!--在bean中填写多个创建好的对象-->
<ref bean="course1"></ref>
<ref bean="course2"></ref>
</list>
</property>
<bean id="course1" class="com.chenxiii.collection.Course"><property name="cname" value="Java"></property> </bean>
<bean id="course2" class="com.chenxiii.collection.Course"><property name="cname" value="JS"></property> </bean>
把集合注入部分提取出来
为什么要将集合注入部分提取出来?
如果我们有多个对象 需要注入集合的内容一致 那么按照以往的方式需要在每个对象中都注入一次集合 这样很费力 因此将其提取出来
在Spring配置文件中引入util名称空间
<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">
<!--将xmlns复制一遍 在xmlns后面加上:util 在路径后面将beans修改为util-->
<!--将xsi:schemaLocation复制一遍 将其中所有的beans修改为util-->
为多个对象注入集合
<!--提取list集合属性注入-->
<!--如果注入的是对象 则和之前一样将value替换为ref即可-->
<util:list id="bookList">
<value>一本好书</value>
<value>一本武功秘籍</value>
<value>一本不好看的书</value>
</util:list>
<!--将提取的list集合注入-->
<!--和之前注入集合的方式不一样 这里通过ref注入集合-->
<bean id="book" class="com.chenxiii.collection.Book">
<property name="list" ref="bookList"></property>
</bean>
<!--将提取的list集合注入-->
<bean id="book2" class="com.chenxiii.collection.Book">
<property name="list" ref="bookList"></property>
</bean>
工厂bean
Spring有两种类型的bean 一种是普通bean 另一种是工厂bean(FactoryBean)
普通bean:在配置文件中定义的类型就是返回类型
工厂bean:在配置文件中定义的bean类型可以和返回类型不一样
创建类 让这个类作为工厂bean 实现接口FactoryBean
public class MyBean implements FactoryBean<Course> {
}
实现接口方法 在实现的方法中定义返回的bean类型
@Override
public Course getObject() throws Exception {
//这里我们定义一个course对象 实际上一般是使用工厂模式
Course course = new Course();
course.setCname("abc");
return course;
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
return FactoryBean.super.isSingleton();
}
在xml中配置MyBean
<bean id="myBean" class="com.chenxiii.factorybean.MyBean"></bean>
测试
@Test
public void testMyBean() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean7.xml");
Course course = context.getBean("myBean", Course.class);
System.out.println(course);
}
可以看出我们在xml中配置的类是MyBean类 但是我们在测试方法中返回的是Course类
bean作用域
在Spring里面 设置创建bean实例是单实例还是多实例 默认情况下是单实例对象
我们先看看默认的情况下 以之前的代码为例
我们将返回的对象分别用stu和stu2指向 输出stu和stu2 可以发现两个地址是一样的 说明返回的是一个对象
@Test
public void testStudent() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean5.xml");
Student stu = context.getBean("stu", Student.class);
Student stu2 = context.getBean("stu", Student.class);
//stu.show();
System.out.println(stu);
System.out.println(stu2);
}
通过设置bean标签的scope属性来设置对象为单实例或多实例[singleton: 单实例; prototype: 多实例]
<bean id="stu" class="com.chenxiii.collection.Student" scope="prototype">
...
</bean>
设置完多实例后 重新执行测试代码 可以发现两个对象的地址是不一样的 此时返回的是不同的对象
注:singleton和prototype的区别:
singleton: 单实例;prototype: 多实例- 设置
singleton时 加载Spring配置文件的时候就会创建单实例对象 - 设置
prototype时 不是在加载配置文件的时候创建对象 而是在调用getBean()方法的时候创建多实例对象
bean的生命周期
- 通过构造器创建bean实例(无参数构造)
- 为bean的属性设置值和对其它bean的引用(调用set方法)
- 调用bean的初始化方法(需要进行配置初始化的方法)
- bean可以使用(对象已经获取到了)
- 当容器关闭的时候 调用bean的销毁方法(需要进行配置销毁的方法)
我们通过一个bean实例来看生命周期
public class Orders {
private String oname;
public Orders() {
System.out.println("1. 无参构造函数调用");
}
public void setOname(String oname) {
System.out.println("2. 调用了set方法");
this.oname = oname;
}
//创建执行初始化的方法
public void initMethod() {
System.out.println("3. 执行初始化的方法");
}
//创建执行销毁的方法
public void desMethod() {
System.out.println("5. 执行销毁的方法");
}
}
在xml中配置bean实例
<bean id="orders" class="com.chenxiii.life.Orders" init-method="initMethod" destroy-method="desMethod">
<property name="oname" value="手机"></property>
</bean>
最后进行测试
@Test
public void testLife() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean8.xml");
Orders orders = context.getBean("orders", Orders.class);
System.out.println("4. 创建bean对象" + orders);
//手动让bean实例销毁
((ClassPathXmlApplicationContext) context).close();
}
最终结果如上所描述
如果加入bean的后置处理器 则共有七步
- 通过构造器创建bean实例(无参数构造)
- 为bean的属性设置值和对其它bean的引用(调用set方法)
把bean实例传递给bean后置处理器的方法 postProcessBeforeInitialization- 调用bean的初始化方法(需要进行配置初始化的方法)
把bean实例传递给bean后置处理器的方法 postProcessAfterInitialization- bean可以使用(对象已经获取到了)
- 当容器关闭的时候 调用bean的销毁方法(需要进行配置销毁的方法)
创建后置处理器
创建一个类 让它实现BeanPostProcessor接口
public class Processor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("后置处理器的before方法");
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("后置处理器的after方法");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
配置后置处理器
<!--配置后置处理器-->
<!--会给该配置文件中所有对象都配置后置处理器-->
<bean id="processor" class="com.chenxiii.life.Processor"></bean>
此时再次测试可以得

xml自动装配
什么是自动装配?
根据指定装配规则(属性名称或者属性类型) Spring自动将匹配的属性值进行注入
我们创建两个类 Dept 和 Emp
public class Emp {
private Dept dept;
public void setDept(Dept dept) {
this.dept = dept;
}
@Override
public String toString() {
return "Emp{" +
"dept=" + dept +
'}';
}
public void show() {
System.out.println(dept);
}
}
public class Dept {
@Override
public String toString() {
return "Dept{}";
}
}
在配置文件中实现自动注入
<!--实现自动装配
bean标签属性autowire 配置自动装配
autowire属性常用两个值:
byName根据属性名称注入 注入值bean的id值和类属性名称一样
即bean的id值(dept)要和emp中定义的属性名一样(Dept dept)
byType根据属性类型注入
-->
<bean id="emp" class="com.chenxiii.autowire.Emp" autowire="byType">
<!--<property name="dept" ref="dept"></property>-->
</bean>
<bean id="dept" class="com.chenxiii.autowire.Dept"></bean>
<!--如果是byType 我们在这里在加一个则会报错 因为有两个对象符合条件-->
<!-- <bean id="dept2" class="com.chenxiii.autowire.Dept"></bean>-->
外部属性文件
直接配置数据库连接池
<!--直接配置连接池-->
<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/test"></property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
</bean>
如何使用外部属性文件来配置数据库连接池呢?
首先创建properties文件
url=jdbc:mysql://localhost:3306/test
username=root
password=123456
driverClassName=com.mysql.cj.jdbc.Driver
把外部properties属性文件引入到Spring配置文件中 在配置文件中引入context名称空间
<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">
在Spring配置文件中使用标签引入外部属性文件
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!--配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driverClassName}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${username}"></property>
<property name="password" value="${password}"></property>
</bean>
基于注解方式
什么是注解?
-
注解是代码特殊标记,格式:@注解名称(属性名称=属性值, 属性名称=属性值…)
-
使用注解,注解作用在类上面,方法上面,属性上面
-
使用注解目的:简化 xml 配置
Spring 针对 Bean 管理中创建对象提供注解
下面四个注解功能是一样的,都可以用来创建 bean 实例
- @Component 普通注解
- @Service 一般用在业务逻辑层或者Service层
- @Controller 一般用在Web层
- @Repository 一般用在Dao层或持久层
创建对象
首先引入依赖 该jar包也是在Spring的lib目录下

其次 开启组件扫描 需要引入context命名空间
<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">
<!--开启组件扫描-->
<!--需要扫描多个包 写法1:加逗号-->
<context:component-scan base-package="com.chenxiii.dao, com.chenxiii.service"></context:component-scan>
<!--写法2 写法2:写上层目录-->
<context:component-scan base-package="com.chenxiii"></context:component-scan>
</beans>
创建类 在类上面添加创建对象注解
//在注解里面value属性值可以省略不写 默认值是首字母小写的类名称
@Component(value = "user") //<bean id="" class=""> id等价于value
public class User {
public void show() {
System.out.println("show..");
}
}
最后进行测试
@Test
public void testAnnotation() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean11.xml");
User user = context.getBean("user", User.class);
System.out.println(user); //com.chenxiii.annotation.User@5c1a8622
user.show();//show..
}
组件扫描配置
看懂示例即可
<!--开启组件扫描-->
<!--需要扫描多个包 写法1:加逗号-->
<context:component-scan base-package="com.chenxiii.dao, com.chenxiii.service"></context:component-scan>
<!--写法2 写法2:写上层目录-->
<context:component-scan base-package="com.chenxiii"></context:component-scan>
<!--示例一
use-default-filters="false" 表示不使用默认的filter,自行配置filter
context:include-filter 设置扫描哪些内容
在com.chenxiii包下只扫描带Controller注解的类
-->
<context:component-scan base-package="com.chenxiii" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--示例二
下面的配置扫描包下的所有内容
context:exclude-filter 设置哪些内容不扫描
-->
<context:component-scan base-package="com.chenxiii">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
基于注解的方式实现属性注入
-
@AutoWired:根据属性类型进行自动装配把service和dao对象创建 在service和dao类添加创建对象注解
@Repository public class UserDaoImpl implements UserDao{ @Override public void update() { System.out.println("userDao update..."); } } @Component public class UserService { public void add() { System.out.println("service add ..."); } }在service注入dao对象 在service类添加dao属性 在属性上使用注解
@Component public class UserService { //创建userDao属性 //不需要添加set方法 @Autowired private UserDao userDao; public void add() { System.out.println("service add ..."); userDao.update(); } } -
@Qualifier:根据属性名称进行注入@Qualifier注解的使用要和@AutoWired一起使用需要先在userDao类设定名称
@Repository(value = "daoImp") public class UserDaoImpl implements UserDao{ @Override public void update() { System.out.println("userDao update..."); } }注入
//创建userDao属性 //不需要添加set方法 @Autowired @Qualifier(value = "daoImp") private UserDao userDao; -
@Resource:可以根据类型注入也可以根据名称注入需要注意的时
import javax.annotation.Resource;它是javax包下的而不是Spring的//@Resource //根据类型进行注入 @Resource(name = "daoImp") //根据名称注入 private UserDao userDao; -
@Value:注入普通类型属性@Value(value = "abc") private String name;
完全注解开发
创建配置类 替代xml配置文件
@Configuration //把当前类作为配置类 替代xml配置文件
@ComponentScan(basePackages = {"com.chenxiii"}) //组件扫描
public class SpringConfig {
}
测试类修改
@Test
public void testConfig() {
//加载配置类
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService service = context.getBean("userService", UserService.class);
System.out.println(service);
service.add();
}
AOP
什么是AOP?
面向切面编程 面向方面编程
可以对业务的各个逻辑部分进行隔离 从而使得业务逻辑各部分之间的耦合度降低 提高程序的可重用性 同时提高了开发的效率
通俗的描述:不通过修改源代码的方式 在主干功能里添加新内容
底层原理
AOP底层使用动态代理
有两种情况的动态代理 1)有接口的情况 使用JDK的动态代理 2)没有接口的情况 使用CGLIB的动态代理
-
有接口的情况
创建接口实现类的代理对象 增强类的方法

-
没有接口的情况下
创建子类的代理对象 增强类的方法

-
使用JDK动态代理 使用Proxy类里面的方法创建代理对象
/* newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h) ClassLoader loader:类加载器 类<?>[] interfaces:增强方法所在类的实现接口 支持多个接口 InvocationHandler h:实现这个接口InvocationHandler 创建代理对象 写增强的部分 返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序 */ -
编写JDK动态代理代码
-
创建接口 定义方法
public interface UserDao { public int add(int a, int b); public String update(String id); } -
创建接口实现类 实现方法
public class UserDaoImpl implements UserDao{ @Override public int add(int a, int b) { return a + b; } @Override public String update(String id) { return id; } } -
使用Proxy类创建接口代理对象
public class JDKProxy { public static void main(String[] args) { //创建接口实现代理对象 Class[] interfaces = {UserDao.class}; //Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() { // @Override // public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // return null; // } //}); UserDaoImpl userDao = new UserDaoImpl(); UserDao dao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao)); int add = dao.add(1, 2); System.out.println(add); } } class UserDaoProxy implements InvocationHandler { //1.把创建的是谁的代理对象 把谁传递过来 这里是UserDaoImpl的代理对象 //有参构造传递 private Object obj; public UserDaoProxy(Object obj) { this.obj = obj; } //2.增强的逻辑部分 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //方法之前 System.out.println("方法之前执行.." + method.getName() + "传递的参数.." + Arrays.toString(args)); //被增强的方法执行 //若只需要增强add方法 则通过method.getName进行判断 Object res = method.invoke(obj, args); //方法之后 System.out.println("方法之后执行.." + obj); return res; } }
-
一些术语和准备工作
class User{
add(){};
update(){};
select(){增强};
delete(){增强};
}
连接点:类里面哪些方法可以被增强 那么这些方法称为连接点 [ add update select delete ]
切入点:实际被真正增强的方法被称为切入点 [ select delete ]
通知(增强):实际增强的逻辑部分称为通知 [ select中的增强 delete中的增强 ]
通知有多种类型
- 前置通知
- 后置通知
- 环绕通知
- 异常通知
- 最终通知
切面:把通知应用到切入点的过程
AOP操作
Spring框架中一般都是基于AspectJ实现AOP操作
什么是AspectJ?AspectJ不是Spring组成部分 是独立的AOP框架 一般把AspectJ和Spring框架一起使用 进行AOP操作
基于AspectJ实现AOP操作: 1)基于xml文件 2)基于注解
引入相关依赖

切入点的表达式
作用:知道对哪个类里面的哪个方法进行增强
语法结构:execution([权限修饰符][返回类型][类全路径][方法名称]([参数列表]))
举例:
对com.chenxiii.dao.BookDao类里面的add方法进行增强
execution(* com.chenxiii.dao.BookDao.add(…))
*表示任意修饰符
返回类型省略
…表示方法中的参数
对com.chenxiii.dao.BookDao类里面的所有方法进行增强
execution(* com.chenxiii.dao.BookDao.*(…))
对com.chenxiii.dao包里面所有类里面的所有方法进行增强
execution(* com.chenxiii.dao.*.*(…))
AspectJ
基于注解方式
创建类 在类里面实现方法
public class User {
public void add() {
System.out.println("add...");
}
}
创建增强类 编写增强逻辑
在增强类里面创建方法 让不同的方法代表不同的通知类型
public class UserProxy {
//前置通知
public void before() {
System.out.println("before...");
}
}
进行通知的配置 在Spring的配置文件中 开启注解扫描
<?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: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">
<!--1.新增命名空间context aop-->
<!--2.开启注解扫描-->
<context:component-scan base-package="com.chenxiii.aop.ano"></context:component-scan>
</beans>
使用注解创建User和UserProxy对象
@Component
public class User {
public void add() {
System.out.println("add...");
}
}
@Component
public class UserProxy {
//前置通知
public void before() {
System.out.println("before...");
}
}
在增强类上面添加一个注解@Aspect
@Component
@Aspect //生成代理对象
public class UserProxy {
//前置通知
public void before() {
System.out.println("before...");
}
}
在Spring配置文件中开启生成代理对象
<!--1.新增命名空间context aop-->
<!--2.开启注解扫描-->
<context:component-scan base-package="com.chenxiii.aop.ano"></context:component-scan>
<!--3.开启Aspect生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
配置不同类型的通知 在增强类的里面 在作为通知方法上面添加通知类型注解 使用切入点表达式配置
//前置通知
//Before注解表示作为前置通知 在增强方法的前面执行
@Before(value = "execution(* com.chenxiii.aop.ano.User.add(..))")
public void before() {
System.out.println("before...");
}
//最终通知 在方法之后执行
@After(value = "execution(* com.chenxiii.aop.ano.User.add(..))")
public void after() {
System.out.println("after...");
}
//后置通知 在方法返回结果之后执行 有异常就不执行
@AfterReturning(value = "execution(* com.chenxiii.aop.ano.User.add(..))")
public void afterReturning() {
System.out.println("afterReturning...");
}
//异常通知 在有异常的时候执行
@AfterThrowing(value = "execution(* com.chenxiii.aop.ano.User.add(..))")
public void afterThrowing() {
System.out.println("afterThrowing...");
}
//环绕通知 在方法之前和之后都执行
@Around(value = "execution(* com.chenxiii.aop.ano.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("around之前...");
proceedingJoinPoint.proceed();
System.out.println("around之后...");
}
最后进行测试
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean01.xml");
User user = context.getBean("user", User.class);
user.add();
}
around之前...
before...
add...
around之后...
after...
afterReturning...
相同的切入点进行抽取
//相同切入点的抽取
@Pointcut(value = "execution(* com.chenxiii.aop.ano.User.add(..))")
public void point() {
}
//前置通知
//Before注解表示作为前置通知 在增强方法的前面执行
@Before(value = "point()")
public void before() {
System.out.println("before...");
}
有多个增强类对同一个方法进行增强 设置优先级
@Component
@Aspect
public class PersonProxy {
//前置通知
//Before注解表示作为前置通知 在增强方法的前面执行
@Before(value = "execution(* com.chenxiii.aop.ano.User.add(..))")
public void before() {
System.out.println("person before...");
}
}
在增强类上面添加注解@order(数字类型值) 数字值越小优先级越高
@Component
@Aspect
@Order(1)
public class PersonProxy {
//前置通知
//Before注解表示作为前置通知 在增强方法的前面执行
@Before(value = "execution(* com.chenxiii.aop.ano.User.add(..))")
public void before() {
System.out.println("person before...");
}
}
@Component
@Aspect //生成代理对象
@Order(3)
public class UserProxy {
...
}
基于配置文件方式
创建增强类和被增强类 创建方法
public class Book {
public void buy() {
System.out.println("buy...");
}
}
public class BookProxy {
public void before() {
System.out.println("before...");
}
}
在Spring配置文件中创建对象
<!--创建对象-->
<bean id="book" class="com.chenxiii.aop.xml.Book"></bean>
<bean id="proxy" class="com.chenxiii.aop.xml.BookProxy"></bean>
在Spring配置文件中配置切入点
<?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: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">
<!--创建对象-->
<bean id="book" class="com.chenxiii.aop.xml.Book"></bean>
<bean id="proxy" class="com.chenxiii.aop.xml.BookProxy"></bean>
<!--配置AOP增强-->
<aop:config>
<!--切入点-->
<aop:pointcut id="p" expression="execution(* com.chenxiii.aop.xml.Book.buy(..))"/>
<!--配置切面-->
<aop:aspect ref="proxy">
<!--增强作用在具体的方法上-->
<aop:before method="before" pointcut-ref="p"></aop:before>
</aop:aspect>
</aop:config>
</beans>
完全注解开发
@Configuration
@ComponentScan(basePackages = {"com.chenxiii.aop"})
@EnableAspectJAutoProxy(proxyTargetClass = true) //开启生成代理模式
public class ConfigAop {
}
JDBCTemplate
什么是JDBCTemplate? 是Spring框架对JDBC进行封装,使用JDBCTemplate方便实现对数据库的操作
准备工作
引入相关依赖

在Spring的配置文件中配置数据库连接池
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="jdbc:mysql://localhost:3306/test"></property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
</bean>
配置JDBCTemplate对象 注入DataSource
<!--JDBCTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入DataSource-->
<property name="dataSource" ref="dataSource"></property>
</bean>
开启组件扫描
<!--开启组件扫描-->
<context:component-scan base-package="com.chenxiii.jdbc"></context:component-scan>
创建service类和dao类 在dao类中注入jdbcTemplate对象
@Repository
public class BookDaoImpl implements BookDao{
//注入JDBCTemplate
@Autowired
private JdbcTemplate jdbcTemplate;
}
@Service
public class BookService {
//注入dao
@Autowired
private BookDao bookDao;
}
操作数据库
增删改
对应数据库表建立实体类
public class User {
private int id;
private String name;
private String password;
private String address;
.....get set方法
}
在dao进行数据库添加操作
@Repository
public class UserDaoImpl implements UserDao {
//注入JDBCTemplate
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void add(User user) {
String sql = "insert into user(id, name, password) values(?,?,?)";
int update = jdbcTemplate.update(sql, user.getId(), user.getName(), user.getPassword());
System.out.println(update);
}
}
@Service
public class UserService {
//注入dao
@Autowired
private UserDao userdao;
//添加
public void add(User user) {
userdao.add(user);
}
}
进行测试
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean03.xml");
UserService userService = context.getBean("userService", UserService.class);
User user = new User();
user.setId(999);
user.setName("成龙");
user.setPassword("ChinaChen");
userService.add(user);
}
同理修改和删除
@Override
public void update(User user) {
String sql = "update user set name=?,password=? where id=?";
int update = jdbcTemplate.update(sql, user.getName(), user.getPassword(), user.getId());
System.out.println(update);
}
@Override
public void delete(int id) {
String sql = "delete from user where id=?";
int update = jdbcTemplate.update(sql, id);
System.out.println(update);
}
查询某个值
查询user表中有多少条数据
@Override
public int selectCount() {
String sql = "select count(*) from user";
Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
return count;
}
查询返回对象
查询某个对象
@Override
public User findUserInfo(int id) {
String sql = "select * from user where id=?";
User user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), id);
return user;
}
查询返回多个对象
@Override
public List<User> findAllUser() {
String sql = "select * from user";
List<User> users = jdbcTemplate.query(sql, new BeanPropertyRowMapper<User>(User.class));
return users;
}
批量操作
批量添加
@Override
public void batchAdd(List<Object[]> users) {
String sql = "insert into user(id, name, password) values(?,?,?)";
int[] ints = jdbcTemplate.batchUpdate(sql, users);
System.out.println(Arrays.toString(ints));
}
@Test
public void batchAdd() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean03.xml");
UserService userService = context.getBean("userService", UserService.class);
List<Object[]> bathArgs = new ArrayList<>();
Object[] u1 = {11, "Tom", "qqq"};
Object[] u2 = {22, "Jerry", "bbb"};
Object[] u3 = {33, "Mary", "rrr"};
bathArgs.add(u1);
bathArgs.add(u2);
bathArgs.add(u3);
userService.batchAdd(bathArgs);
}
批量修改
@Override
public void batchUpdate(List<Object[]> users) {
String sql = "update user set name=?,password=? where id=?";
int[] ints = jdbcTemplate.batchUpdate(sql, users);
System.out.println(Arrays.toString(ints));
}
@Test
public void batchUpdate() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean03.xml");
UserService userService = context.getBean("userService", UserService.class);
List<Object[]> bathArgs = new ArrayList<>();
Object[] u1 = {"tom", "aaa", 11};
Object[] u2 = {"jerry", "ddd", 22};
Object[] u3 = {"mary", "eee", 33};
bathArgs.add(u1);
bathArgs.add(u2);
bathArgs.add(u3);
userService.batchUpdate(bathArgs);
}
批量删除
@Override
public void batchDelete(List<Object[]> ids) {
String sql = "delete from user where id=?";
int[] ints = jdbcTemplate.batchUpdate(sql, ids);
System.out.println(Arrays.toString(ints));
}
@Test
public void batchDelete() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean03.xml");
UserService userService = context.getBean("userService", UserService.class);
List<Object[]> bathArgs = new ArrayList<>();
Object[] u1 = {11};
Object[] u2 = {22};
Object[] u3 = {33};
bathArgs.add(u1);
bathArgs.add(u2);
bathArgs.add(u3);
userService.batchDelete(bathArgs);
}
事务操作
什么是事务?事务是数据库操作最基本单元 逻辑上的一组操作 所有操作要么都成功 要么都失败
事务的四个特性(ACID): 原子性 一致性 隔离性 持久性
搭建事务操作环境
配置xml配置文件 注入DataSource和JdbcTemplate
<!--开启组件扫描-->
<context:component-scan base-package="com.chenxiii.transaction"></context:component-scan>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="jdbc:mysql://localhost:3306/test"></property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
</bean>
<!--JDBCTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入DataSource-->
<property name="dataSource" ref="dataSource"></property>
</bean>
搭建Service层
@Service
public class UserService {
//注入dao
@Autowired
private UserDao userDao;
}
搭建Dao层
public interface UserDao {
}
@Repository
public class UserDaoImpl implements UserDao{
@Autowired
private JdbcTemplate jdbcTemplate;
}
在dao层创建两个方法 用于多钱和少钱
@Repository
public class UserDaoImpl implements UserDao{
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void addMoney() {
//AA转账100给CC CC要少100
String sql = "update user_table set balance=balance+? where user=?";
jdbcTemplate.update(sql, 100, "CC");
}
@Override
public void reduceMoney() {
//AA转账100给CC AA要少100
String sql = "update user_table set balance=balance-? where user=?";
jdbcTemplate.update(sql, 100, "AA");
}
}
在service层中创建转钱方法
@Service
public class UserService {
//注入dao
@Autowired
private UserDao userDao;
//转账的操作
public void accountMoney() {
userDao.reduceMoney(); //少钱
userDao.addMoney(); //多钱
}
}
基于注解方式加入事务
使用事务解决转账异常问题 逻辑如下
//转账的操作
public void accountMoney() {
try {
// 开启事务
// 进行业务操作
userDao.reduceMoney(); //少钱
userDao.addMoney(); //多钱
// 发生异常 进行事务提交
}catch (Exception e) {
// 出现异常 进行事务回滚
}
}
在Spring进行事务操作有两种方式:编程式事务管理和声明式事务管理
基于声明式事务管理有两种方式:基于xml配置文件和基于注解方式
在Spring进行声明式事务管理 底层使用AOP
Spring事务管理API:
提供了一个接口 代表事务管理器 这个接口针对不同的框架提供了不同的实现类

在Spring配置文件中配置事务管理器
<!--创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
在Spring配置文件中 开启事务注解
需要引入名称空间tx
<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"
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/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
在service类上面或者service类的方法上添加事务注解@Transactional
如果把注解添加到类上面 则这个类里面的所有方法都添加事务
如果把注解添加到方法上 则只给这个方法添加事务
@Service
@Transactional
public class UserService {
//注入dao
@Autowired
private UserDao userDao;
//转账的操作
public void accountMoney() {
// 进行业务操作
userDao.reduceMoney();
int i = 1 / 0; //异常
userDao.addMoney();
}
}
此时进行事务操作 发现异常后会自动执行回滚
事务参数

-
propagetion:事务传播行为多事务方法直接进行调用 这个过程中事务是如何进行管理的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EaBLOZcL-1640595903445)(C:/Users/81288/AppData/Roaming/Typora/typora-user-images/image-20211224164315318.png)]

@Transactional(propagation = Propagation.REQUIRED) -
isolation:事务隔离级别脏读 不可重复读 虚读(幻读)
脏读:在多个事务之间 一个未提交的事务读取到了另一个未提交事务的数据
不可重复读:一个未提交的事务读取到另一个提交事务修改的数据
虚读:一个未提交的事务读取到另一提交事务的添加数据

@Transactional(isolation = Isolation.DEFAULT) -
timeout:超时时间事务需要在一定时间内提交 如果不提交进行回滚
默认值是-1 表示不设置超时时间 设置时间是以秒为单位计算的
@Transactional(timeout = 100) -
readOnly:是否只读读:查询操作 写:添加修改操作
readOnly默认值是false 表示可以查询 也可以增加修改删除操作
设置为true时 只能做查询操作 不能修改操作
@Transactional(readOnly = true) -
rollbackFor:回滚设置出现哪些异常进行事务回滚
-
noRollbackFor:不回滚设置出现哪些异常不进行事务回滚
基于xml方式加入事务
在spring的配置文件中进行配置
配置事务管理器
<!--创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
配置通知
<!--配置通知-->
<tx:advice id="advice">
<!--配置事务的相关参数-->
<tx:attributes>
<!--指定在哪种规则的方法上添加事务-->
<tx:method name="accountMoney" propagation="REQUIRED"/>
<tx:method name="account*"/><!--以account开头的方法添加事务-->
</tx:attributes>
</tx:advice>
配置切入点和切面
<!--配置切入点和切面-->
<aop:config>
<!--切入点-->
<aop:pointcut id="pt" expression="execution(* com.chenxiii.transaction.service.UserService.*(..))"/>
<!--切面 advice-ref:通知id pointcut-ref:切入点id-->
<aop:advisor advice-ref="advice" pointcut-ref="pt"></aop:advisor>
</aop:config>
完全注解开发
@Configuration
@ComponentScan(basePackages = "com.chenxiii.transaction")
@EnableTransactionManagement //开启事务
public class TxConfig {
//创建数据库连接池
@Bean
public DruidDataSource getDruidDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
@Bean
//创建JdbcTemplate模板对象
public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
//到ioc容器中根据类型找到dataSource
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//注入DataSource
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
@Bean
//创建事务管理器
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
新功能
整合日志框架
Spring5已经移除了Log4jConfigListener 官方建议使用Log4j2
引入相关依赖

创建log4j2.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: 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>
运行之前的事务代码

Nullable注解和函数式注册对象
@Nullable注解可以使用在方法上面 属性上面 参数上面 表示方法返回可以为空 属性值可以为空 参数值可以为空
//方法的返回值可以为空
@Nullable
String getId();
//方法的参数可以为空
public <T> void registerBean(@Nullable String beanName, Class<T> beanClass, @Nullable Supplier<T> supplier, BeanDefinitionCustomizer... customizers) {
this.reader.registerBean(beanClass, beanName, supplier, customizers);
}
//属性值可以为空
@Nullable
private String[] courses;
Spring5核心容器支持函数式风格GenericApplicationContext
如果我们手动的创建一个User user = new User() 此时该对象并不会加入到Spring中 也就不能通过Spring来使用它 因此使用函数式风格将这个对象注册到Spring中
//1.创建GenericApplicationContext对象
GenericApplicationContext context = new GenericApplicationContext();
//2.调用context的方法进行注册
context.refresh();
//context.registerBean(User.class, () -> new User());
context.registerBean("user1", User.class, () -> new User());
//3.获取Spring中注册的对象
//User user = (User) context.getBean("com.chenxiii.transaction.bean.User");
User user = (User) context.getBean("user1");
System.out.println(user);
支持整合JUnit5
整合JUnit4
引入Spring中针对测试的依赖

创建测试类 使用注解方式
@RunWith(SpringJUnit4ClassRunner.class) //指定单元测试版本
@ContextConfiguration("classpath:bean03.xml") //加载配置文件
public class JTest4 {
@Autowired
private UserService userService;
@Test
public void test1() {
userService.accountMoney();
}
}
整合JUnit5
引入Junit5依赖
创建测试类
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:bean03.xml")
public class JTest5 {
@Autowired
private UserService userService;
@Test
public void test1() {
userService.accountMoney();
}
}
或者使用复合注解
@SpringJUnitConfig(locations = "classpath:bean03.xml")
public class JTest5 {
@Autowired
private UserService userService;
@Test
public void test1() {
userService.accountMoney();
}
}
SpringWebFlux
是Spring5添加的新模块 用于web开发的 功能和SpringMVC类似 Webflux使用当前一种比较流行的响应式编程出现的框架
是一种异步非阻塞的框架 异步非阻塞的框架是在Servlet3.1以后才支持的 核心是基于Reactor的相关API实现的
什么是异步非阻塞?异步和同步 阻塞和非阻塞
异步和同步针对调用者 调用者发送请求 如果等着对方回应之后才做其他事情就是同步 而发送请求不需要等对方回应就去做其他事情就是异步
阻塞和非阻塞针对被调用者 被调用者收到请求之后 做完了请求任务之后才做出反馈就是阻塞 而收到请求之后马上给出反馈然后再去做事情就是非阻塞
WebFlux特点:
- 异步非阻塞 在有限的资源下提高系统的吞吐量和伸缩性 以Reactor为基础实现响应式编程
- 函数式编程 Webflux使用Java8函数式编程方式实现路由请求
比较SpringMVC:
- 两个框架都可以使用注解方式 都运行在Tomcat等容器中
- SpringMVC采用命令式编程 WebFlux采用异步响应式编程
什么是响应式编程?
响应式编程是一种面向数据流和变化传播的编程范式 这意味着可以在编程语音中很方便的表达静态或动态的数据流 而相关的计算模型会自动将变化的值通过数据流进行传播
JAVA8中的响应式编程
提供了观察者模式的两个类Observer和Observable
public class ObserverDemo extends Observable {
public static void main(String[] args) {
ObserverDemo observer = new ObserverDemo();
//添加观察者
observer.addObserver((o,arg)->{
System.out.println("发生变化");
});
observer.addObserver((o,arg)->{
System.out.println("手动被观察者通知,准备改变");
});
observer.setChanged(); //数据变化
observer.notifyObservers(); //通知
}
}
Mono和Flux
响应式编程操作中 Reactor是满足Reactive规范框架
Reactor中有两个核心类 Mono和Flux 这两个类都实现接口Publisher 提供丰富的操作符
Flux对象实现发布者 返回N个元素
Mono对象实现发布者 返回0或1个元素
Flux和Mono都是数据流的发布者 使用Flux和Mono都可以发出三种信号:元素值、错误信号、完成信号。错误信号和完成信号都代表终止信号 终止信号用于告诉订阅者数据流结束了 错误信号终止数据流的同时把错误信息传递给订阅者
引入依赖
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.1.5.RELEASE</version>
</dependency>
测试
public static void main(String[] args) {
//just方法直接声明
Flux.just(1,2,3,4).subscribe(System.out::print);
Mono.just(1).subscribe(System.out::print);
//其他的方法
// Integer[] array = {1,2,3,4};
// Flux.fromArray(array);
//
// List<Integer> list = Arrays.asList(array);
// Flux.fromIterable(list);
//
// Stream<Integer> stream = list.stream();
// Flux.fromStream(stream);
}
调用just或者其它方法只是声明了数据流 数据流并没有发出 只有进行订阅之后才会触发数据流 不订阅则什么都不会发生
三种数据信号特点:
- 错误信号和完成信号都是终止信号 但是不能共存的
- 如果没有发送任何元素值 而是直接发送错误或完成信号 表示空数据流
- 如果没有错误信号 没有完成信号 表示是无线数据流
操作符:
- map 元素因设为新元素
- flatMap 元素映射为流
SpringWebflux执行流程和核心API
SpringWebflux基于Reactor 默认使用容器是Netty Netty是高性能NIO框架 异步非阻塞框架
SpringWebflux执行过程和SpringMVC相似 核心控制器是DispatchHandler 实现接口WebHandler
public interface WebHandler {
Mono<Void> handle(ServerWebExchange var1);
}
注解编程模型
实现接口类
@Repository
public class UserServiceImpl implements UserService {
//创建map集合存储数据
private final Map<Integer,User> users = new HashMap<>();
public UserServiceImpl() {
this.users.put(1,new User("lucy","nan",20));
this.users.put(2,new User("mary","nv",30));
this.users.put(3,new User("jack","nv",50));
}
//根据id查询
@Override
public Mono<User> getUserById(int id) {
return Mono.justOrEmpty(this.users.get(id));
}
//查询多个用户
@Override
public Flux<User> getAllUser() {
return Flux.fromIterable(this.users.values());
}
//添加用户
@Override
public Mono<Void> saveUserInfo(Mono<User> userMono) {
return userMono.doOnNext(person -> {
//向map集合里面放值
int id = users.size()+1;
users.put(id,person);
}).thenEmpty(Mono.empty());
}
}
创建Controller
@RestController
public class UserController {
//注入service
@Autowired
private UserService userService;
//id查询
@GetMapping("/user/{id}")
public Mono<User> geetUserId(@PathVariable int id) {
return userService.getUserById(id);
}
//查询所有
@GetMapping("/user")
public Flux<User> getUsers() {
return userService.getAllUser();
}
//添加
@PostMapping("/saveuser")
public Mono<Void> saveUser(@RequestBody User user) {
Mono<User> userMono = Mono.just(user);
return userService.saveUserInfo(userMono);
}
}
SpringMVC方式实现 同步阻塞方式 基于SpringMVC+Servlet+Tomcat
SpringWebFlux方式实现 异步非阻塞方式 基于SpringWebflux+Reactor+Netty
基于函数式编程模型
在使用函数式编程模型操作的时候 需要自己初始化服务器
基于函数式编程模型的时候 有两个核心接口:RouterFunction(实现路由功能 请求转发给对应的handler)和HandlerFunction(处理请求生成响应的函数)
核心任务是定义这两个函数式接口的实现并且启动需要的服务器
SpringWebFlux请求不再是ServletRequest和ServletResponse 而是ServerRequest和ServerResponse
Service的实现类和之前一致
创建Handler
public class UserHandler {
private final UserService userService;
public UserHandler(UserService userService) {
this.userService = userService;
}
//根据id查询
public Mono<ServerResponse> getUserById(ServerRequest request) {
//获取id值
int userId = Integer.valueOf(request.pathVariable("id"));
//空值处理
Mono<ServerResponse> notFound = ServerResponse.notFound().build();
//调用service方法得到数据
Mono<User> userMono = this.userService.getUserById(userId);
//把userMono进行转换返回
//使用Reactor操作符flatMap
return
userMono
.flatMap(person -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
.body(fromObject(person)))
.switchIfEmpty(notFound);
}
//查询所有
public Mono<ServerResponse> getAllUsers(ServerRequest request) {
//调用service得到结果
Flux<User> users = this.userService.getAllUser();
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(users,User.class);
}
//添加
public Mono<ServerResponse> saveUser(ServerRequest request) {
//得到user对象
Mono<User> userMono = request.bodyToMono(User.class);
return ServerResponse.ok().build(this.userService.saveUserInfo(userMono));
}
}
创建路由和服务器
public class Server {
public static void main(String[] args) throws Exception{
Server server = new Server();
server.createReactorServer();
System.out.println("enter to exit");
System.in.read();
}
//1 创建Router路由
public RouterFunction<ServerResponse> routingFunction() {
//创建hanler对象
UserService userService = new UserServiceImpl();
UserHandler handler = new UserHandler(userService);
//设置路由
return RouterFunctions.route(
GET("/users/{id}").and(accept(APPLICATION_JSON)),handler::getUserById)
.andRoute(GET("/users").and(accept(APPLICATION_JSON)),handler::getAllUsers);
}
//2 创建服务器完成适配
public void createReactorServer() {
//路由和handler适配
RouterFunction<ServerResponse> route = routingFunction();
HttpHandler httpHandler = toHttpHandler(route);
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
//创建服务器
HttpServer httpServer = HttpServer.create();
httpServer.handle(adapter).bindNow();
}
}
ingWebFlux方式实现 异步非阻塞方式 基于SpringWebflux+Reactor+Netty
基于函数式编程模型
在使用函数式编程模型操作的时候 需要自己初始化服务器
基于函数式编程模型的时候 有两个核心接口:RouterFunction(实现路由功能 请求转发给对应的handler)和HandlerFunction(处理请求生成响应的函数)
核心任务是定义这两个函数式接口的实现并且启动需要的服务器
SpringWebFlux请求不再是ServletRequest和ServletResponse 而是ServerRequest和ServerResponse
Service的实现类和之前一致
创建Handler
public class UserHandler {
private final UserService userService;
public UserHandler(UserService userService) {
this.userService = userService;
}
//根据id查询
public Mono<ServerResponse> getUserById(ServerRequest request) {
//获取id值
int userId = Integer.valueOf(request.pathVariable("id"));
//空值处理
Mono<ServerResponse> notFound = ServerResponse.notFound().build();
//调用service方法得到数据
Mono<User> userMono = this.userService.getUserById(userId);
//把userMono进行转换返回
//使用Reactor操作符flatMap
return
userMono
.flatMap(person -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
.body(fromObject(person)))
.switchIfEmpty(notFound);
}
//查询所有
public Mono<ServerResponse> getAllUsers(ServerRequest request) {
//调用service得到结果
Flux<User> users = this.userService.getAllUser();
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(users,User.class);
}
//添加
public Mono<ServerResponse> saveUser(ServerRequest request) {
//得到user对象
Mono<User> userMono = request.bodyToMono(User.class);
return ServerResponse.ok().build(this.userService.saveUserInfo(userMono));
}
}
创建路由和服务器
public class Server {
public static void main(String[] args) throws Exception{
Server server = new Server();
server.createReactorServer();
System.out.println("enter to exit");
System.in.read();
}
//1 创建Router路由
public RouterFunction<ServerResponse> routingFunction() {
//创建hanler对象
UserService userService = new UserServiceImpl();
UserHandler handler = new UserHandler(userService);
//设置路由
return RouterFunctions.route(
GET("/users/{id}").and(accept(APPLICATION_JSON)),handler::getUserById)
.andRoute(GET("/users").and(accept(APPLICATION_JSON)),handler::getAllUsers);
}
//2 创建服务器完成适配
public void createReactorServer() {
//路由和handler适配
RouterFunction<ServerResponse> route = routingFunction();
HttpHandler httpHandler = toHttpHandler(route);
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
//创建服务器
HttpServer httpServer = HttpServer.create();
httpServer.handle(adapter).bindNow();
}
}
5764

被折叠的 条评论
为什么被折叠?



