Spring5笔记
Spring5
1,Spring概念
Spring框架概述
- Spring是轻量级的开源的JavaEE框架。
- Spring可以解决企业应用开发的复杂性。
- Spring有两个核心部分:IOC和AOP
- IOC:控制反转,把创建对象过程交给Spring管理。
- AOP:面向切面,不修改源代码的情况下进行功能增强。
- Spring特点:
- 方便解耦,简化开发。
- AOP编程的支持。
- 方便程序测试。
- 方便和其他框架进行整合。
- 降低JavaEE APL开发难度。
- 方便进行事务操作。
- 现在课程中,选取Spring5版本5.X
入门案例
-
下载Spring5:https://spring.io/projects/spring-framework#learn
左侧选择Spring framework,右侧选择learn, 选择GA版本,GA是稳定版本。
下一步选择GetHub,去GetHub下载,下拉选择Access to Binaries
下拉选择Downloading a Distribution
到新网页,选择Artifacts -》libs-release-》org-》springframeword-》spring
右侧赋值Repository Path 地址,在地址栏上 https://repo.spring.io/追加到后面
下载地址:https://repo.spring.io/libs-release/org/springframework/spring/
-
用idea创建一个普通的module
-
导入Spring5的包
beans core context expression
还需要一个commons logging 日志包
日志包网址:http://commons.apache.org/proper/commons-logging/download_logging.cgi 选择二进制文件
-
创建普通类,在这个类创建普通方法。
public class User { public void add() { System.out.println("add..."); } }
-
spring中创建对象可以通过配置文件或者注解。
创建spring配置文件,在配置文件使用xml格式,在src下
<?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.zhiyou100.User"></bean> <!--在bean中可以写两个属性 id:别名 class:类的全路径--> </beans>
-
进行测试代码编写
@Test public void testAdd() { //1. 加载spring配置文件 //创建类路径Xml应用程序上下文 ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); //2. 获取配置创建的对象 //根据xml中的id传入,和要传入的类字节码文件 User user = context.getBean("user", User.class); System.out.println(user); user.add(); }
2,IOC容器
IOC概念原理
-
什么是IOC?
-
控制反转(Inversion of Control),把对象创建和对象之间的调用过程,交给Spring进行管理。
-
使用IOC目的:为了耦合度降低
-
做的入门案例就是IOC实现
-
-
IOC底层原理
xml解析,工厂模式,反射
-
图画讲解IOC底层原理
IOC接口
-
IOC思想基于IOC容器完成,IOC容器底层就是对象工程
-
Spring提供IOC实现两种方式(两个接口):
-
BeanFactory:IOC容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用。
特点:加载配置文件的时候不会创建对象,在获取对象(使用)的时候才会创建对象。
-
ApplicationContext:BeanFactory接口的子接口,提供更多强大的功能,一般由开发人员进行使用。
特点:加载配置文件的时候就会把配置文件对象进行创建。
-
他们都能进行IOC容器实现,功能都可以做到。
-
-
ApplicationContext 接口的实现类
FileSystemXmlApplicationContext:配置文件就要写盘符里面的--绝对路径
ClassPathXmlApplicationContext:src下类路径--相对路径
-
BeanFactory 接口的实现类
Bean管理
-
什么是Bean管理?
Bean管理指的是两个操作
1.Spring创建对象
2.Spring注入属性 -
Bean管理操作有两种方式
- 基于xml配置文件方式实现
- 基于注解方式实现
IOC操作Bean管理(基于xml)
-
基于xml方式创建对象
<bean id="user" class="com.atguigu.spring5.User"></bean>
-
在Spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建。
-
在bean标签有很多属性,介绍常用的属性
-
id属性:唯一标识
-
class属性:类全名路径(包类路径)
-
name属性: 和id属性一样
id和name属性的区别是:id不能加特殊符号,name可以。
-
-
创建对象的时候,默认是执行无参数构造方法完成对象创建。
-
-
基于xml方式注入属性
-
DI:依赖注入,就是注入属性。di是ioc的一种具体实现,就是依赖输入。必须式在创建对象的前提下。
-
第一种方式:使用set方法进行注入
-
创建类,定义属性和对应的set方法
public class Book { private String bName; private String bAuthor; public void setbName(String bName) {this.bName = bName;} public void setbAuthor(String bAuthor) {this.bAuthor = bAuthor;} public void show() {System.out.println(bName+"的作者是:"+bAuthor);} }
-
在spring配置文件配置对象创建,配置属性注入
<!--1. 配置book对象创建--> <!--在bean中可以写两个属性 id:别名 class:类的全路径--> <bean id="book" class="com.zhiyou100.Book"> <!--2. set方法注入属性--> <!--使用property完成属性注入 name:类里面属性名称 value:向属性注入的值--> <property name="bName" value="《西游记》"></property> <property name="bAuthor" value="吴承恩"></property> </bean>
-
测试类
@Test public void testBook1() { //1. 加载spring配置文件 BeanFactory context = new ClassPathXmlApplicationContext("bean1.xml"); //2. 获取配置创建的对象 Book book = context.getBean("book", Book.class); book.show(); }
-
-
第二种方式:使用有参构造方法进行注入
-
创建类,定义属性,创建属性对应有参数构造方法
public class Orders { private String oName; private String address; public Orders(String oName, String address) {this.oName = oName;this.address = address;} public void show() { System.out.println("名字:" + oName + ",地址:" + address); } }
-
在spring配置文件中配置
<bean id="order" class="com.zhiyou100.Orders"> <!--使用constructor-arg标签进行构造方法赋值 name:属性 value:值 --> <constructor-arg name="address" value="电脑"></constructor-arg> <constructor-arg name="oName" value="China"></constructor-arg> <!--或者使用index=属性 value=值 也可以--> <!--<constructor-arg index="0" value="电脑"></constructor-arg> <constructor-arg index="1" value="China"></constructor-arg> --> </bean>
-
测试:
@Test public void testOrder() { //创建类路径Xml应用程序上下文,加载spring配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); //根据方法传入属性创建对象 Orders order = context.getBean("order", Orders.class); order.show(); }
-
-
-
p名称空间注入(set方法注入)
- 使用p名称空间注入,可以简化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" 第一步:添加p名称空间在配置文件中 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标签里面进行操作 <bean id="book" class="com.zhiyou100.Book" p:bAuthor="金庸" p:bName="《笑傲江湖》"></bean> </beans>
测试:之前那个就可以(o)!
IOC操作Bean管理(xml注入其他类型)
-
xml注入其他类型属性
-
字面量:
-
null值
<!--根据set方法赋值--> <property name="address" ><null/></property>
-
属性值包含特殊符号:
<!--属性值包含特殊符号 1. 把<>进行转义 <> 2. 把带特殊符号内容写道CDATA中(必须大写) --> <property name="address" > <value><![CDATA[ <<南京>> ]]></value> </property>
-
-
-
注入属性 --外部bean
-
创建两个类service类和dao类
-
在service调用dao里面的方法
-
在Spring配置文件中进行配置
public interface UserDao { public void update(); } public class UserDaoImpl implements UserDao { @Override public void update() {System.out.println("dao update ......");} } public class UserService { //属性注入 private UserDao userDao; public void setUserDao(UserDao userDao) {this.userDao = userDao;} public void add() { System.out.println("service add ......"); //调用方法 userDao.update();} }
<!--创建service和dao对象--> <bean id="userService" class="com.zhiyou100.service.UserService"> <!--注入userDao对象 ref属性:创建userDao对象bean标签id值 ref=bean标签id值(传入对象) --> <property name="userDao" ref="userDaoImp"></property> </bean> <bean id="userDaoImp" class="com.zhiyou100.dao.UserDaoImpl"></bean>
-
-
注入属性 --内部bean
-
一对多关系:部门和员工
一个部门有对个员工,一个员工属于一个部门
部门是一,员工是多。
-
在实体类之间标识一对多关系
public class Dept { private String dName; public void setdName(String dName) {this.dName = dName;} public String getdName() {return dName;} } public class Emp { private String eName; private String gender; //员工属于一个部门,使用对象形式表示 private Dept dept; public void setDept(Dept dept) {this.dept = dept;} public void seteName(String eName) {this.eName = eName;} public void setGender(String gender) {this.gender = gender;} public void show() {System.out.println(eName + "," + gender + "," + dept.getdName());} }
<!--内部bean--> <bean id="emp" class="com.zhiyou100.bean.Emp"> <!--设置两个不同普通属性--> <property name="eName" value="lucy"></property> <property name="gender" value="男"></property> <!--设置对象属性--> <property name="dept"> <!--在内部创建对象--> <bean id="dept" class="com.zhiyou100.bean.Dept"> <property name="dName" value="保安部"></property> </bean> </property> </bean>
-
-
注入属性 --级联赋值
-
第一种:(和3一样只是xml配置不一样)
<!--级联赋值--> <bean id="emp" class="com.zhiyou100.bean.Emp"> <!--设置两个不同普通属性--> <property name="eName" value="lucy"></property> <property name="gender" value="男"></property> <!--级联赋值--> <property name="dept" ref="dept"></property> </bean> <bean id="dept" class="com.zhiyou100.bean.Dept"> <property name="dName" value="财务部"></property> </bean>
-
第二种:
需要做一件事情,在emp中属性dept生成get方法,如果取不到就不能赋值
<!--级联赋值--> <bean id="emp" class="com.zhiyou100.bean.Emp"> <!--设置两个不同普通属性--> <property name="eName" value="lucy"></property> <property name="gender" value="男"></property> <!--级联赋值--> <property name="dept" ref="dept"></property> <property name="dept.dName" value="技术部"></property> </bean> <bean id="dept" class="com.zhiyou100.bean.Dept"> </bean>
-
IOC操作Bean管理(xml注入集合属性)
-
注入数组,list集合,map集合,set集合,对象集合,类型属性
public class Stu { //1. 数组属性 private String[] course; //2. list集合类型属性 private List<String> list; //3. map集合类型属性 private Map<String, String> map; //4. set集合类型属性 private Set<String> set; //5. 对象集合 private List<Course> courses; //省略set方法toString方法 }
<bean id="stu" class="com.zhiyou100.collectionType.Stu"> <!--数组--> <property name="course"> <!--list和array都是可以的--> <array> <value>语文</value> <value>数学</value> <value>英语</value> </array> </property> <!--list集合--> <property name="list"> <list> <value>张三</value> <value>李四</value> <value>王五</value> </list> </property> <!--map集合--> <property name="map"> <map> <entry key="java" value="java"></entry> <entry key="php" value="php"></entry> </map> </property> <!--set集合--> <property name="set"> <set> <value>北京</value> <value>南京</value> </set> </property> <!--在集合里面设置对象类型值--> <property name="courses"> <list> <ref bean="course1"></ref> <ref bean="course2"></ref> </list> </property> </bean> <!--创建对象给集合对象赋值--> <bean id="course1" class="com.zhiyou100.bean.Course"> <property name="cName" value="孙子兵法"></property> </bean> <bean id="course2" class="com.zhiyou100.bean.Course"> <property name="cName" value="中华小子"></property> </bean>
测试省略。
-
把集合注入部分提取出来
public class Book { private List<String> list; //省略set,toString方法 }
<?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: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"> <!--1. 在spring配置文件中引入名称空间util 把 schemaLocation后面的地址赋值一份beans换成util加在后面--> <!-- 2. 使用util标签完成list集合注入提取。--> <!--2.1 提取list集合类型属性注入--> <util:list id="bookList"> <!--如果是对象就用 ref标签bean属性--> <value>易筋经</value> <value>九阳神功</value> <value>八卦神功</value> </util:list> <!--2.2 提取list集合类型属性注入使用--> <bean id="book" class="com.zhiyou100.bean.Book"> <property name="list" ref="bookList"/> </bean> </beans>
IOC操作Bean管理(FactoryBean)
-
Spring有两种类型bean,一种普通bean,另外一种工厂bean(factorybean)
-
普通bean,在配置文中定义bean类型就是返回类型
之前写的就是普通bean。
-
工厂bean,在配置文件定义bean类型可以返回类型不一样
-
第一步 创建类,让这个类作为工厂bean,实现FactoryBean。
-
第二部 实现接口里面的方法,在实现的方法中定义返回的bean类型。
public class MyBean implements FactoryBean<Course> { //定义返回bean对象 @Override public Course getObject() throws Exception { Course course = new Course(); course.setcName("NIKE"); return course; } @Override public Class<?> getObjectType() {return null;} @Override public boolean isSingleton() {return false;} }
<bean id="myBean" class="com.zhiyou100.factoryBean.MyBean">
@Test public void MyBeanTest() { ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml"); Course course = context.getBean("myBean", Course.class); System.out.println(course);//Course{cName='NIKE'} }
-
IOC操作Bean管理(作用域基于注解)
-
在Spring里面,设置创建bean实例是单实例还是多实例。
-
在Spring里面,默认情况下,bean是单实例对象。
-
如何设置单实例还是多实例
-
在spring配置文件bean标签里面有属性(scope)用于设置单实例还是多实例
-
scope属性值
第一个值 默认值,singleton,表示单实例对象
第二个值,prototype,表示多实例对象
-
singleton和prototype区别
-
singleton单实例,prototype多实例。
-
设置scope 值是 singleton的时候,加载spring配置文件的时候就会创建单例对象。
设置scope 值是 prototype的时候,不是加载spring配置文件时候创建 对象,在调用getBean()方法时候创建多实例对象。
-
request(一次请求):
-
session(一次会话):
-
IOC操作Bean管理(声明周期基于注解)
-
生命周期:从对象创建到对象销毁的过程。
-
bean声明周期:
- 通过构造器创建bean实例(无参构造方法)
- 为bean的属性设置值和其他bean引用(调用set方法)
- 调用bean的初始化的方法(需要进行配置初始化的方法)
- bean可以使用了(对象获取到了)
- 当容器关闭时候,调用bean的销毁的方法(需要进行配置销毁的方法)
-
代码:
public class Orders { private String oName; public void setoName(String oName) {this.oName = oName;System.out.println("第二分 调用set方法设置值"); } public Orders() {System.out.println("第一步 执行无参构造创建bean实例");} //创建执行初始化的方法 需要在xml配置 public void initMethod() {System.out.println("第三步 执行初始化的方法");} //创建执行销毁的方法 public void destroy() {System.out.println("第五步 执行销毁的方法");} }
<!--init-method=类中方法名字 就是执行初始化的配置--> <!--destroy-method=类之方法名 就是执行销毁的配置--> <bean id="orders" class="com.zhiyou100.bean.Orders" init-method="initMethod" destroy-method="destroy"> <property name="oName" value="摩托"/> </bean>
@Test public void OrderTest() { BeanFactory context = new ClassPathXmlApplicationContext("bean4.xml"); Orders orders = context.getBean("orders", Orders.class); System.out.println("第四步 获取创建bean实例对象"); System.out.println(orders); //销毁需要手动销毁bean实例 //close()方法是子类的方法,父类没有所有需要使用强转来使用 ((ClassPathXmlApplicationContext)context).close(); /* * 第一步 执行无参构造创建bean实例 * 第二分 调用set方法设置值 * 第三步 执行初始化的方法 * 第四步 获取创建bean实例对象 * com.zhiyou100.bean.Orders@396e2f39 * 第五步 执行销毁的方法 * */ }
-
bean的后置处理器,bean的声明周期有七步操作
- 通过构造器创建bean实例(无参构造方法)
- 为bean的属性设置值和其他bean引用(调用set方法)
- 把bean实例传递bean后置处理器的方法 postProcessBeforeInitialization
- 调用bean的初始化的方法(需要进行配置初始化的方法)
- 把bean实例传递bean后置处理器的方法 postProcessAfterInitialization
- bean可以使用了(对象获取到了)
- 当容器关闭时候,调用bean的销毁的方法(需要进行配置销毁的方法)
-
演示添加后置处理器效果
-
创建类,实现类BeanPostProcessor,创建后置处理器(和上面的代码不变,除了新类和配置文件)
public class MyBeanPost implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("在初始化之前执行的方法");return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("在初始化之后执行的方法");return bean; } }
<!--init-method=类中方法名字 就是执行初始化的配置--> <!--destroy-method=类之方法名 就是执行销毁的配置--> <bean id="orders" class="com.zhiyou100.bean.Orders" init-method="initMethod" destroy-method="destroy"> <property name="oName" value="摩托"/> </bean> <!--配置后置处理器--> <!--过程:当你加载配置文件的时候,会把配置文件中的对象创建,并且把 后置处理器创建(当一个类作为BeanPostProcessor的实现类时就把它作为后置处理器执行) ,而后置处理器会对你当前配置文件的所有bean都添加后置处理器的处理 --> <bean id="myBeanPost" class="com.zhiyou100.bean.MyBeanPost"></bean>
@Test public void OrderTest() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml"); Orders orders = context.getBean("orders", Orders.class); System.out.println("第四步 获取创建bean实例对象"); System.out.println(orders); //销毁需要手动销毁bean实例 //close()方法是子类的方法,父类没有所有需要使用强转来使用 context.close(); /* 第一步 执行无参构造创建bean实例 * 第二分 调用set方法设置值 * 在初始化之前执行的方法 * 第三步 执行初始化的方法 * 在初始化之后执行的方法 * 第四步 获取创建bean实例对象 * com.zhiyou100.bean.Orders@365185bd * 第五步 执行销毁的方法 * */ }
-
IOC操作Bean管理(xml自动装配)
-
什么时自动装配
根据指定装配规则 (属性名或者属性类型),Spring自动匹配的属性值进行注入
-
演示自动装配
public class Dept { //省略toStirng } public class Emp { private Dept dept; //省略set,get,toString }
<!--实现自动装配 bean标签autowire,配置自动装配 autowire属性常用两个值: byName根据属性名称注入,注入值bean的id值和类属性名称一样 byType根据属性类型注入,相同类型的bean不能在xml中定义多个 --> <bean id="emp" class="com.zhiyou100.autowire.Emp" autowire="byName"> <!--<property name="dept" ref="dpt"/>--> </bean> <bean id="dept" class="com.zhiyou100.autowire.Dept"></bean>
IOC操作Bean管理(引入外部属性文件 基于xml)
-
直接配置数据库信息
-
配置德鲁伊连接池
-
引入德鲁伊连接依赖jar
<!--直接配置连接池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql:///test"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean>
-
-
引入外部属性文件配置数据库连接池
-
创建外部属性文件,properties格式文件,写数据库信息
url=jdbc:mysql:///test username=root password=root driverClassName=com.mysql.jdbc.Driver
-
把外部properties属性文件引入到spring配置文件中,先引入名称空间context
<?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" 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"> <!--1. 引入名称空间↑ context--> <!--2. 引入外部属性文件--> <context:property-placeholder location="classpath:druid.properties"/> <!--3. 配置连接池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${driverClassName}"/> <!--4. 这里value=${属性文件中的key}--> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </bean> </beans>
-
IOC操作Bean管理(基于注解)
-
什么是注解
- 注解是代码特殊标记,格式:@注解名称(属性名称=属性值,属性名称=属性值,,)
- 使用注解,注解作用在类上面,方法上面,属性上面。
- 使用注解的目的:简化xml配置
-
Spring针对Bean管理中创建对象(交给spring管理)提供的注解
- @Component
- @Service
- @Controller
- @Repository >用在持久层的接口上
- 上面四个注解功能是一样的,都可以用来创建bean实例。
-
基于注解方式实现对象创建
-
第一步 引入依赖:
spring-aop-5.3.4.jar
架包。 -
第二部 开启组件扫描
-
创建类,在类上面添加对象注解
<?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" 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"> <!--引入名称空间context--> <!--开启组件扫描 1. 入库给扫描多个包,多个包使用逗号隔开 2. 扫描包上层目录 --> <context:component-scan base-package="com.zhiyou100"/> </beans>
//在创建对象注解里面value属性值可以省略不写 //默认就是类名称,首字母小写 //UserService --> userService @Component(value = "userService") //<bean id="userService" class=".."/ > value和bean里面的id是等价的 //别的创建对象注解也可以实现效果 public class UserService { public void add() {System.out.println("service add ...");} } //测试 @Test public void test1() { //加载配置文件 类路径应用程序上下文 ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); UserService user = context.getBean("userService", UserService.class); System.out.println(user); user.add(); /* * 第一部分加载配置文件,开启资源扫描, * 在xml找组件扫描中的路径,如果类中有相关的注解 * 根据相关的注解,就把对象创建 * */ }
-
-
开启组件扫描细节配置
<!--示例1 标签context:component-scan,属性:use-default-filters="false" 表示现在不适用默认filter,自己配置filter context:include-filter标签,设置扫描包含那些内容 --> <context:component-scan base-package="com.zhiyou100" use-default-filters="false"> <!--这句话的意思是只在com.zhiyou100下的包中只扫描Controller注解的类,别的注解别的类不扫描--> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <!--示例2 context:exclude-filter标签,设置扫描不包含那些内容 --> <context:component-scan base-package="com.zhiyou100"> <!--这句话的意思是不扫描注解是Controller--> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
-
基于注解方式实现属性注入
-
@Autowired:根据类型自动注入进行自动装配
第一步把 service 和 dao 包的对象创建,在 service 和 dao 中类添加创建对象注解
第二步把 service 和 dao包中对象,在 service 类添加到dao类型属性,在属性上面使用注解
public interface UserDao {public void add();} @Repository //创建对象 可以根据类型或者名称进行注入 public class UserDaoImp implements UserDao { @Override public void add() {System.out.println("dao ad......");} } @Service //创建对象 public class UserService { //定义dao类型属性 //不需要添加set方法,因为在里面已经包我们封装了 //添加注入属性注解 @Autowired //根据类型注入 private UserDao userDao; public void add() { System.out.println("service add ...");userDao.add();} }
<?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" 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"> <!--1. 引入名称空间context--> <!--2. 开启组件扫描 2.1. 入库给扫描多个包,多个包使用逗号隔开 2.2. 扫描包上层目录 --> <context:component-scan base-package="com.zhiyou100"/> </beans>
-
@Qualifier:根据属性名称进行注入
这个 @Qualifier 注解的使用,和上面 @Autowired一起使用
@Repository(value = "userDaoImpl1") //可以根据类型或者名称进行注入 public class UserDaoImpl implements UserDao { @Override public void add() {System.out.println("dao ad......");} } @Service//创建对象 public class UserService { //定义dao类型属性 //不需要添加set方法,因为在里面已经包我们封装了 //添加注入属性注解 @Autowired //根据类型注入 //@Autowired是为了找类型,但是可以有两种或多中相同类型的bean对象,所以需要搭配@Qualifier @Qualifier(value = "userDaoImpl1")//根据名称进行注入 private UserDao userDao; public void add() {System.out.println("service add ..."); userDao.add();} }
-
@Resource:可以根据类型注入,可以根据名称注入
所在:javax.annotation.Resource ,Java扩展包中的注解,jdk11中取消javax扩展。spring官方推荐使用前两个,因为是spirng的
@Service public class UserService { //@Resource//根据类型进行注入 @Resource(name = "userDaoImpl1")//根据名称进行注入 private UserDao userDao; public void add() {System.out.println("service add ...");userDao.add();} }
-
@Value:注入普通类型属性
@Value(value = "李白 ")//这就可以将name设置为abc private String name;
-
-
完全注解开发
-
创建配置类,代替xml配置文件
@Configuration//该注解表示当前类作为配置类,代替xml配置文件 //该注解表示开去组件扫描,属性basePackages是一个数组形式。改注解和xml中开启组件扫描是等价的 @ComponentScan(basePackages = {"com.zhiyou100"}) public class SpringConfig { }
-
编写测试类
@Test public void test2() { //创建注释配置应用程序上下文, 加载配置类,使用配置类中的内容 ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); //获取配置创建对象 UserService userService = context.getBean("userService", UserService.class);//此类在上面 System.out.println(userService); userService.add(); }
-
3,Aop
什么是AOP
-
面向切面编程(方面)Aspect Oriented Programing,利用AOP可以对业务逻辑个各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
-
通俗描述:不通过修改源代码方式,在主干功能里面添加新功能。
-
使用登陆例子
AOP(底层原理)
-
AOP底层使用动态代理
-
有两种情况的动态代理
有接口的情况,使用JDK动态代理
- 创建接口实现类代理对象,增强类的方法
没有接口的情况,使用CGLIB动态代理
- 创建子类的代理对象,增强类的方法
-
AOP(jdk动态代理)
-
使用JDK动态代理,使用Proxy类里面的方法创建代理对象
java.langObject
java.lang.reflect.Proxy
(1)调用newProxyInstance方法
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 返回指定接口的代理实例,该代理实例将方法调用分派给指定的调用处理程序。 //参数: loader - 类加载器来定义代理类 interfaces - 增强方法所在的类,这个实现的接口,支持多个接口 h - 实现这个接口InvocationHandler,创建代理对象,写增强的方法
-
代码:
-
创建接口,定义方法
-
创建接口实现类,实现方法
-
使用Proxy类创建接口代理对象
public interface UserDao { public int add(int a, int b); 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; } } 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; } }); */ UserDaoProxy proxy = new UserDaoProxy(new UserDaoImpl()); UserDao dao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, proxy); System.out.println(dao.add(12, 11)); } } //创建代理对象代码 class UserDaoProxy implements InvocationHandler { //1. 把创建的是谁的代理对象,把谁传递过来 private Object o; //通过有参构造传递 public UserDaoProxy(Object o) {this.o = o;} //增强的逻辑 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //在方法之前 System.out.println("方法之前执行" + method.getName() + ",传递的参数" + Arrays.toString(args)); //被增强的方法执行 Object invoke = method.invoke(o, args); //方法之后 System.out.println("invoke = " + invoke); System.out.println("方法之后执行" + o); return invoke; } }
-
AOP(术语)
-
连接点
类里面那些方法可以被增强,这些方法称为连接点
-
切入点
实际被真正增强的方法,称为切入点
-
通知(增强)
-
实际增强的逻辑部分称为通知(增强)
-
通知有多种类型
前置通知: @Before
后置通知(返回值通知): @AfterReturning
环绕(在被增强类的增强方法之前之后进行通知): @Around
异常: @AfterThrowing
最终(类似finally): @After
-
特点:
前置,当有异常就不执行
最终,有没有异常都执行
-
-
切面
时动作,把通知应用到切入点过程。
AOP操作(准备)
-
Spring框架一般都是基于AspectJ实现AOP操作
-
什么是AspectJ?
AspectJ不是Spring组成部分,独立AOP框,一般把AspectJ和Spring框架一起使用,进行AOP操作
-
-
基于AspectJ实现AOP操作
- 基于xml配置文件实现
- 基于注解方式实现(使用)
-
在项目工程里面引入相关的依赖:
在原来的jar包上在引入一个spring-aspects-5.3.4.jar / spring-aspects-5.2.6.RELEASE.jar
和aop其他架包com.springsource.net.sf.cglib-2.2.0.jar,com.springsource.org.aopalliance-1.0.0.jar,com.springsource.org.aspectj.weaver-1.6.8.release.jar
-
切入点表达式
-
作用:对符合切入点表达式的类,会自动生成代理对象。
-
语法:该语句在spring框架下载的解压包-》docs-》reference-》core.html-》5.4.3 Declaring a Pointcut-》Examples
访问修饰符 返回值类型(必填) 包和类 方法(必填) 异常 execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?namepattern(param-pattern) throws-pattern?) execution(<修饰符模式>? <返回类型模式> <方法名模式>(<参数模式>) <异常模式>?)
-
表达式通配符:
*:匹配所有字符
..:一般用于匹配多个包,多个参数
+:表示类及其子类
-
支持逻辑运算符:&&,||,!
-
注意:
除了返回类型模式,方法名模式和参数模式外,其他项都是可选的
-
示例:
1.最全的写法 拦截返回void,指定类的指定方法,参数必须有两个:int、String execution(public void com.sunny.service.Impl.AccountServiceImpl.save(int,java.lang.String)) 2.省略访问修饰符,返回值任意的指定类的save方法,无参数 execution(* com.sunny.service.Impl.AccountServiceImpl.save()) 3.拦截com包下所有的类、以及其子包下所有的类的save()方法 execution(void com..*.save()) 包名与类名或方法名称都可以使用* 4.拦截save()方法/拦截所有方法 execution(* save()) //拦截save() execution(* *()) //拦截所有方法 5.不拦截save()方法 !execution(* save()) not execution(* save()) 注意not前面要有空格 6.拦截save()方法或者update()方法 execution(* save()) || execution(* update())) execution(* save()) or execution(* update())) 7.拦截所有方法,参数任意,但必须有参数 execution(* *(*)) 8.拦截所有方法,参数任意,参数可有可无 execution(* *(..)) 9.对IOC容器中以Service结尾的类,生成代理对象 bean(*Service) 10.最常用 execution(* com.sunny..*ServiceImpl.*(..)) 表示com.sunny包及其子包下所有的以ServiceImpl结尾的类生成代理对象
-
AOP操作(AspectJ注解)
-
创建类,在类里面定义方法
-
创建增强类(编写增强逻辑)
在增强类里面,创建方法,让不同方法代表不同通知类型
-
进行通知的配置
- 在spring配置文件中,开启注解扫描(配置文件或者配置类)
- 使用注解创建User和UserProxy对象
- 在增强类上面添加注解@Aspect 生成代理对象
- 在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.zhiyou100.aopanno"></context:component-scan> <!--3. 开启Aspect生成代理对象。在类中找@Aspect注解,如果有就把这个对象生成代理对象--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
//被增强的类 @Component //创建对象 public class User { //int i=10/0;//创建异常 public void add() {System.out.println("add......");} } //增强的类 @Component //创建对象 @Aspect //生成代理对象 public class UserProxy { //让这个方法作为前置通知 //@Before 注解表示作为前置通知,value可以省略 里面使用切入点表达式 @Before(value = "execution(* com.zhiyou100.aopanno.User.add(..))") public void before() {System.out.println("before......");} } //测试 @Test public void test1() { //加载配置文件,创建类路径Xml应用程序上下文 ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); //根据该类.getBean()方法得到该对象 User user = context.getBean("user", User.class);//前面创建对象注解value可以不写,默认是类首字母小写 //调用方法 System.out.println(user); user.add(); } /*before...... add......*/
在原来的增强的类中添加别的通知类型
//增强的类 @Component //创建对象 @Aspect //生成代理对象 public class UserProxy { //让这个方法作为前置通知 //@Before 注解表示作为前置通知,value可以省略 @Before("execution(* com.zhiyou100.aopanno.User.add(..))") public void before() { System.out.println("before......"); } //最终通知 @After("execution(* com.zhiyou100.aopanno.User.add(..))") public void after() { System.out.println("after......"); } //后置通知 或者 在方法返回值之后执行 @AfterReturning("execution(* com.zhiyou100.aopanno.User.add(..))") public void afterReturning() { System.out.println("AfterReturning......"); } //异常通知 @AfterThrowing("execution(* com.zhiyou100.aopanno.User.add(..))") public void afterThrowing() { System.out.println("AfterThrowing......"); } //环绕通知 @Around("execution(* com.zhiyou100.aopanno.User.add(..))") public void around(ProceedingJoinPoint point) throws Throwable {//通过参数可以让被增强方法在里面执行 System.out.println("环绕之前。。。"); //被增强的方法执行 point.proceed(); System.out.println("环绕之后"); } } //没有异常执行 环绕之前。。。 before...... add...... AfterReturning...... after...... 环绕之后 //有异常执行 环绕之前。。。 before...... AfterThrowing...... after......
-
两个细节问题
-
公共切入点抽取
在增强的类里面
//相同切入点抽取 @Pointcut(value = "execution(* com.zhiyou100.aopanno.User.add(..))") //切入点的注解 public void pointDemo() {} //@Before 注解表示作为前置通知,value可以省略 @Before("pointDemo()")//通过方法在里面调用,方法上面定义了切入点表达式 public void before() { System.out.println("before......"); }
-
有多个增强类对同一个方法进行增强,设置增强类优先级
- 在增强类上面添加注解 @Order(数字类型值) ,数字类型值越小优先级越高。
@Component //创建对象 @Aspect //生成代理对象 @Order(-10) //设置优先级 public class PersonProxy { //让这个方法作为前置通知 //@Before 注解表示作为前置通知,value可以省略 @Before("execution(* com.zhiyou100.aopanno.User.add(..))") public void before() {System.out.println("1 Person before......");} }
-
-
完全注解开发
-
创建配置类,不需要创建xml配置文件按
@Configuration //表示当前类是配置类 @ComponentScan(basePackages = {"com.zhiyou100"}) //开启组件扫描 @EnableAspectJAutoProxy(proxyTargetClass = true)//开启Aspect生成代理对象。在类中找@Aspect注解,如果有就把这个对象生成代理对象 public class ConfigAop { }
-
AOP(AspectJ配置文件)
- 创建两个类,增强类和被增强类,创建方法
- 在spring配置文件中使用名称空间,创建两个类
- 在spring配置文件中配置切入点
//被增强的类
public class Book {
public void buy(){System.out.println("buy ...");}
}
//增强的类
public class BookProxy {
//前置通知
public void before() {System.out.println("before ...");}
}
<?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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--使用名称空间aop ↑-->
<!--创建对象-->
<bean id="book" class="com.zhiyou100.aopXml.Book"></bean>
<bean id="bookProxy" class="com.zhiyou100.aopXml.BookProxy"></bean>
<!--配置切入点:aop增强-->
<aop:config>
<!--切入点-->
<aop:pointcut id="p" expression="execution(* com.zhiyou100.aopXml.Book.buy(..))"/>
<!--配置切面:把增强或通知应用到切入点过程-->
<aop:aspect ref="bookProxy">
<!--增强作用在具体的方法上-->
<!--aop:通知类型 method="增强方法" pointcut-ref="应用的切入点"-->
<aop:before method="before" pointcut-ref="p"></aop:before><!--这句话的意思是把before方法应用在buy上面-->
</aop:aspect>
</aop:config>
</beans>
@Test
public void test2() {
//加载配置文件 创建类路径Xml应用程序上下文
ApplicationContext context =
new ClassPathXmlApplicationContext("bean.xml");
Book book = context.getBean("book", Book.class);
book.buy();
}
4,JdbcTemplate
4.1 概念和准备
-
什么是JdbcTemplate?
Spring框架对JDBC进行封装,使用JdbcTemplate方便实现对数据库操作
-
准备工作
-
引入相关的jar包
Spring框架里的 spring-jdbc-5.3.4.jar(对jdbc进行了封装),spring-tx-5.3.4.jar(针对事务相关操作),spring-orm-5.3.4.jar(整合其他框架需要的)
-
在spring配置文件配置数据库连接池
-
配置JdbcTemplate 对象,注入 DataSource
-
创建service类注入dao对象,创建dao类,在dao注入JdbcTemplate对象
<?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" 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"> <!--使用名称空间 context--> <!--开启组件扫描--> <context:component-scan base-package="com.dalao"/> <!--使用德鲁伊配置连接池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql:///test"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean> <!--创建JdbcTemplate对象注入 DataSource--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <!--注入数据源,父类的set方法注入--> <!--property name=属性 ref=数据源信息--> <property name="dataSource" ref="dataSource"></property> </bean> </beans>
public interface BookDao {} @Repository //创建对象 public class BookDaoImp implements BookDao { //注入JdbcTemplate @Autowired //根据类型自动注入进行自动装配 private JdbcTemplate jdbcTemplate; public JdbcTemplate getJdbcTemplate() {return jdbcTemplate;} public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;} } @Service //创建对象 public class BookService { //注入dao @Autowired //根据类型自动注入进行自动装配 private BookDao bookDao; public BookDao getBookDao() {return bookDao;} public void setBookDao(BookDao bookDao) {this.bookDao = bookDao;} }
-
4.2 JdbcTemplate操作数据库
添加
-
对应数据库创建实体类
-
编写service和dao
-
编写dao进行数据库添加操作
-
调用JdbcTemplate对象里面update方法实现添加操作
int update(String sql, @Nullable Object... args) 参数: sql:sql语句 args:可变参数,设置sql语句值
-
//实体类
public class Book {
private Integer id;
private String userName;
private String ustatus;
//省略set,get,toString,无参,有参方法
}
//dao层实现类代码实现
@Repository //创建对象
public class BookDaoImp implements BookDao {
//注入JdbcTemplate
@Autowired //根据类型自动注入进行自动装配
private JdbcTemplate jdbcTemplate;
@Override//重写接口方法
public void add(Book book) {
String sql = "insert into t_book (username,ustatus) values(?,?) ";//id自增
Object[] args = {book.getUserName(), book.getUstatus()};
int update = jdbcTemplate.update(sql, args);//返回受影响行数
}
}
//方法测试
@org.junit.Test
public void testAdd() {
//创建对象
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
BookService book = context.getBean("bookService", BookService.class);
book.addUser(new Book(100, "赵六", "吃饭"));
}
修改和删除
@Override
public void delete(int id) {
String sql = " delete from t_book where user_id =?";
int update = jdbcTemplate.update(sql, id);
}
@Override
public void modify(Book book) {
String sql = "update t_book set username=?,ustatus=? where user_id=?";
Object[] args = {book.getUserName(), book.getUstatus(), book.getId()};
int update = jdbcTemplate.update(sql, args);
}
查询返回某个值
-
查询表里面有多条记录,返回的某个值
-
使用JdbcTemplate实现查询返回某个值
<T> T queryForObject(String sql, Class<T> requiredType) 参数: sql:sql语句 requiredType:返回类型Class
@Override
public Integer selectCount() {
String sql = "select count(*) from t_book";
Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
return count;
}
查询返回一个对象
-
场景:查询图书详情
-
JdbcTemplate实现查询返回对象
<T> T queryForObject(String sql, RowMapper<T> rowMapper, @Nullable Object... args) 参数 sql:sql语句 rowMapper:RowMapper是接口,针对返回不同类型数据,使用这个接口里面实现类完成数据封装 args:sql语句值
@Override
public Book queryOne(Integer id) {
String sql = "select user_id as id,username as userName,ustatus from t_book where user_id=? ";
Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), id);
return book;
}
查询返回多个对象
-
场景:查询图书列表分页。。。
-
调用JdbcTemplate方法实现查询返回集合
<T> List<T> query(String sql, RowMapper<T> rowMapper) 参数: sql:sql语句 rowMapper:RowMapper是接口,针对返回不同类型数据,使用这个接口里面实现类完成数据封装 args:sql语句值
@Override
public List<Book> queryAll() {
String sql = "select user_id id,username as userName,ustatus from t_book";
List<Book> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Book.class));
return list;
}
批量操作
-
批量插座:操作表里面多条记录
-
JdbcTemplate实现批量添加操作
int[] batchUpdate(String sql, List<Object[]> batchArgs) 参数: sql:sql语句 batchArgs:list集合,添加多条记录数据
//dao层
@Override
public void batchAdd(List<Object[]> batchArgs) {
String sql = "insert into t_book (username,ustatus) values(?,?) ";
int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);//把list集合遍历,把每个数组值执行sql语句进行添加
System.out.println(Arrays.toString(ints));
}
//测试
@org.junit.Test
public void testBatchAdd() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
BookService books = context.getBean("bookService", BookService.class);
ArrayList<Object[]> batchArgs = new ArrayList<>();
Object[] o1 = {"小华", "b"};
Object[] o2 = {"小明", "a"};
batchArgs.add(o1);
batchArgs.add(o2);
books.batchAdd(batchArgs);//[1, 1]
}
批量修改
//dao层
@Override
public void batchUpdate(List<Object[]> batchArgs) {
String sql = "update t_book set username=?,ustatus=? where user_id=?";
int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
System.out.println(Arrays.toString(ints));
}
//测试
@org.junit.Test
public void testBatchUpdate() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
BookService books = context.getBean("bookService", BookService.class);
ArrayList<Object[]> list = new ArrayList<>();
Object[] o1 = {"理想", "吃饭", 2};
Object[] o2 = {"辉煌", "打豆豆", 3};
list.add(o1);
list.add(o2);
books.batchUpdate(list);
}
批量删除
//dao层
@Override
public void batchDelete(List<Object[]> batchArgs) {
String sql = " delete from t_book where user_id =?";
int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
System.out.println(Arrays.toString(ints));
}
//测试
@org.junit.Test
public void testBatchDelete() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
BookService books = context.getBean("bookService", BookService.class);
ArrayList<Object[]> list = new ArrayList<>();
Object[] o1 = {2};
Object[] o2 = {4};
list.add(o1);
list.add(o2);
books.batchDelete(list);
}
5,事务管理
事务介绍
- 什么是事务?
- 事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败。
- 典型场景:银行转账
- 事务四个特性(ACID)
- 原子性:
- 一致性:
- 隔离性:
- 持久性:
事务操作(搭建事务操作环境)
-
Service:业务操作
创建转账的方法
调用dao两个的方法
-
Dao:数据库操作不写业务
创建两个方法
少钱的方法
多钱的方法
-
创建数据库表,添加记录
-
创建service,搭建dao,完成对象创建和注入关系
- service注入dao,在dao注入JdbcTemplate,在JdbcTemplate注入DataSource
-
在dao创建两个方法:多钱和少钱的方法,在service创建方法(转账的方法)
-
上面代码,如果正常执行没有问题,但是如果代码执行过程中出现异常,有问题
-
上面的问题需要使用--事务解决
-
事务操作过程
-
// try { // 第一步 开启事务 // 第二部 进行业务操作 // 第三步 没有异常提交 // } catch (Exception e) { // 第四步 出现异常回滚 // e.printStackTrace(); // } finally { // }
-
事务操作(Spring事务管理介绍)
-
事务添加到JavaEE三层结构里面Service层(业务逻辑层)
-
在Spring进行事务管理操作
有两种方式:编程事务管理try-case-finally,声明式事务管理(使用)
-
声明事务管理
- 基于注解方式(使用)
- 基于xml配置文件方式
-
在Spring进行声明式事务管理,底层使用AOP原理
-
Spring事务管理api
-
提供一个接口,代表事务管理器,这个接口针对不同框架提供不同的实现类
-
事务操作(注解 声明式事务管理)
-
在Spring配置文件配置事务管理器 DataSourceTransactionManager,注入属性dataSource
-
在Spring配置文件,开启事务注解
-
在spring配置文件引入名称空间tx
-
开启事务注解
-
在service类上面(或者 service类里面方法上面)添加事务注解
- @Transactional,这个注解添加到类上面,也可以添加方法上面
- 如果注解在类上面,这个类里面所有的方法都添加事务
- 如果注解在方法上面,为这个方法添加事务
<?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 http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!--使用名称空间 context,tx--> <!--开启组件扫描--> <context:component-scan base-package="com.dalao"/> <!--使用德鲁伊配置连接池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql:///test"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean> <!--创建JdbcTemplate对象注入 DataSource--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <!--注入数据源,父类的set方法注入--> <!--property name=属性 ref=数据源信息--> <property name="dataSource" ref="dataSource"></property> </bean> <!--创建事务管理--> <bean id="transaction" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--注入数据源dataSource 使用set方法--> <property name="dataSource" ref="dataSource"/> </bean> <!--开启事务注解 transaction-manager=指定的事务管理 --> <tx:annotation-driven transaction-manager="transaction"></tx:annotation-driven> </beans>
public interface AccountDao { void modifyAccount(Account account); } @Repository//创建对象 public class AccountDaoImpl implements AccountDao { @Autowired //根据类型自动装配 private JdbcTemplate jdbcTemplate; @Override public void modifyAccount(Account account) { String sql = "update t_account set username=?,money=? where id=?"; int update = jdbcTemplate.update(sql, account.getUsername(), account.getMoney(), account.getId()); System.out.println(update); } } @Service //创建对象 @Transactional //添加事务注解 public class AccountService { //注入dao @Autowired //根据类型自动装配 private AccountDao accountDao; public void modifyAccount() { // try { // 第一步 开启事务 // 第二部 进行业务操作 //转账 accountDao.modifyAccount(new Account(1, "lucy", 800)); //模拟异常 int a = 1 / 0; accountDao.modifyAccount(new Account(2, "mary", 1200)); // 第三步 没有异常提交 // } catch (Exception e) { // 第四步 出现异常回滚 // e.printStackTrace(); // } finally { // } } } //测试 @org.junit.Test public void test1() { //加载spring配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); //获取配置创建对象 默认v是类名小写 AccountService service = context.getBean("accountService", AccountService.class); //service.modifyAccount(); service.queryAll().forEach(System.out::println); }
-
事务操作(声明式事务管理参数配置)
-
在service类上面添加注解@Transaction,在这个注解里面可以配置事务相关参数
-
propagation:事务传播行为
-
多事务方法直接进行调用,这个过程中事务是如何进行管理的
//添加事务注解 可以在类上,或者类中方法外 @Transactional(propagation = Propagation.REQUIRED) //默认不写就是 REQUIRED
-
-
isolation:事务隔离级别
-
事务有特性成为隔离性,多事务操作之间不会产生影响。不考虑隔离性产生很多问题
-
有三个读的问题:脏读,不可重复读,虚(幻)读
-
脏读:一个未提交事务读取到另一个未提交事务的数据
-
不可重复读:一个未提交事务读取到了另一个提交事务修改的数据
-
幻读:一个未提交事务读取到了另一个提交事务添加的数据
-
通过设置事务隔离级别,解决读问题
@Service //创建对象 //添加事务注解 REPEATABLE_READ -->mysql默认隔离级别 @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
-
-
timeout:超时时间
- 事务需要在一定时间内进行提交,如果不提交就进行回滚
- 默认值是-1,设置使时间以秒单位进行计算
-
readOnly:是否只读
- 读:查询操作。写:添加修改删除操作
- readOnly默认值false,表示可以查询,可以添加修改删除操作
- 设置readOnly值为true,表示只能查询
-
rollbackFor:回滚
- 设置出现那些异常进行事务回滚
-
noRollbackFor:不回滚
- 设置出现那些异常不进行事务回滚
事务操作(xml 声明式事务管理)
-
在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: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 https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--使用名称空间 context,tx,aop--> <!--开启组件扫描--> <context:component-scan base-package="com.dalao"/> <!--使用德鲁伊配置连接池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql:///test"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean> <!--创建JdbcTemplate对象注入DataSource(连接池配置信息)--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <!--注入数据源,父类的set方法注入--> <!--property name=属性 ref=连接池数据源信息--> <property name="dataSource" ref="dataSource"/> </bean> <!--1. 创建事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--注入数据源dataSource 使用set方法--> <property name="dataSource" ref="dataSource"/> </bean> <!--2. 配置(事务)通知--> <tx:advice id="txadvice"> <!--配置事务相关的参数--> <tx:attributes> <!--name属性:指定哪种规则的方法上面添加事务。后面可跟其他事务参数 第一种写法:方法名字 第二部写法:方法名字* (只要是方法名字开头添加事务操作) --> <tx:method name="modifyAccount" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!--3. 配置切入点和切面--> <aop:config> <!--配置切入点 使用表达式--> <aop:pointcut id="pt" expression="execution(* com.dalao.service.AccountService.*(..))"/> <!--配置切面--> <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/> </aop:config> </beans>
@org.junit.Test public void test2() { //加载spring配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml"); //获取配置创建对象 默认v是类名小写 AccountService service = context.getBean("accountService", AccountService.class); //service.modifyAccount(); service.queryAll().forEach(System.out::println); }
事务操作(完全注解声明式事务管理)
- 创建配置类,使用配置类代替xml配置文件
@Configuration //表示当前类式配置类
@ComponentScan(basePackages = "com.dalao")//开启组件扫描
@EnableTransactionManagement //开启事务
public class TxConfig {
//创建数据库连接池
@Bean
public DruidDataSource getDruidDataSource() {
DruidDataSource source = new DruidDataSource();
source.setDriverClassName("com.mysql.jdbc.Driver");
source.setUrl("jdbc:mysql:///test");
source.setUsername("root");
source.setPassword("root");
return source;
}
//创建JdbcTemplate对下个你
@Bean
public JdbcTemplate getJdbcTemplate(DataSource source) {//ioc容器中已经存在了数据源,根据它的类型找到它的对象
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//注入dataSource
jdbcTemplate.setDataSource(source);
return jdbcTemplate;
}
//创建事务管理器
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource source) {//从ioc容器中找到,然后传入
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
//注入datasource
transactionManager.setDataSource(source);
return transactionManager;
}
}
@org.junit.Test
public void test3() {
//创建注释配置应用程序上下文
ApplicationContext context =
new AnnotationConfigApplicationContext(TxConfig.class);
//获取配置创建对象 默认v是类名小写
AccountService service = context.getBean("accountService", AccountService.class);
// service.modifyAccount();
service.queryAll().forEach(System.out::println);
}
6,Spring5新特性
-
整个框架的代码基于java8,运行时兼容JDK9,许多不建议使用的类和方法在代码库中删除。
-
Spring 5.0框架自带了通用的日志封装。
-
Spring5已经移除了Log4jConfigListener,官方建议使用Log4j2, 如果还想使用Log4j需要spring降到4的版本
-
Spring5框架整合Log4j2(http://logging.apache.org/log4j/2.x/download.html)
第一步引入jar包
第二部创建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>
手动输出日志
class UserLog { //都是 org.slf4j 包下的 private static final Logger LOGGER= LoggerFactory.getLogger(UserLog.class); public static void main(String[] args) { LOGGER.info("hello 1"); LOGGER.warn("hello 2"); } }
-
-
spring5支持@Nullable注解
- @Nullable:可以使用在方法,属性,参数上面,表示方法返回值可以为空,属性值可以为空,参数值可以为空。
-
spring5支持函数式风格GenericApplicationContext / AnnotationConfigApplicationContext
@org.junit.Test public void test4() { //创建GenericApplicationContext 对象 GenericApplicationContext context = new GenericApplicationContext(); //调用context的方法对象注册 context.refresh();//把里面内容清空,然后注册 //context.registerBean(User.class, () -> new User()); context.registerBean("user1",User.class, User::new);//简化上面 //3. 获取在spring注册的对象 //User user = (User) context.getBean("com.dalao.bean.User"); User user = (User) context.getBean("user1"); System.out.println(user); }
-
Spring支持JUnit 5's Juptier编程和拓展模块在Spring TestContext框架
-
整合JUnit4
第一步 引入Spring相关针对测试依赖:spring-test-5.3.4.jar,JUnit4的jar包
第二步 创建测试类,使用注解完成
@RunWith(SpringJUnit4ClassRunner.class)//里面设置相关的单元测试版本 @ContextConfiguration("classpath:bean1.xml")//加载配置文件 public class TestJ { @Autowired //自动注入属性 private AccountService accountService; @Test public void test1() {accountService.queryAll();} }
-
整合JUnit5 导入junit的jar包
第一步 导入junit5的jar包
第二步 创建测试类,使用注解完成
@ExtendWith(SpringExtension.class) //注解引用 @ContextConfiguration("classpath:bean1.xml")//加载配置文件 public class TestJ5 { @Autowired private AccountService accountService; @Test public void test1() {accountService.queryAll();} }
-
使用 @SpringJunitConfig:一个复合注解,来代替上面两个注解完成整合
@SpringJUnitConfig(locations = "classpath:bean1.xml")// public class TestJ5 { @Autowired private AccountService accountService; @Test public void test1() {accountService.queryAll();} }
-
Spring5新模块--WebFlux
-
spring webflux介绍
-
(web --》 WebSocket,webMVC,Web,WebFlux)
-
是Spring5添加新的模块,用于web开发的,功能SpringMVC类似,Webflux 使用当前一种比较流行的响应式编程出现的框架。
-
使用传统web框架,比如SpringMVC,这些基于 Servvlet 容器,Webflux 是一种异步非阻塞的框架,异步非阻塞的框架在Servlet3.1以后才支持,核心是居于 Reactor(响应式编程) 的相关api实现的。
-
什么是异步非阻塞:针对对象不一样。
异步,同步:针对调用者。调用者发送请求,如果等着对方回应之后才去做其他事情就是同步;如果发送请求之后不等着对方回应就去做其他事情就是异步。
阻塞,非阻塞针:针对被调用者。被调用者收到请求之后, 做完请求任务之后才给出反馈就是阻塞;收到请求之后马上给出反馈然后再去做其他事情就是非阻塞。(简单来说阻塞需要等待,非阻塞不需要等待)
-
Webflux特点
第一非阻塞式:在有限资源下,提高系统吞吐量和伸缩性,以JReactor为基础实现响应式编程。
第二函数式编程:Spring5框架基于java8,Webflux使用Java8函数式编程方式实现路由请求。
-
比较SpringMVC
第一,两个框架都可以使用注解方式,都运行在Tomcat等容器中。
第二,SpringMVC采用命令式编程,Webflux采用异步相应式编程。
一般项目使用mvc就可以,如果使用远程服务调用不妨使用webflux。
-
-
响应式编程
-
什么是响应式编程:
响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便的表达静态或动态的数据流,而相关的计算机模型会自动变化的值通过数据流进行传播。
-
Java8及其之前的版本。提供了观察者模式两个类 Observer 和 Observable,在java9及之后的版本取代了这两个类(Flow )。
//创建spring initializer项目 public class ObservableDemo extends Observable { public static void main(String[] args) { //创建对象 ObservableDemo demo = new ObservableDemo(); //添加观察者模式 demo.addObserver((o, arg) -> { System.out.println("发生了变化"); }); demo.addObserver((o, arg) -> { System.out.println("手动被观察者通知,准备改变"); }); demo.setChanged();//监控数据是否发生变化 demo.notifyObservers();//通知 } }
-
-
响应式编程( Reactor实现)(有订阅才会有输出)
-
响应式编程操作中,Reactor 是满足 Reactive规范框架。
-
Reactor 有两个核心类,Mono 和 Flux,这两个类实现接口 Publisher,提供丰富操作符。Flux对象实现发布者,返回N个元素;Mono实现翻发者,返回0或者1个元素。
-
Flux 和 Mono都是数据流的发布者,使用Flux 和 Mono都可以发出三种数据信号:元素值,错误信号,完成信号。错误信号,和完成信号,都代表终止信号,终止信号用于告诉订阅者数据流结束了。错误信号终止数据流同时把错误信息传递给订阅者。
-
代码演示Flux 和 Mono
第一步引入依赖
<dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-bom</artifactId> <version>2020.0.5</version> </dependency>
第二部编写代码(有订阅才会有输出)
public static void main(String[] args) { //just方法直接声明 Flux.just(1, 2, 3, 4); Mono.just(1); //其他方法 //数组 Integer[] array = {1, 2, 3, 4}; Flux.fromArray(array); //集合 List<Integer> list = Arrays.asList(array); Flux.fromIterable(list); //stream流 Stream<Integer> stream = list.stream(); Flux.fromStream(stream); //错误信号 //Flux.error(Throwable error); }
-
调用 just 或者其他方法只是声明数据流,数据流并没有发出,只有进行订阅之后才会触发数据流,不订阅什么都不会发生的。
Flux.just(1, 2, 3, 4).subscribe(System.out::println) ; Mono.just(1).subscribe(System.out::println);
-
操作符:对数据流进行一道道操作,成为操作符,比如工厂流水线。
-
第一 map 元素映射为新元素
-
第二 flatMap 元素映射为流
把每个元素转换流,把转换之后多个流合并大的流
-
-
-
WebFlux执行流程和核心API
SpringWebflux基于 Reactor,默认使用容器时Netty,Netty是高性能的NIO框架,异步非阻塞的框架。
-
Netty
-
BIO(阻塞状态)
-
NIO(非阻塞)
-
-
SpringjWebflux执行过程和SpringMVC相似的
SpringWebflux核心控制器DispatchHandler,实现接口WebHandler
接口WebHandler有一个方法
修改导入
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency>
public interface WebHandler { Mono<Void> handle(ServerWebExchange var1); } public class DispatcherHandler implements WebHandler, PreFlightRequestHandler, ApplicationContextAware { public Mono<Void> handle(ServerWebExchange exchange) {//放http请求响应信息 if (this.handlerMappings == null) { return this.createNotFoundError();//如果为空创建一个notfound一个错误 } else { return CorsUtils.isPreFlightRequest(exchange.getRequest()) ? this.handlePreFlight(exchange) : Flux.fromIterable(this.handlerMappings).concatMap((mapping) -> { return mapping.getHandler(exchange);//根据对应请去地址,获取对应mapping }).next().switchIfEmpty(this.createNotFoundError()).flatMap((handler) -> { return this.invokeHandler(exchange, handler);//执行业务 }).flatMap((result) -> { return this.handleResult(exchange, result);//把处理结果进行返回 }); } } }
-
SpringWebflux里面 DispatcherHandler,负责请求的处理
HandlerMapping:根据客户端请求查询处理请求的方法
HandlerAdapter:具体的业务方法
HandlerResultHandler:响应结果进行处理
-
SpringWebflux实现函数式编程,两个接口:RouterFunction(路由处理),HandlerFunction(函数处理)
-
WebHandler的体系
-
SpringWebFlux(基于注解编程模型)
SpringWebflux实现方式有两种:注解编程模型,函数式编程模型。
使用注解编程模型方式,和之前 SpringMVC使用相似的,只需要把相关依赖配置到项目中,SpringBoot自动配置相关运行容器,默认情况下使用Netty服务器。
-
第一步 创建SpringBoot工程,引入webfux 依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency>
-
第二步 配置启动端口号
-
第三步 创建包和相关类
//创建实体类 public class User { private String name; private String gender; private Integer age; //省略set,get,toString,无参构造,有参构造方法 } //用户操作接口 public interface UserService { //根据id查询用户 Mono<User> getUserById(int id); //查询所有用户 Flux<User> getAllUser(); //添加用户 Mono<Void> saveUserInfo(Mono<User> user); } //操作接口实现类 @Repository //创建对象并交给spring管理 public class UserServiceImpl implements UserService { //创建map集合存储数据,充当数据库 private final Map<Integer, User> map = new HashMap<>(); public UserServiceImpl() { this.map.put(1, new User("张三", "男", 18)); } @Override public Mono<User> getUserById(int id) { return Mono.justOrEmpty(this.map.get(id)); } @Override public Flux<User> getAllUser() { return Flux.fromIterable(this.map.values()); } @Override public Mono<Void> saveUserInfo(Mono<User> userMono) { //doOnNext取值(遍历) ; person是起名字 return userMono.doOnNext(person -> { //向map集合中放值 int id = map.size() + 1; map.put(id, person); }).thenEmpty(Mono.empty());//thenEmpty,控制处理。Mono.empty操作之后把里面内容清空。 } }
创建controller
@RestController //交给Spring管理,并返回相关数据 public class UserController { //注入属性 UserService @Autowired private UserService userService; //id查询 @GetMapping("/user/{id}")//查询一般是get请求 public Mono<User> getUserId(@PathVariable int id) {//@PathVariable:获取路径中的id return userService.getUserById(id); } //查询所有 @GetMapping("/user") public Flux<User> getUsers() { return userService.getAllUser(); } //添加 @PostMapping("/saveuser") public Mono<Void> saveUser(@RequestBody User user) {//@RequestBody:使用json形式传递对象 //变成mono形式 Mono<User> userMono = Mono.just(user); return userService.saveUserInfo(userMono); } }
启动在main方法中启动,浏览器中打开:localhost:8081/user/1
-
说明: SpringMVC方式实现,同步阻塞的方式,基于SpringMVC+Servlet+Tomcat
SpringWebflux方式实现,异步非阻塞方式,基于SpringWebflux+Reactor+Netty
SpringWebFlux(基于函数式编程模型)
-
在使用函数式编程模型操作时候,需要自己初始化服务器。
-
基于函数式编程模型的时候,有两个核心接口:ReuterFunction(实现路由功能,请求转发给对应的handler)和HandlerFunction(处理请求生成响应的函数)。核心任务定义两个函数式接口的实现并且启动需要的服务器。
-
SpringWebflux请求和响应不再是Servlet Request 和 Servlet Response,而是ServiceRequest 和 ServerResponse。
-
第一步把注解编程模型工程复制一份
-
第二部创建Handler(具体方法)
public class UserHandler { private final UserService userService; public UserHandler(UserService userService) { this.userService = userService; } //根据id查询 public Mono<ServerResponse> getUserId(ServerRequest request) { //获取id值 int userId = new Integer(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> getUserAll(ServerRequest request) { //调用service得到结果 Flux<User> allUser = this.userService.getAllUser(); return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(allUser, User.class); } //添加 public Mono<ServerResponse> saveUser(ServerRequest request) { //得到user对象 Mono<User> userMono = request.bodyToMono(User.class);//得到请求中的值转成Mono的形式 return ServerResponse.ok().build(this.userService.saveUserInfo(userMono));//build执行 } }
-
第三步初始化服务器,编写Router
public class Server {//创建服务器 //1. 创建Router路由 public RouterFunction<ServerResponse> routerFunction() { //创建hanler对象 UserService userService = new UserServiceImpl(); UserHandler handler = new UserHandler(userService); //设置路由 return RouterFunctions.route( //请求的路径 接收类型的数据 掉handler的方法 GET("/users/{id}").and(accept(APPLICATION_JSON)), handler::getUserId) .andRoute(GET("/users").and(accept(APPLICATION_JSON)), handler::getUserAll); } //2. 创建服务器完成适配 public void createReactorServer() { //2.1 路由和handler适配 //调用路由方法,获取路由对象 RouterFunction<ServerResponse> route = routerFunction(); //创建 HttpHandler ->http请求和存储请求相关的信息 HttpHandler httpHandler = toHttpHandler(route); //适配 ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler); //2.2 创建服务器 //创建http服务 HttpServer httpServer = HttpServer.create(); httpServer.handle(adapter).bindNow();//bindNow:现在进行构建 } //测试调用 public static void main(String[] args) throws IOException { Server server = new Server(); server.createReactorServer();//服务启动 System.out.println("enter to exit"); System.in.read(); } }
-
使用WebClient调用
public class Client { public static void main(String[] args) { //WebClient.create("指定需要调的那个服务器的地址") WebClient webClient = WebClient.create("http:127.0.0.1:6436"); //根据id查询 String id = "1"; //uri(路径,参数) accept(指定接收的类型) retrieve初始化操作 User user = webClient.get().uri("/users/id{}", id).accept(MediaType.APPLICATION_JSON).retrieve(). bodyToMono(User.class).block();//block()执行 //bodyToMono(returnBean.class):得到数据 System.out.println(user.getName()); //查询所有 Flux<User> userFlux = webClient.get().uri("/users") .accept(MediaType.APPLICATION_JSON).retrieve().bodyToFlux(User.class); //输出 //userFlux.map(stu -> stu.getName()).buffer().doOnNext(System.out::println); userFlux.map(User::getName).buffer().doOnNext(System.out::println).blockFirst();//blockFirst订阅 } }