文章目录
- Spring5框架
- 1.SPring框架概述
- 2.IOC容器
- 3.Aop(面向切面编程)
- 4. JdbcTemplate
- 5.事务概念
- 6.Spring5框架新功能
- 7、Spring5新功能-webflux
- 总结
- 总结
Spring5框架
1.SPring概念
2.IOC容器
3Aop
4JDBC Template
5.事务管理
6.Spring 5 新特性
1.SPring框架概述
-
Spring是轻量级的开源的JavaEE框架
-
Spring可以解决企业应用开发的复杂性
-
Spring有两个核心部分 : **
-
IOC 和 AOP
**
-
IOC:控制反转,把创建对象过程交给SPring管理
-
Aop:面向切面,不修改源代码的进行功能增强
-
SPring特点
- 方便解耦,简化开发
- Aop编程支持
- 方便程序测试
- 可以方便和其他框架整合(Toplink, Hibernate, JDO, and iBATIS SQL Maps)
- 方便进行事务操作
- 降低API开发难度
-
选取Spring5版本
入门案例
1 下载SPring5
下载链接
https://repo.spring.io/ui/native/release/org/springframework/spring/
这里下载的是5.2.9 RELEASE稳定版本
2 打开idea工具创建普通Java工程
3.导入Spring5相关jar包
导入jar包
4.创建普通类 在这个类里面创建普通方法
public class User {
public void add(){
System.out.println("add..");
}
}
5.创建Spring配置文件,在配置文件中配置创建的对象
(1)Spring配置文件使用xml格式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fRrojz1u-1639549019989)(1Spring框架概述.assets/image-20211207173650355.png)]
<?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.lyp.spring5.User"></bean>
</beans>
6.进行测试代码编写
public class TestSpring5 {
@Test
public void testAdd(){
//1.加载spring配置文件
ApplicationContext context =
new ClassPathXmlApplicationContext("bean1.xml");
//2.获取配置创建的对象
User user = context.getBean("user", User.class);
System.out.println(user);
user.add();
}
}
2.IOC容器
(1)IOC底层原理
概念和原理
- 什么是IOC 控制反转(Inversion of Control,缩写为IoC)
- 控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理
- 使用IOC目的 : 为了耦合度降低
- 做入门案例就是IOC实现
- IOC底层原理
- xml解析、工厂模式、反射
- 画图
(2)IOC接口(BeanFactory)与ApplicationContext
-
IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
-
Spring提供IOC容器实现两种方式:(两个接口) 都可以. 加载spring配置文件, 获取配置创建的对象
-
BeanFactory :IOC容器基本实现,是Spring内部使用接口,不提供开发人员进行使用
- 加载配置文件时候不会创建对象,在获取对象(使用) 才去创建对象
//1.加载spring配置文件 // ApplicationContext context = // new ClassPathXmlApplicationContext("bean1.xml"); BeanFactory context = new ClassPathXmlApplicationContext("bean1.xml"); //BeanFActory加载配置文件时候不会创建对象,在获取对象(使用) 才去创建对象 //ApplicationContext 加载配置文件时候 会把配置文件的对象进行创建 //2.获取配置创建的对象 User user = context.getBean("user", User.class);
-
ApplicationContext : BeanFActory 接口的子接口,提供更多更强大的功能,一般是由开发人员进行使用。
- 加载配置文件时候 会把配置文件的对象进行创建
-
ApplicationContext接口有实现类
-
一个是c盘下的路径 一个是class类的路径
(3)IOC操作Bean管理(基于xml)与(基于注解)
-
Bean管理 指的是两个操作
- Spring创建对象
- Spring注入属性
-
Bean管理操作有两种方式
-
(基于xml)
-
(基于注解)
-
1 、基于xml方式创建对象
1)在spring配置中,使用bean标签,标签里面添加对应属性,就可以实现对象创建
2)在bean标签里面有很多属性,介绍常用属性
-
id属性 :唯一标识
-
**class属性 :**类全路径(包类路径)
3)创建对象时候,默认也是执行无参构造方法
2 、基于xml方式注入属性
1)DI:依赖注入,就是注入属性
1. 使用set方法进行注入
//1.创建类,定义属性和对应的set方法
public class Book {
//1.创建类,定义属性和对应的set方法
private String bname;
public void setBname(String bname) {
this.bname = bname;
}
public static void main(String[] args) {
Book book = new Book();
book.setBname("abc");
}
}
//2.在spring配置文件配置对象创建,配置属性注入
<!--1. 配置User对象创建 -->
<!--<bean id="user" class="com.lyp.spring5.User" ></bean>-->
<!-- 1/set方法注入属性 -->
<bean id="book" class="com.lyp.spring5.Book">
<!--使用property完成属性注入
name: 类里面属性名称
value:向属性注入的值
-->
<property name="bname" value="水浒传"> </property>
<property name="bauthor" value="施耐庵"> </property>
</bean>
2.使用有参数构造进行注入
public class Book {
private String bname;
//set方法注入
public void setBname(String bname) {
this.bname = bname;
}
//有参数构造注入
public Book(String bname) {
this.bname = bname;
}
public static void main(String[] args) {
//set方法注入
// Book book = new Book();
// book.setBname("abc");
//有参数构造注入
Book book = new Book("abc");
}
}
1》创建类,定义属性,创建属性对应的有参数构造方法
public class Orders {
private String oname;
private String sddress;
//有参构造
public Orders(String oname, String sddress) {
this.oname = oname;
this.sddress = sddress;
}
}
2》在spring配置文件中进行配置
<!-- 3.用有参构造方法注入属性 -->
<bean id="orders" class="com.lyp.spring5.Orders">
<constructor-arg name="oname" value="电脑"></constructor-arg>
<constructor-arg name="address" value="China"></constructor-arg>
</bean>
3 . 第三种 p名称空间注入
1》使用p名称空间注入,可以简化基于xml配置方式
第一步 添加p名称空间在配置文件中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DRxBHgP7-1639549019996)(1Spring框架概述.assets/image-20211208143719712.png)]
第二步 进行属性注入,在bean标签里面进行操作
<!-- 2.set方法注入属性 -->
<bean id="book" class="com.lyp.spring5.Book " p:bname="西游戏" p:bauthor="吴承恩">
</bean>
4 xml注入其他类型属性
1》字面量
- null值
<!--设置null值-->
<property name="address" >
<null/>
</property>
- 属性值包含特殊符号
<!--属性值中包含特殊符号
1.把<> 进行转义 < 大于 >小于
2.把带特殊符号内容写道CDATA
-->
<property name="address" >
<value><![CDATA[<<南京>>]]></value>
</property>
5.注入属性—外部bean
-
创建两个类 Service类和Dao类
-
在service 调用dao里面的方法
-
在spring配置文件中进行配置
public class UserService { //创建UserDao类型属性,生成set方法 private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void add(){ System.out.println("service add。。。。。"); userDao.update(); //创建UserDao对象 原始方式 // UserDao userDao = new UserDaoImpl(); //接口= new 实现类 // userDao.update(); } } public interface UserDao { public void update(); } public class UserDaoImpl implements UserDao{ @Override public void update() { System.out.println("dao update。。。。."); } }
<!-- 1 . service和dao 对象创建-->
<bean id="userService" class="com.lyp.spring5.service.UserService">
<!--注入UserDao对象-->
<!--使用property完成属性注入
name: 类里面属性名称
value:向属性注入的值
ref属性:创建userDao对象bean标签id值 将外部bean 引入进来了
-->
<property name="userDao" ref="userDaoImpl"></property>
</bean>
<bean id="userDaoImpl" class="com.lyp.spring5.dao.UserDaoImpl"></bean>
测试类
public class TestBean {
@Test
public void testAdd(){
//1.加载spring 配置文件
ApplicationContext context =
new ClassPathXmlApplicationContext("bean2.xml");
//2. 获取配置创建对象
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService);
userService.add();
}
}
6.注入属性—内部bean和级联赋值
内部bean
1》一对多关系:部门和员工
一个部门有多个员工,一个员工属于一个部门
部门是一,员工是多
2》在实体类之间表示一对多的关系, 员工表示所属部门,使用对象类型属性进行表示
//部门类
public class Dept {
private String name;
public void setName(String name) {
this.name = name;
}
}
//员工类
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;
}
}
3》在sping配置文件中进行配置
<!--内部bean-->
<bean id="emp" class="com.company.bean.Emp">
<!--设置两个普通属性 -->
<property name="ename" value="lucy"></property>
<property name="gender" value="女"></property>
<!--设置对象类型属性-->
<property name="dept">
<bean id="dept" class="com.company.bean.Dept">
<property name="name" value="安保部"></property>
</bean>
</property>
</bean>
级联赋值
第一种写法
<!--级联赋值-->
<bean id="emp" class="com.company.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.company.bean.Dept">
<property name="dname" value="财务部"></property>
</bean>
第二种写法
<!-- 第二种写法-->
<bean id="emp" class="com.company.bean.Emp">
<property name="ename" value="Lili"></property>
<property name="gender" value="女"></property>
<property name="dept" ref="dept"></property>
<property name="dept.dname" value="技术部"></property>
</bean>
<bean id="dept" class="com.company.bean.Dept">
<property name="dname" value="财务部"></property>
</bean>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D8C26Jpr-1639549019998)(1Spring框架概述.assets/image-20211208211921390.png)]
7.xml注入集合属性
1》注入数组类型属性
2》注入List集合类型属性
3》注入Map集合类型属性
第一步创建类,定义数组,list,map,set类型属性,生成对应set方法
public class Stu {
//1 数组类型属性
private String[] courses;
//2 List集合类型属性
private List<String> list;
//3 Map集合类型属性
private Map<String,String> maps;
//4 set集合类型属性
private Set<String> sets;
public void setCourses(String[] courses) {
this.courses = courses;
}
public void setMaps(Map<String, String> maps) {
this.maps = maps;
}
public void setList(List<String> list) {
this.list = list;
}
public void test(){
System.out.println(Arrays.toString(courses));
System.out.println(list);
System.out.println(maps);
System.out.println(sets);
}
}
第二步 在spring配置文件中进行配置
<!--1. 集合类型属性的注入-->
<bean id="stu" class="com.lyp.collectiontype.Stu">
<!--数组类型属性注入-->
<property name="courses" >
<array>
<value>java课程</value>
<value>数据库课程</value>
</array>
</property>
<!--list类型属性注入-->
<property name="list">
<list>
<value>张三</value>
<value>张四</value>
</list>
</property>
<!--map类型属性注入-->
<property name="maps">
<map>
<entry key="JAVA" value="java"></entry>
<entry key="PHP" value="php"></entry>
</map>
</property>
<!--set类型属性注入-->
<property name="sets">
<set>
<value>MySQL</value>
<value>Redis</value>
</set>
</property>
</bean>
第三步 写一个测试类
public class TestSpring5Demo1 {
@Test
public void testCollection(){
ApplicationContext context =
new ClassPathXmlApplicationContext("bean1.xml");
Stu stu = context.getBean("stu", Stu.class);
stu.test();
}
}
4》 在集合里面设置对象类型值
<!--创建多个course对象-->
<bean id="course1" class="com.lyp.collectiontype.Course">
<property name="cname" value="Spring5框架"></property>
</bean>
<bean id="course2" class="com.lyp.collectiontype.Course">
<property name="cname" value="Mybatis框架"></property>
</bean>
<!-- 注入List集合类型,值是对象-->
<property name="courseList">
<list>
<ref bean="course1"></ref>
<ref bean="course2"></ref>
</list>
</property>
</bean>
5》把集合注入部分提取出来
- 在spring配置文件中引入名称空间 util
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
</beans>
-
使用util标签完成list集合注入提取
<!--1 提取集合类型属性注入--> <util:list id="bookList"> <value>水浒传</value> <value>西游记</value> <value>红楼梦</value> </util:list> <!--1 提取集合类型属性注入 使用--> <bean id="book" class="com.lyp.collectiontype.Book"> <property name="list" ref="bookList"></property> </bean>
8 .IOC操作Bean管理(FactoryBean)
Spring有两种类型bean,一种普通bean,另外一种工厂bean(factorybean)
- 普通bean:在配置文件中定义bean类型就是返回类型
- factorybean: 在配置文件中定义bean类型可以和返回类型不一样
第一步 创建类,让这个类作为工厂bean,实现接口FactoryBean
第二步 实现接口里面的方法,在实现方法定义返回的bean类型
//设置的对象类型是MyBean 但是返回的类型是Course
public class MyBean implements FactoryBean<Course> {
//定义返回bean
@Override
public Course getObject() throws Exception {
Course course = new Course();
course.setCname("abc");
return course;
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
return false;
}
}
@Test
public void testCollection3(){
ApplicationContext context =
new ClassPathXmlApplicationContext("bean3.xml");
Course course = context.getBean("myBean", Course.class);
System.out.println(course);
}
9. IOC操作Bean管理(bean作用域) ,singleton 和 prototype
1.在spring里面,设置创建bean实例是单实例还是多实例
2.在Spring里面,默认情况下,bean是一个单实例对象
默认是单实例对象
3.如何 设置单实例还是多实例
-
在spring配置文件bean标签里面有属性(scope) 用于设置单实例还是多实例 scope
-
scope属性值
第一个值 默认值 ,singleton,表示单实例对象
第二个值prototype,表示多实例对象
- singleton 和 prototype区别
第一 :singleton单实例 prototype多实例
第二:设置scope值是 singleton时候,加载spring配置文件时候就会创建单实例对象
设置scope值是 prototype时候, 不是在加载spring配置文件时候 创建对象 ,在调用getbean方法时候创建多实例对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CcoxOgsV-1639549020002)(1Spring框架概述.assets/image-20211209160533740.png)]
request
session
10 bean生命周期
1 生命周期
- 从对象创建到对象销毁的过程
2.bean 生命周期
1》 通过构造器创建bean实例(无参数构造)
2》为bean的属性设置值和对其他bean引用(调用set方法)
3》调用bean的初始化的方法(需要进行配置)
4》bean可以使用了(对象获取到了)
5》当容器关闭时候,调用bean的销毁方法(需要进行配置销毁的方法)
3.演示bean生命周期
public class Orders {
//无参数构造
public Orders() {
System.out.println("第一步 执行无参数构造创建bean实例");
}
private String oname;
public void setOname(String oname) {
this.oname = oname;
System.out.println("第二步 调用set方法 设置属性值" );
}
// 创建执行的初始化的方法
public void initMethod(){
System.out.println("第三步 执行初始化的方法");
}
// 创建执行销毁的方法
public void destroyMethod(){
System.out.println("第五步 执行销毁的方法");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="orders" class="com.lyp.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
<property name="oname" value="手机"></property>
</bean>
</beans>
@Test
public void testBean4(){
// ApplicationContext context =
// new ClassPathXmlApplicationContext("bean4.xml");
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("bean4.xml");
Orders orders = context.getBean("orders", Orders.class);
System.out.println("第四步 获取创建bean实例对象");
System.out.println(orders);
//手动让bean的实例销毁
// ( (ClassPathXmlApplicationContext) context).close();
context.close();
}
4.bean的后置处理器 bean的生命周期有七步
1》 通过构造器创建bean实例(无参数构造)
2》为bean的属性设置值和对其他bean引用(调用set方法)
3》把bean实例传递bean后置处理器的方法 postProcessBeforeInitialization
4》调用bean的初始化的方法(需要进行配置)
5》把把bean实例传递bean后置处理器的方法 postProcessAfterInitialization
6》bean可以使用了(对象获取到了)
7》当容器关闭时候,调用bean的销毁方法(需要进行配置销毁的方法)
5.演示添加后置处理器效果
1》创建类,实现接口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;
}
}
<!--配置后置处理器-->
<bean id="myBeanPost" class="com.lyp.bean.MyBeanPost"></bean>
11 xml自动装配
-
什么是自动装配
根据指定装配规则(属性 名称 或者属性类型), Spring自动将匹配的属性值进行注入
-
自动装配过程
1》根据属性名称自动注入
!--实现自动装配
bean标签属性autowire,配置自动装配
autowire属性常用两个值:
byName根据属性名称注入 ,注入值bean 的id值和类属性名称一致
byType根据属性类型注入
-->
<bean id="emp" class="com.lyp.autowire.Emp" autowire="byName">
<!--手动装配-->
<!-- <property name="dept" ref="dept"></property>-->
</bean>
<bean id="dept" class="com.lyp.autowire.Dept"></bean>
2》根据属性类型注入
!--实现自动装配
bean标签属性autowire,配置自动装配
autowire属性常用两个值:
byName根据属性名称注入 ,注入值bean 的id值和类属性名称一致
byType根据属性类型注入
-->
<bean id="emp" class="com.lyp.autowire.Emp" autowire="byType">
<!--手动装配-->
<!-- <property name="dept" ref="dept"></property>-->
</bean>
<bean id="dept" class="com.lyp.autowire.Dept"></bean>
12 外部属性文件
1.直接配置数据库信息
- 配置德鲁伊连接池
- 引你入德鲁伊连接池依赖jar包
<!--直接配置连接池-->
<!--<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/userDb"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>-->
2 引入外部属性文件配置数据库连接池
- 创建外部属性文件,properties格式文件,写数据库信息
- 把外部properties属性文件引入到spring配置文件中
1》引入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: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">
2》在spring配置文件使用标签引入外部属性文件
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${prop.driverClass}"></property>
<property name="url" value="${prop.url}"></property>
<property name="username" value="${prop.userName}"></property>
<property name="password" value="${prop.password}"></property>
</bean>
(4).IOC操作Bean管理(基于注解) demo3
1.什么是注解
- 注解是代码特殊标记,格式:@注解名称(属性名称= 属性值,属性名称 = 属性值。。。)
- 使用注解,注解作用在类上面,方法上面,属性上面
- 使用注解目的 : 简化xml配置
2.Spring针对Bean管理中创建对象提供注解
- @Component
- @Service 业务逻辑层
- @Controller web层
- @Repository dao层
上面四个注解功能是一样的,都可以用来创建bean实例
3.基于注解方式实现对象的创建
第一步 引入依赖
第二步 开启组件扫描
<!--开启组件扫描
1.如果扫描多个包,多个包之间使用逗号隔开
2.扫描包的上层目录 com.lyp
-->
<!--<context:component-scan base-package="com.lyp.dao,com.lyp.service"></context:component-scan>-->
<context:component-scan base-package="com.lyp"></context:component-scan>
第三步,创建类,在类上面添加创建对象注解
//在注解里面value属性值可以省略不写
//默认是类的名称,首字母小写
@Component(value = "userService") // <bean id="userService“ class=”..“/>
public class UserService {
public void add(){
System.out.println("service add....");
}
}
测试类
public class TestSpring5Demo1 {
@Test
public void testdemo1(){
ApplicationContext context =
new ClassPathXmlApplicationContext("bean1.xml");
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService);
userService.add();
}
}
4.开启组件扫描细节配置
<!--示例1
use-default-filters="false" 表示现在不适应默认filter,自己配置filter
context:include-filter,设置扫描哪些内容 //只扫描带Component注解的类
-->
<context:component-scan base-package="com.lyp" use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Component"/>
</context:component-scan>
<!--示例2
下面配置扫描包所有内容
context:exclude-filter 设置哪些内容不进行扫描
-->
<context:component-scan base-package="com.lyp">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
5 基于注解方式实现 属性注入
第一步 把service 和dao 对象创建,在service 和dao 类添加创建对象注解
第二步 在service注入 dao 对象,在service类添加dao类型属性,在属性上面使用注解
//在注解里面value属性值可以省略不写
//默认是类的名称,首字母小写
//@Component(value = "userService") // <bean id="userService“ class=”..“/>
@Service
public class UserService {
//定义dao类型属性D
//不需要添加set方法
//添加注入属性注解
@Autowired //根据类型进行注入
private Userdao userdao;
public void add(){
System.out.println("service add....");
userdao.add();
}
}
这个Qualifier 注解的使用,和上面@Autowired 一起使用
@Service
public class UserService {
//定义dao类型属性D
//不需要添加set方法
//添加注入属性注解
@Autowired //根据类型进行注入
@Qualifier(value = "userDaoImpl1") //根据名称进行注入
private Userdao userdao;
public void add(){
System.out.println("service add....");
userdao.add();
}
}
@Repository(value = "userDaoImpl1")
public class UserDaoImpl implements Userdao{
@Override
public void add() {
System.out.println("dao add ....");
}
}
// @Resource //根据类型进行注入
@Resource(name = "userDaoImpl1") //根据名称注入
private Userdao userdao;
Resource 是javax中的
import javax.annotation.Resource;
@Value(value = "abc")
private String name;
6.完全注解开发
(1) 创建配置类,替代xml配置文件
@Configuration //作为配置类,代替xml配置文件
@ComponentScan(basePackages = {"com.lyp"})
public class SpringConfig {
}
(2)编写测试类
@Test
public void testdemo2(){
//加载配置类
ApplicationContext context =
new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService);
userService.add();
}
3.Aop(面向切面编程)
1.什么是AOP (不通过修改源代码方式添加新的功能)
(1) AOP 为 Aspect Oriented Programming ,(面向切面编程),利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
(2) 通俗描述 : 不通过修改源代码方式,在主干功能里面添加新的功能
(3) 使用登录的例子说明AOP
2.AOP底层原理
1.AOP底层使用动态代理
(1) 有两种情况动态代理
第一种 有接口情况,使用JDK动态代理
- 创建UserDao接口实现类代理对象 利用代理对象增强类的方法
第二种 没有接口情况 ,使用CGLIB动态代理
- 创建子类的代理对象,增强类里面的方法
3 AOP(JDK动态代理)
1.使用JDK动态代理,使用Proxy类里面的方法创建代理对象
(1)调用newProxyInstance方法
方法里面有三个参数:
第一个参数:类加载器
第二个参数:增强方法所在的类,这个类实现的接口,支持多个接口
第三个参数:实现这个接口Interface InvocationHandler,创建代理对象,写增强的方法。
2.编写JDK 动态代理代码
(1) 创建接口,定义方法
package com.lyp;
public interface UserDao {
public int add(int a ,int b);
public String update(String id);
}
(2)创建接口实现类,实现方法
package com.lyp;
public class UserDaoImpl implements UserDao{
@Override
public int add(int a, int b) {
System.out.println("add方法执行了。。。。");
return a+b;
}
@Override
public String update(String id) {
System.out.println("update方法执行了。。。。");
return id;
}
}
(3)使用Proxy类创建接口代理对象
package com.lyp;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
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 result = dao.add(1, 2);
System.out.println("result : "+result);
}
}
//创建代理对象代码
class UserDaoProxy implements InvocationHandler{
//1 把创建的 是谁的代理对象,把谁传递过来
//有参数构造
private Object obj;
public UserDaoProxy(Object obj){
this.obj = obj ;
}
//增强的逻辑
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法之前
System.out.println("在方法之前执行。。。。"+method.getName() +"传递的参数。。。" + Arrays.toString(args));
//被增强的方法
Object res = method.invoke(obj, args);
//方法之后
System.out.println("方法之后执行" + obj);
return res;
}
}
4.AOP(术语)
1.连接点
类里面哪些方法可以被增强,这些方法称为 连接点。
2.切入点
实际被真正增强的方法,称为切入点。
3.通知(增强)
实际增强的逻辑部分称为通知(增强)
通知有多种类型
- 前置通知
- 后置通知
- 环绕通知
- 异常通知
- 最终通知
4.切面
是动作
把通知应用到切入点的过程
5、AOP操作(准备)
1.Spring框架一般基于AspectJ 实现 AOP操作
(1)什么是AspectJ
- AspectJ不是spring组成部分,独立AOP 框架,一般把AspectJ 和 Spring 框架一起使用,进行AOP操作
2.基于AspectJ实现AOP操作
(1)基于xml配置文件
(2) 基于注解方式实现(使用)
3、在项目工程里面先引入AOP相关依赖
4、切入点表达式
(1)切入点表达式作用 : 知道对哪个类里面的哪个方法进行增强
(2)语法结构 :
execution([权限修饰符] [返回类型] [类全路径] [方法名称] [参数列表])
举例1. 对 com.lyp.dao.BookDao类里面的add 进行增强
Execution(* com.lyp.dao.BookDao.add(…))
举例2. 对 com.lyp.dao.BookDao类里面的所有( *)方法进行增强
Execution(* com.lyp.dao.BookDao.*****(…))
举例3.对 com.lyp.dao包里面的类里面的所有类,类里面的所有方法 进行增强
Execution(* com.lyp.dao.*.*****(…))
5.AOP操作(AspectJ 注解) **
1、创建类, 在类里面定义方法
//被增强的类
public class User {
public void add(){
System.out.println("add...");
}
}
2.创建增强类(编写增强逻辑)
(1 )在增强类里面,创建方法,让不同方法代表不同通知类型
//增强的类
public class UserProxy {
//前置通知
public void before(){
System.out.println("before..");
}
}
3.进行通知配置
(1)在 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">
<!--开启注解扫描-->
<context:component-scan base-package="com.lyp.aopAnno"></context:component-scan>
</beans>
(2)使用注解创建User和UserProxy 对象
(3)在增强类上面添加注解@Aspect
//增强的类
@Component
@Aspect //生成代理对象
public class UserProxy {
(4)在Spring配置文件中开启生成代理对象
<!--开启Aspet 生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
4.配置不同类型的通知
(1)在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置
package com.lyp.aopAnno;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
//增强的类
@Component
@Aspect //生成代理对象
public class UserProxy {
//前置通知
//before 注解 表示作为前置通知
@Before(value = "execution(* com.lyp.aopAnno.User.add(..))")
public void before(){
System.out.println("before..。。");
}
//后置通知(返回通知)
@AfterReturning(value = "execution(* com.lyp.aopAnno.User.add(..))")
public void AfterReturning(){
System.out.println("AfterReturning..。。");//在返回值后执行
}
//最终通知
@After(value = "execution(* com.lyp.aopAnno.User.add(..))")
public void After(){
System.out.println("After..。。");//在方法执行后执行
}
//异常通知
@AfterThrowing(value = "execution(* com.lyp.aopAnno.User.add(..))")
public void AfterThrowing(){
System.out.println("AfterThrowing..。。");
}
//环绕通知
@Around(value = "execution(* com.lyp.aopAnno.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("环绕之前");
//被增强的方法执行
proceedingJoinPoint.proceed();
System.out.println("环绕之后");
}
}
5.相同的切入点抽取
//相同切入点抽取
@Pointcut(value = "execution(* com.lyp.aopAnno.User.add(..))")
public void pointdemo(){
}
//前置通知
//before 注解 表示作为前置通知
@Before(value = "pointdemo()")
public void before(){
System.out.println("before..。。");
}
6.有多个增强类对同一个方法进行增强,设置增强类优先级
(1)在增强类上面添加注解@Order(数字类型值),数字类型值越小优先级越高
@Component
@Aspect
@Order(1)
public class PersonProxy {
7.完全使用注解开发
(1)创建配置类,不需创建xml配置文件
@Component
@ComponentScan(basePackages = {"com.lyp"}) //扫描
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop {
}
6 AOP操作(AspectJ配置文件)
1.创建两个类,增强类和被增强类,创建方法
2.在spring配置文件中创建两个类对象
<!-- 创建对象-->
<bean id="book" class="com.lyp.aopxml.Book"></bean>
<bean id="bookProxy" class="com.lyp.aopxml.BookProxy"></bean>
3.在spring配置文件中配置切入点
<!-- 配置aop增强-->
<aop:config>
<!--切入点 -->
<aop:pointcut id="p" expression="execution(* com.lyp.aopxml.Book.buy(..))"/>
<!--配置切面 -->
<aop:aspect ref="bookProxy">
<!--增强作用在具体的方法上-->
<aop:before method="before" pointcut-ref="p"/>
</aop:aspect>
</aop:config>
测试
@Test
public void testAopXML(){
ApplicationContext context =
new ClassPathXmlApplicationContext("bean2.xml");
Book book = context.getBean("book", Book.class);
book.buy() ;
}
4. JdbcTemplate
1. JdbcTemplate(概念和准备)
1.什么是JdbcTemplate (Template模板)
(1)spring 框架对 JDBC进行封装,使用 JdbcTemplate 方便实现对数据库操作
2.准备工作
(1)引入相关jar包
(2)在spring 配置文件中配置数据库连接池
<!--德鲁伊数据库连接池-->
<!--数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<property name="url" value="jdbc:mysql://localhost:3306/user_db?serverTimezone=GMT%2B8&useSSL=false&useUnicode=true&characterEncoding=UTF-8&allowPublicKeyRetrieval=true"></property>
<property name="username" value="root"></property>
<property name="password" value="000000"></property>
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
</bean>
(3)配置JdbcTemplate 对象,注入DataSource
<!-- JdbcTemplate 对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入dataSource-->
<property name="dataSource" ref="dataSource"></property>
</bean>
(4) 创建service类,创建dao类,在dao类注入jdbcTemplate 对象
- 配置文件中
<!--组件扫描-->
<context:component-scan base-package="com.lyp"></context:component-scan>
- Service 注入dao
@Service
public class BookService {
//注入dao
@Autowired
private BookDao bookDao;
}
- Dao 注入JdbcTemplate
@Repository
public class BookDaoImpl implements BookDao{
//注入JdbcTemplate
@Autowired
private JdbcTemplate jdbcTemplate;
}
2.JdbcTemplate操作数据库(添加)
1.对应数据库创建实体类
遇到的question
如果步对应在查询时会出现jdbcTempalte查询为null的情况
2.编写service和dao
(1)在dao进行数据库添加操作
@Repository
public class BookDaoImpl implements BookDao{
//注入JdbcTemplate
@Autowired
private JdbcTemplate jdbcTemplate;
//添加的方法
@Override
public void add(Book book) {
//1 创建sql语句
String sql = "insert into t_book values(?,?,?)";
//2 调用方法实现
Object[] args = {book.getUserId(), book.getUsername(), book.getUstatus()};
int update = jdbcTemplate.update(sql,args);
// int update = jdbcTemplate.update(sql, book.getUserId(), book.getUsername(), book.getUstatus());
System.out.println(update);
}
}
(2)调用JdbcTemplate对象里面update 方法实现添加操作
- 有两个参数
- 第一参数: sql语句
- 第二个参数: 可变参数 ,设置sql语句值
3.测试类
public class TestBook {
@Test
public void testAdd(){
ApplicationContext context =
new ClassPathXmlApplicationContext("bean1.xml");
BookService bookService = context.getBean("bookService", BookService.class);
Book book = new Book();
book.setUserId("1");
book.setUsername("java");
book.setUstatus("a");
bookService.addBook(book);
}
}
3.JdbcTemplate操作数据库(修改和删除)
//修改的方法
@Override
public void updateBook(Book book) {
String sql = "update t_book set user_name=?,ustatus=? where user_id=?";
Object[] args = {book.getUsername(), book.getUstatus(), book.getUserId()};
//注意 三者的顺序
int update = jdbcTemplate.update(sql, args);
System.out.println(update);
}
//删除的方法
@Override
public void delete(String id) {
String sql = "delete from t_book where user_id=?";
int update = jdbcTemplate.update(sql, id);
System.out.println(update);
}
测试类
package com.lyp.dao.test;
import com.lyp.entity.Book;
import com.lyp.service.BookService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestBook {
@Test
public void testAdd(){
ApplicationContext context =
new ClassPathXmlApplicationContext("bean1.xml");
BookService bookService = context.getBean("bookService", BookService.class);
// //添加
// Book book = new Book();
// book.setUserId("1");
// book.setUsername("java");
// book.setUstatus("a");
// bookService.addBook(book);
// //修改
// Book book = new Book();
// book.setUserId("1");
// book.setUsername("javaupup");
// book.setUstatus("a");
// bookService.updateBook(book);
//删除
bookService.delete("1");
}
}
4、JdbcTemplate操作数据库(查询功能-返回某个值)
1.查询表里面有多少条记录,返回是某个值
2.使用JdbcTemplate 实现查询返回某个值代码
- 有两个参数
- 一个参数:sql语句
- 第二个参数:返回类型Class
//查询表中记录数
@Override
public int selectCount() {
String sql = "SELECT COUNT(*) FROM t_book";
Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
return count;
}
4、JdbcTemplate操作数据库(查询返回对象)
1.场景:查询图书详情
- JdbcTemplate 实现查询返回对象
- 三个参数
- 第一个sql语句
- 第二个RowMapper,是接口,针对返回不同类型的数据,使用这个接口里面实现类完成数据封装
- 第三个 sql语句值
//查询返回对象值
@Override
public Book findBookInfo(String id) {
String sql = "SELECT * FROM t_book where user_id=?";
//调用方法
Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), id);
return book;
}
4、JdbcTemplate操作数据库(查询返回集合)
1.场景:查询图书列表分页。。
2.调用JdbcTemplate 方法实现查询返回集合
- 三个参数
- 第一个sql语句
- 第二个RowMapper,是接口,针对返回不同类型的数据,使用这个接口里面实现类完成数据封装
- 第三个 sql语句值
5JdbcTemplate操作数据库(批量操作)
1.JdbcTemplate操作数据库(批量添加操作)
1.批量操作:操作表里面多条记录
2.JdbcTemplate实现批量添加操作
- 两个参数
- 第一个 sql语句
- 第二个:List集合,添加多条记录数据
//批量添加
@Override
public void batchAddBook(List<Object[]> batchArgs) {
String sql = "insert into t_book values(?,?,?)";
int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
System.out.println(Arrays.toString(ints));
}
测试类
//批量添加
List<Object[]> batchArgs = new ArrayList<>();
Object[] o1= {"4","php","b"};
Object[] o2= {"5","c","d"};
Object[] o3= {"6","c++","a"};
batchArgs.add(o1);
batchArgs.add(o2);
batchArgs.add(o3);
//调用批量添加
bookService.batchAdd(batchArgs);
}
2.JdbcTemplate操作数据库(批量修改操作)
//批量修改
@Override
public void batchUpdateBook(List<Object[]> batchArgs) {
String sql = "update t_book set user_name=?,ustatus=? where user_id=?";
int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
System.out.println(ints);
}
3.JdbcTemplate操作数据库(批量删除操作)
//批量删除
List<Object[]> batchArgs = new ArrayList<>();
Object[] o1= {"4"};
Object[] o2= {"5"};
batchArgs.add(o1);
batchArgs.add(o2);
//调用方法实现批量删除
bookService.batchDelete(batchArgs);
5.事务概念
1.什么是事务
(1)事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败
(2)典型场景:银行转账
-
lucy转账100给mary
-
lucy少100 mary多100
2.事物四个特性(ACID)
(1)原子性 : 不可分割,要么都失败要么都成功
(2)一致性 :操作之前和操作之后总量是不变的
(3)隔离性:多事务进行操作时,彼此不会产生影响
(4)持久性:事物提交后,表中发生变化
3.事务操作(搭建事物操作环境)
1、创建数据库表,添加记录
2、创建service,创建dao,完成对象创建和注入关系
(1)在 service 注入 dao, 在 dao 注入JdbcTemplate ,在JdbcTemplate 注入DataSource
@Service
public class UserService {
//注入dao
@Autowired
private UserDao userDao;
}
@Repository
public class UserDaoImpl implements UserDao{
//注入JdbcTemplate
@Autowired
private JdbcTemplate jdbcTemplate;
}
<!--组件扫描-->
<context:component-scan base-package="com.lyp"></context:component-scan>
<!--数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<property name="url" value="jdbc:mysql://localhost:3306/user_db?serverTimezone=GMT%2B8&useSSL=false&useUnicode=true&characterEncoding=UTF-8&allowPublicKeyRetrieval=true"></property>
<property name="username" value="root"></property>
<property name="password" value="000000"></property>
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
</bean>
<!-- JdbcTemplate 对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入dataSource-->
<property name="dataSource" ref="dataSource"></property>
</bean>
3、在dao创建两个方法: 多钱和少钱的方法,在service 创建方法(转账的方法)
//多钱
@Override
public void addMoney() {
String sql = "update t_account set money=money+? where username=?";
jdbcTemplate.update(sql,100,"mary");
}
//lucy转账100给mary
//少钱
@Override
public void reduceMoney() {
String sql = "update t_account set money=money-? where username=?";
jdbcTemplate.update(sql,100,"lucy");
}
@Service
public class UserService {
//注入dao
@Autowired
private UserDao userDao;
//转账的方法
public void accountMoney(){
//lucy少100
userDao.reduceMoney();
//mary多100
userDao.addMoney();
}
}
测试类
@Test
public void testAccount(){
ApplicationContext context =
new ClassPathXmlApplicationContext("bean1.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.accountMoney();
}
4.上面银行转账代码,如果正常执行没有问题,但是如果执行过程中出现异常,有问题
(1)上面的问题如果解决?
- 使用事物进行解决
(2)事物操作过程
4.事务操作(Spring 事务管理介绍)
1、事务添加到JavaEE 三层结构里面Service层(业务逻辑层)
2.在Sping 进行事务管理操作
(1)有两种方式:编程式事务管理 和 声明式事务管理(使用)
3.声明式事务管理
(1)基于注解方式
(2)基于xml 配置文件方式
4.在Spring进行声明式事务管理,底层使用AOP原理
5.Spring事务管理API
(1) 提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类
5.事务操作(注解声明式事务管理)@Transactional**
1、在Spring 配置文件中 配置事务管理器
<!--创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
2、在Spring 配置文件中,开启事务注解
(1)在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:aop="http://www.springframework.org/schema/aop"
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/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
(2)开启事务注解
<!--开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
3、在Service类上面 (获取service 类里面方法上面) 添加事务注解
(1)@Transactional ,这个注解添加到类上面,也可以添加到方法上面
(2) 如果把这个注解添加类上面,这个类里面所有的方法都添加事务
(3)如果把这个注解添加方法上面,为这个方法添加事务
@Service
@Transactional
public class UserService {
6.事务操作(声明式事务管理参数配置)
1、在Service类上面添加注解**@Transactional** ,在这个注解里面可以配置事务相关参数,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O6xYgUAc-1639549020031)(1Spring框架概述.assets/image-20211212192045562.png)]
2、propagation:事务传播行为
(1)多事务方法直接进行调用,这个过程中事务时如何进行管理的
以上两种最常见
@Service
@Transactional(propagation = Propagation.REQUIRED)
public class UserService {
3、isolation:事务隔离级别
(1)**事务有特性称为隔离性,多事务操作之间不会产生影响。**不考虑隔离性产生很多问题。
(2)有三个读问题:脏读、(虚)幻读、不可重复读
(3)脏读:一个未提交事务读取到另一个未提交事务的数据 是一种致命问题
在事务回滚前读到修改的数据
(4)幻读:一个未提交事务读取到另一提交事务添加的数据
(5)不可重复读:一个未提交事务读取到另一个提交事务修改数据 是一种现象
(6)通过设置事务隔离级别,解决读问题
@Service
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
public class UserService {
默认 :REPEATABLE_READ
4.timeout:超时时间
(1)事务需要在一定时间内进行提交,如果不提交进行回滚
(2)默认值时-1,设置时间以秒为单位进行计算
@Service
@Transactional(timeout = -1,propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
public class UserService {
5.readyOnly:是否只读
(1)读:查询操作,写:添加修改删除操作
(2)readyOnly 默认值是false,表示可以查询,可以添加修改删除
(3)设置readyOnly 值是true,设置成true后,只能是查询
@Transactional( readOnly = false,timeout = -1,propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
6.rollbackFor:回滚
(1)设置出现哪些异常进行事务回滚
7.noRollbackFor:不回滚
(1)设置出现哪些异常不进行事务回滚
7.事务操作(XML声明式事务管理)*
1、在spring 配置文件 中进行配置
第一步 配置事务管理器
第二步 配置通知
第三步 配置切入点和 切面
<!-- 1.创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--2 配置通知-->
<tx:advice id="txadvice">
<!--配置事务参数-->
<tx:attributes>
<!-- 指定在哪种规则的方法上面添加事务 -->
<tx:method name="accountMoney"propagation="REQUIRED"/>
<!-- <tx:method name="account*"/>-->
</tx:attributes>
</tx:advice>
<!--3 配置切入点和切面-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pt" expression="execution(* com.lyp.service.UserService.*(..))"/>
<!--配置切面-->
<aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
</aop:config>
8.事务操作(完全注解声明式事务管理)
1、创建配置类,使用配置类替代xml 配置文件
@Configuration//配置类
@ComponentScan(basePackages ="com.lyp" ) //开启组件扫描
@EnableTransactionManagement //开启事务
public class TxConfig {
//创建数据库连接池
@Bean
public DruidDataSource getDruidDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/user_db");
dataSource.setUsername("root");
dataSource.setPassword("000000");
return dataSource;
}
//创建JdbcTemplate 对象
@Bean
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;
}
}
测试类
@Test
public void testAccount3(){
ApplicationContext context =
new AnnotationConfigApplicationContext(TxConfig.class);
UserService userService = context.getBean("userService", UserService.class);
userService.accountMoney();
}
6.Spring5框架新功能
1、整个Spring5框架的代码基于java8 ,运行时兼容 JDK9,许多不建议使用的类和方法在代码库中删除
2、Spring5框架自带了通用的日志封装
(1)Spring5 已经移除Log4jConfigListener,官方Log4j2
(2)Spring5 框架整合Log4j2
第一步 引入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>
3、Spring5框架核心容器支持 @Nullable注解*
(1) @Nullable 注解可以使用在方法上面、属性上面、参数上面,表示方法返回可以为空,属性值可以为空,参数值可以为空
(2)注解用在方法上面,方法返回值可以为空
(3)注解用在方法的参数里面,方法参数可以为空
(4)注解用在属性上面,属性值可以为空
4、Spring5 核心容器支持 函数式风格 GenericApplicationContext*
//函数式风格创建对象,交给spring进行管理
@Test
public void testAccount4(){
//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.lyp.test.User");
User user = (User)context.getBean("user1");
System.out.println(user);
}
7、Spring5新功能-webflux
1.SpringWebFlux介绍
(1)是Spring5台南佳的新模块,用于web开发的,功能类似SpringMVC,WebFlux使用当前比较流行的响应式编程出现的框架
(2)使用传统web框架,比如SpringMVC,这些基于Servlet容器,Webflux是一种异步非阻塞的框架,异步非阻塞的框架在Servlet 3.1以后才支持,核心是基于Reactor 的相关API实现的。
(3)解释什么是异步非阻塞
- 异步 同步 针对调用者,调用者发送请求,如果等着对方回应之后才去做其他事情就是同步;如果发送请求之后不等着对方回应就去做其他事情就是异步。
- 阻塞和非阻塞 针对被调用者 , 被调用者收到请求后,做完请求任务之后才给出反馈就是阻塞,受到请求之后马上给出反馈然后再去做事情就是非阻塞。
上面都是针对对象不一样
(4)Webflux 特点:
第一:非阻塞式: 再有限资源下,提高系统吞吐量和伸缩性,以Reactor为基础实现响应式编程
第二:函数式编程:spring5框架基于java8,Webflux使用Java8函数式编程方式实现路由请求
(5)比较SpringMVC
1》两个框架都可以使用注解方式,都运行再Tomcat等容器中
2》SpringMVC采用命令式编程,Webflux采用异步响应式编程
2.响应式编程
(1)什么是响应式编程
简称RP(Reactive Programming)
响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。
例如,对于 a=b+c 这个表达式的处理,在命令式编程中,会先计算 b+c 的结果,再把此结果赋值给 变量a,因此 b,c 两值的变化不会对 变量a 产生影响。但在响应式编程中,变量a 的值会随时跟随 b,c 的变化而变化。
电子表格程序就是响应式编程的一个例子。单元格可以包含字面值或类似"=B1+C1"的公式,而包含公式的单元格的值会依据其他单元格的值的变化而变化。
响应式编程最初是为了简化交互式用户界面的创建和实时系统动画的绘制而提出来的一种方法,但它本质上是一种通用的编程范式。
例如,在MVC软件架构中,响应式编程允许将相关模型的变化自动反映到视图上,反之亦然。
(2)java8 及其之前版本
-
提供的观察者模式两个类Observer 和Observable
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.7</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.lyp</groupId> <artifactId>demoreactor8</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demoreactor8</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <!-- 打包插件 --> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
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();//通知 } }2.响应式编程(**Reactor**实现)
3.响应式编程(Reactor实现)
(1)响应式编程操作中,Reactor是满足Reactive 规范框架
(2)Reactor 有两个核心类,Mono和Flux,这两个类实现接口 Publisher ,提供丰富操作符。Flux对象实现发布者,返回N个元素;Mono实现发布者,返回0或者1个元素。
(3)Flux 和 Mono都是数据流的发布者,使用Flux和Mono都可以发出三种数据信号:
元素值,错误信号,完成信号,
错误信号和完成信号都代表终止信号,终止信号用于告诉订阅者数据流结束了,错误信号终止数据流同时把错误 信息传递给订阅者。
(4)代码演示Flux和Mon
第一步 引入依赖
<!--引入reactor依赖-->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.1.5.RELEASE</version>
</dependency>
第二步 编写代码
public class TestReactor {
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<Integer> stream = list.stream();
Flux.fromStream(stream);
}
}
(5)三种信号特点
1》错误信号和完成信号都是终止信号,不能共存的,
2》如果没有发送任何元素值,而是直接发送错误或者完成信号,表示是空数据流
3》如果没有错误信号,没有完成信号,表示是无线数据流
(6)调用**just **或者其他方法只是声明数据流,数据流并没有发出,只有进行订阅后才会触发数据流,不订阅什么都不会发生的
(7)操作符
-
对数据流进行一道道操作,成为操作符,比如工厂流水线
-
第一 map :元素映射为新的元素
-
flatMap :元素映射为流
把每个元素先转成流,把转换后的流合并成大的流。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SAooAy87-1639549020049)(1Spring框架概述.assets/image-20211213213527279.png)]
4.WebFlux执行流程和核心API
SpringWebflux基于Reactor,默认容器时Netty,Netty是高性能的NIO框架,异步非阻塞的框架
(1)Netty
- BIO 阻塞方式
- NIO 异步非阻塞
(2)SpringWebFlux 执行过程和SpingMVC 相似的
- SpringWebFlux 核心控制器 DispatchHandler ,实现接口WebHandle
- 接口WebHandle有一个方法 handle
(3)SpringWebFlux 里面 DispatchHandler ,负责请求的处理
- HandlerMapping: 请求查询到处理的方法
- HandlerAdapter: 真正负责请求处理
- HandlerResultHandler : 响应结果处理
(4) SpringWebFlux 实现函数式编程,两个接口 :RouterFunction (路由处理) 和HandlerFunction(处理函数)
5.SpringWebFlux(基于注解编程模型)
SpringWebFlux 实现方式有两种:注解编程模型 和 函数式编程模型
使用注解编程模型方式,和之前SpringMVC 使用相似的,只需要把相关依赖配置到项目中,SpringBoot自动配置相关运行容器,默认情况下使用 Netty 服务器
第一步 创建SpringBoot工程 ,引入WebFlux依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
第二步配置启动端口号
第三步
- 创建包和相关类
- 创建接口定义操作的方法
package com.lyp.webfluxdemo1.service;
import com.lyp.webfluxdemo1.entity.User;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
//用户操作接口
public interface UserService {
//根据id查询用户
Mono<User> getUserById(int id);
//查询所有用户
Flux<User> getAllUser();
//添加用户
Mono<Void> saveUserInfo(Mono<User> user);
}
接口实现类
public class UserServiceImpl implements UserService{
//创建map集合存储数据
private final Map<Integer,User> users = new HashMap<>();
public UserServiceImpl() {
this.users.put(1,new User("lucy", "男", 20));
this.users.put(2,new User("mary", "女", 21));
this.users.put(3,new User("nana", "男", 24));
}
//根据id查询
@Override
public Mono<User> getUserById(int id) {
return Mono.just(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;//防止id值重复
users.put(id,person);
}).thenEmpty(Mono.empty());
}
}
- 创建controller
@RestController
public class UserController {
//注入service
@Autowired
private UserService userService;
//id查询
@GetMapping("/user/{id}")
public Mono<User> getUserId(@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);
}
}
实现:
- 说明 1
SpringMVC方式实现,同步阻塞的方式,基于SpringMVC+Servlet+Tomcat
SpringWebFlux方式实现,异步非阻塞方式,基于SpringWebFlux+Reactor+Netty
6.SpringWebFlux(基于函数式编程模型)
(1)再使用函数式编程模型操作的时候,需要自己初始化服务器
(2)基于函数式编程模型时候,有两个核心接口:
RouterFuction(实现路由功能,请求转发给对应的handler) 和 HandleFuction (处理请求生成响应的函数)。核心任务做定义两个函数式接口的实现并且启动需要的服务器
(3)SpringWebFlux 请求和响应不再是ServletRequest 和 ServletResponce, 而是ServerRequest和 ServerResponce
第一步把注解编程模型工程复制一份
第二步 创建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((BodyInserter<?, ? super ServerHttpResponse>) person))
.switchIfEmpty(notFound);
}
//查询所有
public Mono<ServerResponse> getAllUsers(){
//调用service得到结果
Flux<User> users = this.userService.getAllUser();
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
.body(users,User.class);
}
//添加
public Mono<ServerResponse> saveUser(ServerRequest request){
Mono<User> userMono = request.bodyToMono(User.class);
return ServerResponse.ok().build(this.userService.saveUserInfo(userMono));
}
}
第三步初始化服务器,编写Router
- 创建路由的方法
//1.创建Router路由
public RouterFunction<ServerResponse> routerFunction(){
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 creatReactorserver(){
//路由和handler适配
RouterFunction<ServerResponse> route= routerFunction();
HttpHandler httpHandler = toHttpHandler(route);
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
//创建服务器
HttpServer httpSever = HttpServer.create();
httpSever.handle(adapter).bindNow();
}
- 最终调用
//最终调用
public static void main(String[] args) throws Exception {
Server server =new Server();
server.creatReactorserver();
System.out.println("enter to exiy");
System.in.read();
}
(4)使用WebClient
public class Client {
public static void main(String[] args) {
//调用服务器地址
WebClient webClient = WebClient.create("http://127.0.0.1:57994]]");
//根据id查询
String id = "1";
User userresult = webClient.get().uri("]users/id", id).accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(User.class)
.block();
System.out.println(userresult.getName());
//查询所有
Flux<User> results = webClient.get().uri("/users/")
.accept(MediaType.APPLICATION_JSON).retrieve().bodyToFlux(User.class);
results.map(stu -> stu.getName()).buffer().doOnNext(System.out::println).blockFirst();
}
}
总结
1.spring 框架概述
Spring是轻量级的开源的JavaEE框架,解决企业应用开发的复杂性,两个核心部分 : IOC 和 AOP
SPring5.2.9版本
2.IOC容器
(1)IOC底层原理(工厂 、反射等)
(2)IOC接口 (BeanFactory)
(3)IOC操作Bean管理(基于xml)
(4)IOC操作Bean管理(基于注解)
3.AOP
(1)AOP 底层原理:动态代理,有接口情况(使用JDK动态代理),没有接口(CGLIB动态代理)
(2)术语 切入点、增强(通知),切面
(3)基于AspectJ 实现AOP操作
4. JdbcTemplate
(1)使用 JdbcTemplate 实现数据库 curd 操作
(2)使用 JdbcTemplate 实现数据库批量操作
5 事务管理
(1)事务概念
(2)重要概念 :传播行为和隔离级别
(3)基于注解实现声明式事务管理
(4)完全注解方式实现声明式事务管理
6.Spring5 新功能
(1)整合日志框架
(2)@Nullable注解
(3)函数式注册对象
(4)整合Junit5 单元测试框架
(5)SpringWebFlux使用
Service.getAllUser();
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
.body(users,User.class);
}
//添加
public Mono<ServerResponse> saveUser(ServerRequest request){
Mono<User> userMono = request.bodyToMono(User.class);
return ServerResponse.ok().build(this.userService.saveUserInfo(userMono));
}
}
第三步初始化服务器,编写Router
* 创建路由的方法
```java
//1.创建Router路由
public RouterFunction<ServerResponse> routerFunction(){
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 creatReactorserver(){
//路由和handler适配
RouterFunction<ServerResponse> route= routerFunction();
HttpHandler httpHandler = toHttpHandler(route);
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
//创建服务器
HttpServer httpSever = HttpServer.create();
httpSever.handle(adapter).bindNow();
}
- 最终调用
//最终调用
public static void main(String[] args) throws Exception {
Server server =new Server();
server.creatReactorserver();
System.out.println("enter to exiy");
System.in.read();
}
(4)使用WebClient
public class Client {
public static void main(String[] args) {
//调用服务器地址
WebClient webClient = WebClient.create("http://127.0.0.1:57994]]");
//根据id查询
String id = "1";
User userresult = webClient.get().uri("]users/id", id).accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(User.class)
.block();
System.out.println(userresult.getName());
//查询所有
Flux<User> results = webClient.get().uri("/users/")
.accept(MediaType.APPLICATION_JSON).retrieve().bodyToFlux(User.class);
results.map(stu -> stu.getName()).buffer().doOnNext(System.out::println).blockFirst();
}
}
总结
1.spring 框架概述
Spring是轻量级的开源的JavaEE框架,解决企业应用开发的复杂性,两个核心部分 : IOC 和 AOP
SPring5.2.9版本
2.IOC容器
(1)IOC底层原理(工厂 、反射等)
(2)IOC接口 (BeanFactory)
(3)IOC操作Bean管理(基于xml)
(4)IOC操作Bean管理(基于注解)
3.AOP
(1)AOP 底层原理:动态代理,有接口情况(使用JDK动态代理),没有接口(CGLIB动态代理)
(2)术语 切入点、增强(通知),切面
(3)基于AspectJ 实现AOP操作
4. JdbcTemplate
(1)使用 JdbcTemplate 实现数据库 curd 操作
(2)使用 JdbcTemplate 实现数据库批量操作
5 事务管理
(1)事务概念
(2)重要概念 :传播行为和隔离级别
(3)基于注解实现声明式事务管理
(4)完全注解方式实现声明式事务管理
6.Spring5 新功能
(1)整合日志框架
(2)@Nullable注解
(3)函数式注册对象
(4)整合Junit5 单元测试框架
(5)SpringWebFlux使用