目录
7.2、普通bean:在配置文件中定义bean的类型就是返回类型
7.3、工厂bean:在配置文件中定义bean的类型可以和返回类型不一样
(3)@Resource:可以根据类型注入,可以根据名称注入。
15.1、使用JDK动态代理,使用Proxy类里面的方法创建代理对象
17.1、Spring框架一般都是基于AspectJ实现AOP操作
18.6、有多个增强类对同一个方法进行增强,可以设置增强类的优先级
28.3、在service类上(或者service内的方法上)添加事务注解
1、整个Spring5框架的代码基于Java8,运行时兼容JDK9,许多不建议使用的类和方法在代码中删除。
3、Spring5.0框架核心容器支持@Nullable注解
4、Spring5.0框架核心容器支持函数式风格GenericApplicationContext
1、Spring框架概述
1.1、Spring是轻量级的开源的JavaEE框架
1.2、Spring可以解决企业级应用开发的复杂性
1.3、Spring有两个核心部分:IOC和AOP
(1)IOC:控制反转,把创建对象过程交给Spring进行管理
(2)Aop:面向切面,不修改源码进行功能增强
1.4、Spring特点
(1)方便解耦,简化开发
(2)Aop编程支持
(3)方便程序测试
(4)方便和其他框架进行整合
(5)方便进行事务操作
(6)降低API开发难度
1.5、现在选取Spring5.X来学习
2、IOC(概念和原理)
2.1、什么是IOC
- 控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理
- 使用IOC目的:为了耦合度降低
2.2、IOC底层原理
- xml解析、工厂模式、反射
第一步:xml配置文件,配置创建的对象
<bean id = "dao" class ="com.atguigu.UserDao"/>
第二步:有service类和dao类,创建工厂类
class UserFactory{
public static UserDao getDao(){
String classValue = class属性值; //1、xml解析
Class clazz = Class.forName(classValue );//2、通过反射创建对象
return (UserDao)clazz.newInstance();
}
}
3、IOC(接口)
3.1、IOC思想基于IOC容器完成,IOC容器底层就是对象工厂。
3.2、Spring就是一种提供IOC容器的框架,它提供容器的两种实现方式:(即两个接口)
- BeanFactory:IOC容器基本实现,是Spring内部的使用接口,不提供给开发人员使用。
- 加载配置文件时不会创建对象,在获取对象(或使用)才创建。
- ApplicationContext:BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员使用。
- 加载配置文件时就会把配置文件中的对象创建。
3.3、ApplicationContext接口有实现类
FileSystemXmlApplicationContext:根据xml文件在系统中的绝对路径。
ClassPathXmlApplicationContext:根据xml文件在类路径下的相对路径。
4、IOC操作Bean管理(概念)
1、什么是Bean管理
(0)Bean管理指的是2个操作
(1)Spring创建对象
(2)Spring注入属性
2、Bean管理操作有两种方式
(1)基于xml配置文件方式实现。
(2)基于注解方式实现。
5、IOC操作Bean管理(基于xml方式)
5.1、基于xml方式创建对象
<bean id="user" class="com.atguigu.spring5.User"/>
(1)在spring配置文件中, 使用bean标签,标签里面添加对应属性,就可以实现对象创建
(2)在bean标签中有很多属性,常用的属性有
- id属性:唯一标识
- class属性:类全路径(包类路径)
(3)创建对象时,默认执行无参构造方法
5.2、基于xml方式注入属性
(1)DI:依赖注入,就是注入属性
面试题:DI和IOC有什么区别?
DI是IOC中的一种具体实现,表示依赖注入,也就是注入属性,需要在创建对象的基础上完成。
5.2.1、第一种注入方式:使用set方法进行注入
(1)创建类,定义属性,创建对应的set方法。
/**
* 演示使用set方法进行注入属性
*/
public class Book {
//创建属性
private String bname;
private String bauthor;
//创建属性对应的set方法
public void setBname(String bname) {
this.bname = bname;
}
public void setBauthor(String bauthor) {
this.bauthor = bauthor;
}
}
(2)在spring配置文件中,配置对象创建,配置属性注入。
<!--2 set方法注入属性-->
<bean id="book" class="com.atguigu.spring5.Book">
<!-- 使用proptertiy完成属性注入
name:类里面属性名称
value:向属性注入的值
-->
<property name="bname" value="易筋经"/>
<property name="bauthor" value="达摩老祖"/>
</bean>
5.2.2、第二种注入方式:使用有参数构造进行注入
(1)创建类,定义属性,创建属性对应有参构造方法。
/**
* 使用有参构造注入
*/
public class Orders {
//创建属性
private String oname;
private String address;
//创建属性有参构造
public Orders(String oname, String address) {
this.oname = oname;
this.address = address;
}
}
(2)在spring配置文件中,进行有参构造配置。
<!--3 有参构造注入-->
<bean id="orders" class="com.atguigu.spring5.Orders">
<constructor-arg name="oname" value="电脑"/>
<constructor-arg name="address" value="China"/>
</bean>
5.2.3、p名称空间注入(了解)
(1)使用p名称空间注入,可以简化基于xml配置方式,底层还是使用set方法注入。
第一步,导入p名称空间约束。
第二步,直接在bean标签中进行属性注入。
<bean id="book" class="com.atguigu.spring5.Book" p:bname="易筋经" p:bauthor="达摩老祖">
</bean>
5、IOC操作Bean管理(xml方式注入其他类型属性)
5.1、字面量(Null和特殊符号)
(1)null
<!--注入null值-->
<property name="address">
<null/>
</property>
(2)属性值包含特殊符号
处理方式一:把<>进行转义 < >
处理方式二:把带特殊符号内容写到CDATA
<!--属性值带特殊符号
解决方法1:对<>进行转义 < >
解决方法2:把带特殊符号内容写到CDATA
-->
<property name="address">
<value><![CDATA[<<南京>>]]></value>
</property>
5.2、注入属性-外部bean
(1)创建两个类 service类和dao类。
(2)在service中调用dao里面的方法。
(3)在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();
}
}
<!--1 service 和 dao 对象创建-->
<bean id="userService" class="com.atguigu.spring5.service.UserService">
<!--注入userDao对象
name属性:类里属性名称
ref属性:创建userDao对象bean标签的值
-->
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" class="com.atguigu.spring5.dao.UserDaoImpl"/>
5.3、注入属性-内部bean
(1)一对多关系:部门和员工
一个部门有多个员工,一个员工属于一个部门;部门是一,员工是多。
(2)在实体类之间表示一对多关系:员工表示所属部门,使用对象类型属性表示
/**
* 部门类
*/
public class Dept {
private String dname;
public void setDname(String dname) {
this.dname = dname;
}
}
/**
* 员工类
*/
public class Emp {
private String ename;
private String gender;
//员工属于某一个部门,使用对象表示
private Dept dept;
public void setEname(String ename) {
this.ename = ename;
}
public void setGender(String gender) {
this.gender = gender;
}
public void setDept(Dept dept) {
this.dept = dept;
}
}
(3)在spring配置文件中进行配置
<!--内部bean-->
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
<!-- 设置两个普通属性 -->
<property name="ename" value="Lucy"/>
<property name="gender" value="女"/>
<!-- 设置对象类型属性 -->
<property name="dept">
<bean id="dept" class="com.atguigu.spring5.bean.Dept">
<property name="dname" value="安保部"/>
</bean>
</property>
</bean>
5.4、注入属性--级联赋值
(1)第一种写法
<!--级联赋值-->
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
<!-- 设置两个普通属性 -->
<property name="ename" value="Lucy"/>
<property name="gender" value="女"/>
<!-- 设置对象类型属性 -->
<!--级联赋值-->
<property name="dept" ref="dept"/>
</bean>
<bean id="dept" class="com.atguigu.spring5.bean.Dept">
<property name="dname" value="财务部"/>
</bean>
(2)第二种写法
<!--级联赋值-->
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
<!-- 设置两个普通属性 -->
<property name="ename" value="Lucy"/>
<property name="gender" value="女"/>
<!-- 设置对象类型属性 -->
<!--级联赋值-->
<property name="dept" ref="dept"/>
<property name="dept.dname" value="技术部"/>
</bean>
6、IOC操作Bean管理(xml方式注入集合属性)
6.1、注入数组类型属性
6.2、注入List集合类型属性
6.3、注入Map集合类型属性
6.4、注入Set集合类型属性
(1)创建类,定义数组、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 setList(List<String> list) {
this.list = list;
}
public void setMaps(Map<String, String> maps) {
this.maps = maps;
}
public void setSets(Set<String> sets) {
this.sets = sets;
}
}
(2)在spring配置文件进行配置:
<!--集合类型属性注入-->
<bean id="stu" class="com.chenp.spring5.collectiontype.Stu">
<!--1.数组类型注入-->
<property name="courses">
<array>
<value>Java</value>
<value>数据库</value>
</array>
</property>
<!--2.List类型注入-->
<property name="list">
<list>
<value>张三</value>
<value>小张</value>
</list>
</property>
<!--3.Map类型注入-->
<property name="maps">
<map>
<entry key="JAVA" value="java"/>
<entry key="PHP" value="php"/>
</map>
</property>
<!--4.Set类型注入-->
<property name="sets">
<set>
<value>MySql</value>
<value>Redis</value>
</set>
</property>
</bean>
6.5、在集合里面设置对象类型值
先创建多个对象
<!-- 创建多个Course对象 -->
<bean id="course1" class="com.chenp.spring5.collectiontype.Course">
<property name="cname" value="Spring5框架"/>
</bean>
<bean id="course2" class="com.chenp.spring5.collectiontype.Course">
<property name="cname" value="Mybatis框架"/>
</bean>
再注入集合类型,值是对象
<!-- 注入List 集合类型,值是对象 -->
<property name="courseList">
<list>
<ref bean="course1"></ref>
<ref bean="course2"></ref>
</list>
</property>
6.6、把集合注入部分提取出来
(1)在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: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>
(2)使用 util 标签完成list集合注入提取。
<!--1.提取list集合类型属性-->
<util:list id="bookList">
<value>九阳神功</value>
<value>葵花宝典</value>
<value>易筋经</value>
</util:list>
<!-- 2.将提取的属性 注入使用-->
<bean id="book" class="com.chenp.spring5.collectiontype.Book">
<property name="list" ref="bookList"/>
</bean>
7、IOC操作Bean管理(FactoryBean)
7.1、spring中有两种bean
- 普通bean
- 工厂bean(FactoryBean)
7.2、普通bean:在配置文件中定义bean的类型就是返回类型
7.3、工厂bean:在配置文件中定义bean的类型可以和返回类型不一样
(1)创建类,让这个类作为工厂bean,实现接口FactoryBean。
(2)实现接口方法,在实现接口方法内定义返回的bean类型。
public class MyBean implements FactoryBean<Course> {
//定义返回bean
public Course getObject() throws Exception {
Course course = new Course();
course.setCname("abc");
return course;
}
public Class<?> getObjectType() {
return null;
}
public boolean isSingleton() {
return false;
}
}
<bean id="mybean" class="com.chenp.spring5.factorybean.MyBean"></bean>
@Test
public void test3(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Course mybean = context.getBean("mybean", Course.class);
System.out.println(mybean);
}
8、IOC操作Bean管理(Bean的作用域)
8.1、在spring中,可以设置创建bean实例是单实例还是多实例。
8.2、在spring中,默认情况下,创建的bean是一个单实例对象。
对象地址相同,意味着同一个实例。
8.3、如何设置单实例还是多实例
(1)在spring配置文件bean标签中有属性用于设置单实例还是多实例 scope
(2)scope属性值
- singleton:默认值,表示单实例
- prototype:表示多实例
(3)二者区别
- 前者单实例、后者多实例。
- 实例对象创建时间不同:
- 为单实例时,加载spring配置文件时就会创建单实例对象。
- 为多实例时,在调用getBean方法时才创建实例对象。
9、IOC操作Bean管理(Bean的生命周期)
9.1、生命周期
(1)从对象创建到对象销毁的过程
9.2、bean生命周期
(1)通过构造器创建bean实例(无参构造)
(2)为bean的属性设置值和对其他bean引用(调用set方法)
(3)调用bean的初始化的方法(需要进行配置初始化的方法)
(4)bean可以使用了(已获取到对象)
(5)当容器关闭时,调用bean的销毁方法(需要进行配置销毁的方法)
9.3、演示bean的生命周期
类代码
public class Orders {
private String oname;
//无参构造
public Orders() {
System.out.println("第一步:执行无参构造函数,创建bean实例");
}
public void setOname(String oname) {
this.oname = oname;
System.out.println("第二步:调用set方法设置属性值");
}
//创建执行时的初始化方法
public void initMethod(){
System.out.println("第三步:调用初始化方法");
}
//创建销毁时执行的方法
public void destroyMethod(){
System.out.println("第五步:执行销毁的方法");
}
}
spring配置文件
<bean id="orders" class="com.chenp.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
<property name="oname" value="手机"/>
</bean>
测试代码
@Test
public void test4(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
Orders orders = context.getBean("orders", Orders.class);
System.out.println("第四步:获取创建bean实例对象");
System.out.println(orders);
//手动销毁bean
((ClassPathXmlApplicationContext)context).close();
}
执行结果
9.4、若加上bean的后置处理器,bean生命周期有七步
(1)通过构造器创建bean实例(无参构造)
(2)为bean的属性设置值和对其他bean引用(调用set方法)
(3)把bean实例传递给bean后置处理器的方法 postProcessBeforeInitialization
(4)调用bean的初始化的方法(需要进行配置初始化的方法)
(5)把bean实例传递给bean后置处理器的方法 postProcessAfterInitialization
(6)bean可以使用了(已获取到对象)
(7)当容器关闭时,调用bean的销毁方法(需要进行配置销毁的方法)
9.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;
}
}
(2)spring配置文件中配置后置处理器
<!-- 配置后置处理器 -->
<bean id="myBeanPost" class="com.chenp.spring5.bean.MyBeanPost"/>
10、IOC操作Bean管理(xml自动装配)
10.1、什么是自动装配
- 根据指定装配规则(属性名称或者属性类型),spring自动将匹配的属性值进行注入
10.2、演示自动装配过程
(1)根据属性名字自动注入
<!-- 自动装配
1.byName 根据属性名称注入,注入值bean的id值需要跟类属性名一致
-->
<bean id="emp" class="com.chenp.spring5.autowire.Emp" autowire="byName">
</bean>
<bean id="dept" class="com.chenp.spring5.autowire.Dept">
</bean>
(2)根据属性类型自动注入
<!-- 自动装配
2.byType 根据属性类型注入,相同类型的bean不能存在多个
-->
<bean id="emp" class="com.chenp.spring5.autowire.Emp" autowire="byType">
</bean>
<bean id="dept" class="com.chenp.spring5.autowire.Dept">
</bean>
11、IOC操作Bean管理(外部属性文件)
11.1、直接配置数据库信息
(1)引入德鲁伊连接池依赖包
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.9</version>
</dependency>
(2)直接配置德鲁伊连接池
<!--直接配置连接池-->
<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/userDB"></property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
</bean>
11.2、引入外部属性文件配置数据库连接池
(1)创建外部属性文件,properties格式文件,写数据库信息
prop.driverClassName=com.mysql.jdbc.Driver
prop.url=jdbc:mysql://localhost/userDB
prop.username=root
prop.password=123456
(2)把外部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: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">
</beans>
在spring配置文件中使用标签,引入外部属性文件。
<!-- 引入外部属性文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${prop.driverClassName}"></property>
<property name="url" value="${prop.url}"></property>
<property name="username" value="${prop.username}"></property>
<property name="password" value="${prop.password}"></property>
</bean>
12、IOC操作Bean管理(基于注解方式)
12.1、什么是注解
(1)注解是代码的特殊标记,格式:@注解名称(属性名称=属性值,属性名称=属性值...)
(2)使用注解,注解作用在类、方法、属性上面
(3)使用注解的目录:简化xml配置
12.2、Spring针对Bean管理中创建对象提供注解
(1)@Component:普通注解
(2)@Service:一般用于业务层或service层
(3)@Controller:一般用于控制层、web层
(4)@Repository:一般用于持久层、dao层
上面4个注解功能是一样的,都可以用来创建bean实例。
12.3、基于注解方式实现对象创建
(1)引入依赖
spring-aop-5.2.6-RELEASE.jar,maven方式直接导入spring-webmvc ,已包含。
(2)开启组件扫描
<!--开启组件扫描
1 如果扫描多个包,多个包之间用逗号隔开
2 扫描包上层目录
-->
<context:component-scan base-package="com.chenp.spring5"></context:component-scan>
(3)创建类,在类上添加 创建对象 注解
//在注解里面value属性值可以省略不写,默认值是类名称的首字母小写
//UserService -->userService
@Component(value = "userService")// <bean id = "userService" class =".." />
public class UserService {
public void add() {
System.out.println("UserService add......");
}
}
12.4、开启组件扫描细节配置
自定义扫描的包含和排除过滤器。
<!-- 示例1
use-default-filters="false" 表示不适应默认的filters,自己配置filter
context:include-filter,设置扫描包含哪些内容
以下配置是指在扫描包下所有被@Controller注释的内容
-->
<context:component-scan base-package="com.chenp" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 示例2
context:exclude-filter:设置扫描排除哪些内容
以下配置是指在扫描包下所有除了被@Controller注释的内容
-->
<context:component-scan base-package="com.chenp">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
12.5、基于注解方式实现属性注入
(1)@AutoWired:根据属性类型进行自动装配。
第一步:创建service和dao对象。在service和dao类中添加创建对象注解。
第二步:在service注入dao对象。在service类中添加dao类型属性,在属性上面使用注解。
@Service
public class UserService {
//定义dao类型属性
//不需要添加set方法
//添加注入属性注解
@Autowired//根据类型进行注入
private UserDao userDao;
public void add() {
System.out.println("UserService add......");
}
}
(2)@Qualifier:根据属性名称进行注入。
接口可能会有多个实现类,仅靠根据属性类型无法匹配到唯一的类型,这个时候可以用@Qualifier和上面的@AutoWired一起搭配使用。
//定义dao类型属性
//不需要添加set方法
//添加注入属性注解
@Autowired//根据类型进行注入
@Qualifier("userDaoImpl2")//根据名称进行注入
private UserDao userDao;
(3)@Resource:可以根据类型注入,可以根据名称注入。
// @Resource//根据类型注入
@Resource(name="userDaoImpl2") //根据名称注入
private UserDao userDao;
注意:javax.annotation.Resource 注解属于java扩展包里,不在spring中。spring官方更推荐使用@AutoWired 和 @ Qualifier 。
(4)@Value:注入普通类型属性。
@Value(value = "abc")
private String name;
12.6、纯注解开发
(1)创建配置类,替代xml配置文件
@Configuration //作为配置类
@ComponentScan(basePackages = {"com.chenp.spring5"}) //开启扫描
public class SpringConfig {
}
(2)编写测试类
@Test
public void test2() {
//加载配置类
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
13、AOP(概念)
13.1、什么是AOP
(1)面向切面编程(方面),利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度江都,提高程序的可重用性,同时提高了开发的效率。
(2)通俗描述:不通过修改源代码的方式,在主干功能里面添加新功能。
(3)使用登录例子说明AOP
14、AOP(底层原理)
14.1、AOP底层使用动态代理
(1)有2种情况动态代理
第一种:有接口情况,使用JDK动态代理。
- 创建接口实现类代理对象,来增强类的方法。
第二种:没有接口情况,使用CGLIB动态代理。
- 创建子类的代理对象,来增强类的方法。
15、AOP(JDK动态代理)
15.1、使用JDK动态代理,使用Proxy类里面的方法创建代理对象
(1)调用Proxy.newProxyInstance()方法。
方法有三个参数:
- 类加载器。
- 被代理的类实现的接口,支持多个接口。
- 被代理类的增强处理程序,需实现 InvocationHandler 接口,写增强的方法。
(2)编写JDK动态代理代码。
创建接口UserDao。
public interface UserDao {
int add(int a, int b);
String update(String id);
}
接口实现类 UserDaoImpl 和具体的实现方法。
//被代理的真实对象
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类创建接口代理对象。
public class JDKProxy {
public static void main(String[] args) {
UserDaoImpl userDao = new UserDaoImpl();
//创建接口实现类的代理对象,代理对象和被代理对象实现同一个接口
Class<?>[] interfaces = {UserDao.class};
UserDao dao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoHandler(userDao));
System.out.println(dao.add(1, 2));
dao.update("abc");
}
}
//代理类的调用处理程序
class UserDaoHandler implements InvocationHandler {
//1.把创建的是谁的代理对象,把谁传递过来
//有参构造
private Object obj;
public UserDaoHandler(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;
}
}
测试效果
16、AOP(术语)
16.1、连接点(Jointpoint)
- 表示需要在程序中插入横切关注点的扩展点,连接点可能是类初始化、方法执行、方法调用、字段调用或处理异常等等。
- Spring只支持方法执行连接点,即在类里面哪些方法可以被增强,这些方法称为连接点。
- AOP中表示为“在哪里干”。
16.2、切入点(Pointcut)
- 实际被真正增强的方法,称为切入点。
- 在AOP中表示为“在哪里干的集合”;
16.3、通知/增强(Advice)
- 实际增强的逻辑部分称为通知/增强,在AOP中表示为“干什么”。
- 通知有多种类型:
- 【前置通知】:在方法执行之前执行
- 【后置通知】:在方法执行之后执行
- 【环绕通知】:在方法之前、之后执行
- 【异常通知】:在方法出现异常之后执行
- 【最终通知】:在方法无论是否终端都会执行
16.4、切面(Aspect)
- 认为是增强、引入和切入点的组合;
- 在Spring中可以使用Schema和@AspectJ方式进行组织实现;
- 在AOP中表示为“在哪干和干什么集合”;
16.5、目标对象(Target Object)
- 切入点选择的对象,需要被增强的对象,从而也可称为“被增强对象”;
- 由于Spring AOP 通过代理模式实现,从而这个对象永远是被代理对象;
- 在AOP中表示为“对谁干”;
16.6、AOP代理(AOP Proxy)
- AOP框架使用代理模式创建的对象,从而实现在连接点处插入增强(即应用切面);
- 在Spring中,AOP代理可以用JDK动态代理或CGLIB代理实现,而通过拦截器模型应用切面。
16.7、织入(Weaving)
- 织入是一个过程,是将切面应用到目标对象从而创建出AOP代理对象的过程;
- 织入可以在编译期、类装载期、运行期进行。
17、AOP(准备工作)
17.1、Spring框架一般都是基于AspectJ实现AOP操作
(1)什么是AspectJ
- AspectJ不是Spring组成部分,是独立的AOP框架,一般是把AspectJ和Spring框架一起使用,进行AOP操作
17.2、基于AspectJ实现AOP操作
(1)基于xml配置文件
(2)基于注解方式实现(一般使用)
17.3、项目工程中引入AOP相关依赖
<!-- AOP相关Jar -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>
<!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/net.sourceforge.cglib/com.springsource.net.sf.cglib -->
<dependency>
<groupId>net.sourceforge.cglib</groupId>
<artifactId>com.springsource.net.sf.cglib</artifactId>
<version>2.2.0</version>
</dependency>
17.4、切入点的表达式
(1)切入点表达式作用,知道对哪个类里面的哪个方法进行增强。
(2)语法结构
execution( [权限修饰符][返回类型][类全路径][方法名称]([参数列表]))
- 举例1:对com.atguigu.dao,BookDao类里面的add方法进行增强
- execution( * com.atguigu.dao.BookDao.add(..))
- 举例2:对com.atguigu.dao,BookDao类里面的所有方法进行增强
- execution( * com.atguigu.dao.BookDao.*(..))
- 举例3:对com.atguigu.dao,包下的所有类,类里面的所有方法进行增强
- execution( * com.atguigu.dao.*.*(..))
18、AOP操作(AspectJ注解)
18.1、创建类,类中定义方法。(被增强类)
//被增强类
public class User {
public void add() {
System.out.println("add.....");
}
}
18.2、创建增强类(编写增强逻辑)
(1)在增强类里面,创建方法,让不同的方法代表不同的通知类型。
//增强类
public class UserAdvice {
//前置通知
public void before() {
System.out.println("before");
}
}
18.3、进行通知的配置
(1)在spring配置文件中,开启注解扫描
@Configuration
@ComponentScan("com.atguigu.spring5.aopanno")
public class SpringConfig {
}
(2)使用注解创建User和UserAdvide
//被增强类
@Component
public class User {
public void add() {
System.out.println("add.....");
}
}
//增强类
@Component
public class UserAdvice {
//前置通知
public void before() {
System.out.println("before");
}
}
(3)在增强类上面添加注解@Aspect
@Aspect //把当前类标识为一个切面供容器读取
public class UserAdvice {...
(4)在spring配置文件中开启生成代理对象
@EnableAspectJAutoProxy
public class SpringConfig {....
18.4、配置不同类型的通知
(1)在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置
//增强类
@Component
@Aspect //把当前类标识为一个切面供容器读取
public class UserAdvice {
//前置通知
@Before(value = "execution(* com.chenp.spring5.aopanno.User.add(..))")
public void before() {
System.out.println("before");
}
// 后置通知/返回通知
@AfterReturning(value = "execution(* com.chenp.spring5.aopanno.User.add(..))")
public void afterReturning() {
System.out.println("afterReturning");
}
// 最终通知
@After(value = "execution(* com.chenp.spring5.aopanno.User.add(..))")
public void after() {
System.out.println("after");
}
//环绕通知
@Around(value = "execution(* com.chenp.spring5.aopanno.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕之前");
proceedingJoinPoint.proceed();
System.out.println("环绕之后");
}
//异常通知
@AfterThrowing(value = "execution(* com.chenp.spring5.aopanno.User.add(..))")
public void around() {
System.out.println("AfterThrowing");
}
}
测试代码:
public class AopTest {
@Test
public void testAopAnnotion() {
ApplicationContext context = new AnnotationConfigApplicationContext("com.chenp.spring5.config");
User user = context.getBean("user", User.class);
user.add();
}
}
执行结果:
如果在原方法中添加异常
//被增强类
@Component
public class User {
public void add() {
int i = 10 / 0;//异常
System.out.println("add.....");
}
}
再进行测试,测试结果为:
发现环绕之后、AfterReturning不执行,After仍然会执行,AfterThrowing会执行。
说明:
- After无论方法是否报异常是否正常结束,都会执行,所以是 最终通知。
18.5、相同的切入点抽取
//相同切入点抽取
@Pointcut(value = "execution(* com.chenp.spring5.aopanno.User.add(..))")
public void pointDemo() {
}
//前置通知
@Before(value = "pointDemo()")
public void before() {
System.out.println("before");
}
18.6、有多个增强类对同一个方法进行增强,可以设置增强类的优先级
(1)在增强类上面添加注解 @Order(数字类型值),数字类型值越小,优先级越高。
@Component
@Aspect
@Order(1)
public class PersionAdvice {...}
19、AOP操作(AspectJ配置文件)
1、创建两个类,增强类和被增强类,创建方法
//被增强类
public class Book {
public void buy() {
System.out.println("buy....");
}
}
//增强类
public class BookAdvice {
public void before() {
System.out.println("before...");
}
}
2、在spring配置文件中创建两个类对象
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean class="com.chenp.spring5.aopxml.Book"/>
<bean class="com.chenp.spring5.aopxml.BookAdvice"/>
</beans>
3、在spring配置文件中配置切入点
<!-- 配置aop增强 -->
<aop:config>
<!-- 切入点 -->
<aop:pointcut id="before" expression="execution(* com.chenp.spring5.aopxml.Book.buy(..))"/>
<!-- 配置切面 -->
<aop:aspect ref="bookAdvice">
<!-- 增强作用在具体的方法上-->
<aop:before method="before" pointcut-ref="before"/>
</aop:aspect>
</aop:config>
4、测试代码
@Test
public void testAopXml() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Book book = context.getBean("book", Book.class);
book.buy();
}
20、JdbcTemplate(概念和准备)
1、什么是JdbcTemplate
(1)Spring框架对JDBC进行封装,使用JdbcTemplate方便实现对数据库操作。
2、准备工作
(1)引入相关依赖
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.13</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
</dependencies>
(2)在spring配置文件配置数据库连接池
<!-- 配置数据库连接池 -->
<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://localhost:3306/user_db"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
(3)配置JdbcTemplate 对象,注入DataSource
<!--JdbcTemplate 对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入dataSource-->
<property name="dataSource" ref="dataSource"/>
</bean>
注解版:
@Configuration
@ComponentScan("com.chenp.spring5")
public class SpringConfig {
//数据库连接池
@Bean(name = "dataSource")
public DruidDataSource getDruidDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql://localhost:3306/user_db");
druidDataSource.setUsername("root");
druidDataSource.setPassword("123456");
return druidDataSource;
}
//JdbcTemplate 对象
@Bean(name = "jdbcTemplate")
public JdbcTemplate getJdbcTemplate(DruidDataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
(4)创建service类,创建dao类,在dao注入jdbcTemplate对象
- 开启组件扫描
<context:component-scan base-package="com.chenp.spring5"/>
- Service
@Service
public class BookService {
//注入dao
@Autowired
private BookDao bookDao;
}
- Dao
@Repository
public class BookDaoImpl implements BookDao {
//注入JdbcTemplate
@Autowired
private JdbcTemplate jdbcTemplate;
}
22、JdbcTemplate操作数据库(添加)
1、对应数据库创建实体类
public class User {
private Integer userId;
private String userName;
private String ustatus;
public void setUserId(Integer userId) {
this.userId = userId;
}
public void setUserName(String userName) {
this.userName = userName;
}
public void setUstatus(String ustatus) {
this.ustatus = ustatus;
}
}
2、编写service和dao
(1)在dao进行数据库添加操作
(2)调用JdbcTemplate对象里面update方法实现添加操作
@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 value(?,?,?)";
//2 调用方法实现
int update = jdbcTemplate.update(sql, book.getBookId(), book.getBookName(), book.getBstatus());
System.out.println(update);
}
}
3、测试类
public class TestBook {
@Test
public void testJdbcTemplate() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
BookService bookService = context.getBean("bookService", BookService.class);
bookService.addBook(new Book(1, "Spring5", "出售"));
}
}
23、JdbcTemplate操作数据库(删除和修改)
@Override
public int update(Book book) {
//1 创建sql语句
String sql = "update t_book set bookName=?,bstatus=? where book_id=?";
//2 调用方法实现
int update = jdbcTemplate.update(sql, book.getBookName(), book.getBstatus(), book.getBookId());
return update;
}
@Override
public int delete(Integer bookId) {
//1 创建sql语句
String sql = "delete from t_book where book_id=?";
//2 调用方法实现
int update = jdbcTemplate.update(sql, bookId);
return update;
}
23、JdbcTemplate操作数据库(查询)
23.1、查询返回某个值
1、查询表里面有多少条记录,返回某个值
2、使用JdbcTemplate
@Override
public int queryCount() {
//1 创建sql语句
String sql = "select count(1) from t_book";
//2 调用方法实现
Integer integer = jdbcTemplate.queryForObject(sql, Integer.class);
return integer;
}
23.2、查询返回对象
1、场景:查询图书详情
2、使用JdbcTemplate
- sql语句
- RowMapper,是接口,返回不同类型数据,使用这个接口里面实现类完成数据封装
- sql语句值
@Override
public Book queryBookByID(Integer bookId) {
//1 创建sql语句
String sql = "select * from t_book where book_id=?";
//2 调用方法实现
Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), bookId);
return book;
}
23.3、查询返回集合
1、场景查询图书分页列表
2、使用JdbcTemplate
@Override
public List<Book> queryBookList() {
//1 创建sql语句
String sql = "select * from t_book";
//2 调用方法实现
List<Book> bookList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class));
return bookList;
}
3、测试代码
@Test
public void testQueryBookList() {
BookService bookService = xmlContext.getBean("bookService", BookService.class);
List<Book> books = bookService.queryBookList();
for (Book book : books) {
System.out.println(book);
}
}
24、JdbcTemplate操作数据库(批量操作)
1、批量操作:操作表里面多条数据
2、使用JdbcTemplate实现批量添加操作
- sql语句
- List集合,添加多条记录数据
@Override
public int[] batchAdd(List<Object[]> batchArgs) {
String sql = "insert into t_book values(?,?,?)";
int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
return ints;
}
@Test
public void testBatchAdd() {
BookService bookService = xmlContext.getBean("bookService", BookService.class);
List<Object[]> batchArgs = new ArrayList<>();
Object[] o1 = {6, "Jenkins", "0"};
Object[] o2 = {7, "SpringCloud", "1"};
Object[] o3 = {8, "Mariadb", "1"};
batchArgs.add(o1);
batchArgs.add(o2);
batchArgs.add(o3);
System.out.println(Arrays.toString(bookService.batchAdd(batchArgs)));
}
3、使用JdbcTemplate实现批量修改操作
@Override
public int[] batchUpdate(List<Object[]> batchArgs) {
String sql = "update t_book set bookName=?,bstatus=? where book_id=?";
int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
return ints;
}
@Test
public void testBatchUpdate() {
BookService bookService = xmlContext.getBean("bookService", BookService.class);
List<Object[]> batchArgs = new ArrayList<>();
Object[] o1 = {"Jenkins", "0", 6};
Object[] o2 = {"SpringCloud", "0", 7};
Object[] o3 = {"Mariadb", "0", 8};
batchArgs.add(o1);
batchArgs.add(o2);
batchArgs.add(o3);
System.out.println(Arrays.toString(bookService.batchUpdate(batchArgs)));
}
4、使用JdbcTemplate实现批量删除操作
@Override
public int[] batchDelete(List<Object[]> batchArgs) {
String sql = "delete from t_book where book_id=?";
int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
return ints;
}
@Test
public void testBatchDelete() {
BookService bookService = xmlContext.getBean("bookService", BookService.class);
List<Object[]> batchArgs = new ArrayList<>();
Object[] o1 = {6};
Object[] o2 = {7};
Object[] o3 = {8};
batchArgs.add(o1);
batchArgs.add(o2);
batchArgs.add(o3);
System.out.println(Arrays.toString(bookService.batchDelete(batchArgs)));
}
25、事务概念
1、什么是事务
- 事务是数据操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败。
- 典型场景:银行转账
- A转账100给B
- A少100,B多100
2、事务四个特性(ACID)
- 原子性
- 一致性
- 隔离性
- 持久性
26、事务操作(搭建十五操作环境)
1、创建数据库表,添加记录
2、创建service,搭建dao,完成对象创建和注入关系。
(1)service注入dao,在dao注入JdbcTemplate,在JdbaTemplate注入DataSource
@Service
public class UserService {
@Autowired
private UserDao userDao;
}
@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
}
3、在dao层创建两个方法:加钱和扣钱,在service创建转账方法
@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void addMoney(Integer money, String username) {
String sql = "update t_account set money=money+? where username=?";
jdbcTemplate.update(sql, money, username);
}
@Override
public void reduceMoney(Integer money, String username) {
String sql = "update t_account set money=money-? where username=?";
jdbcTemplate.update(sql, money, username);
}
}
@Service
public class UserService {
@Autowired
private UserDao userDao;
/**
* 转账
*
* @param money 转账金额
* @param payer 扣款人
* @param payee 收款人
*/
public void transMoney(Integer money, String payer, String payee) {
userDao.addMoney(money, payee);
userDao.reduceMoney(money, payer);
}
}
4、上面代码,如果正常执行没有问题。但是如果代码执行过程中出现异常,则会有问题。
userDao.addMoney(money, payee);
//模拟异常
int i =10/0;
userDao.reduceMoney(money, payer);
(1)该如何解决呢?
- 使用事务进行解决
(2)事务操作过程
27、事务操作(Spring事务管理介绍)
1、事务添加到三层结构中的Service层(业务逻辑层)
2、在Spring进行事务管理操作
(1)两种方式:编程事务管理和声明式事务管理(使用)
3、声明式事务管理
(1)基于注解方式
(2)基于xml配置文件方式
4、在Spring进行声明式事务管理,底层使用AOP原理
5、Spring事务管理API
(1)提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类。
28、事务操作(注解声明式事务管理)
28.1、在spring配置文件配置事务管理器
<!-- 创建事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
28.2、在spring配置文件,开启事务注解
(1)在spring配置文件引入命名空间tx
<?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"
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">
</beans>
(2)开启事务注解
<!-- 开启事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
28.3、在service类上(或者service内的方法上)添加事务注解
(1)@Transactional 可以加在类上,也可以加在方法上。
(2)若加在类上,这个类里面的所有方法都添加事务。
(3)若加载方法上,只为此方法添加事务。
@Service
@Transactional//2、声明式事务
public class UserService {....
29、事务操作(声明式事务管理参数配置)
1、propagation:事务的传播行为
事务的传播行为指:当一个事务方法被另一个事务方法调用时候,这个事务方法如何进行。
例如:methodA方法调用methodB方法时,methodB是继续在调用者A的事务中运行呢,还是为自己开启一个新事务运行。这就是由methodB的事务传播行为决定的。
事务的传播行为可以由传播属性指定。Spring定义了7种类传播行为:
传播属性 | 描述 |
REQUIRED | 如果有事务在运行,当前的方法就在这个事务内运行;如果当前没有事务,就创建一个新事务,在自己的事务内运行。(这是最常见的选择) |
REQUIRED_NEW | 当前的方法必须启动新事物,并在它自己的事务内运行; 如果有事务正在运行,应该将它挂起。 |
SUPPORTS | 如果有事务在运行,当前的方法就就在该事务内运行;如果当前不存在事务,就以非事务执行。 |
NOT_SUPPORTED | 当前的方法不应该运行在事务中; 如果有运行的事务,将它挂起。 |
MANDATORY | 当前的方法必须运行在事务内部; 如果没有正在运行的事务,就抛出异常。 |
NEVER | 当前的方法不应该运行在事务中; 如果有运行的事务,就抛出异常。 |
NESTED | 如果有事务在运行,当前的方法就应该在这个事务的嵌套子事务内运行; 如果当前没有事务,就启动一个新的事物,并在它自己的事务内运行。 |
2、isolation:事务的隔离级别
(1)事务特性为隔离性,多事务操作之间不会产生影响。不考虑隔离性会产生很多问题。
(2)有三个读问题:脏读、不可重复读、虚(幻)读。
(3)脏读:事务A读到了事务B还没有提交的数据。
(4)不可重复读:在一个事务里面读取了2次某个数据,读到的数据不一致。
(5)幻读:在一个事务里面的操作中发现了未被操作的数据
(6)通过设置事务隔离级别,解决读问题。
@Transactional(isolation = Isolation.REPEATABLE_READ)
3、timeout:超时时间
(1)事务需要在一定的时间内进行提交,如果不提交则进行回滚。
(2)默认值是 -1,设置时间以秒为单位。
4、readOnly:是否只读
(1)读:查询操作;写:添加修改删除删除。
(2)readOnly默认值false,表示可以查询、可以添加删除修改操作。
(3)若设置成true,只能查询。
5、rollbackFor:回滚
(1)设置出现哪些异常,进行事务回滚。
6、noRollbackFor:不回滚
(1)设置出现哪些异常,不进行事务回滚。
30、事务操作(xml声明式事务管理)
1、在spring配置文件中进行配置
① 配置事务管理器
② 配置通知
③ 配置切入点和切面
<!-- 1、创建事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 2、配置通知 -->
<tx:advice id="txadvice">
<!-- 配置事务参数-->
<tx:attributes>
<!--指定哪种规则的方法上面添加事务-->
<tx:method name="transMoney" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 3、配置切入点和切面 -->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut id="pt" expression="execution(* com.chenp.spring5.service.UserService.*(..))"/>
<!-- 配置切面 -->
<aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
</aop:config>
31、事务操作(完全注解开发)
1、创建配置类,使用配置类代替xml配置文件
@Configuration//配置类
@ComponentScan(basePackages = "com.chenp") //组件扫描
@EnableTransactionManagement //开启事务
public class TxConfig {
//创建数据库连接池
@Bean
public DataSource 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("123456");
return dataSource;
}
//创建JdbcTemplate
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
//创建事务管理器
@Bean
public DataSourceTransactionManager transactionManager(DataSource dataSource) {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
32、Spring5框架新功能
1、整个Spring5框架的代码基于Java8,运行时兼容JDK9,许多不建议使用的类和方法在代码中删除。
2、Spring5.0框架自带了通用的日志封装。
(1)Spring5已经移除Log4jConfigListener,官方建议使用Log4j2。
(2)Spring5框架整合Log4j2。
第一步 引入jar包
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.11.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.11.2</version>
</dependency>
第二步 创建Log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
<configuration status="DEBUG">
<!--先定义所有的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.0框架核心容器支持@Nullable注解
(1)@Nullable 注解可以使用在方法上、属性上、参数上,表示方法返回可以为空、属性值可以为空、参数值可以为空。
4、Spring5.0框架核心容器支持函数式风格GenericApplicationContext
@Test
public void testGenericApplicationContext() {
//1 创建 GenericApplicationContext 对象
GenericApplicationContext context = new GenericApplicationContext();
context.refresh();
//2.调用context的方法注册对象
context.registerBean("user1", User.class, () -> new User());
User user1 = context.getBean("user1",User.class);
System.out.println(user1);
}
5、Spring5.0支持整合JUnit5
(1)整合JUnit4
第一步 引入Spring相关针对测试依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
第二步 创建测试类,使用注解方式完成
@RunWith(SpringJUnit4ClassRunner.class)//指定单元测试框架的版本
@ContextConfiguration("classpath:bean2.xml")//加载配置文件
public class JTest4 {
@Autowired
private UserService userService;
@Test
public void test1(){
userService.transMoney(100,"lucy","mary");
}
}
(2)整合JUnit5
第一步 引入Spring相关针对测试依赖
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.0</version>
</dependency>
第二步 创建测试类,使用注解方式完成
package com.chenp.spring5;
import com.chenp.spring5.service.UserService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:bean2.xml")//加载配置文件
public class JTest5 {
@Autowired
private UserService userService;
@Test
public void test1() {
userService.transMoney(100, "lucy", "mary");
}
}
(3)可以使用复合注解 @SpringJUnitConfig 来替代上面两个注解
@SpringJUnitConfig(locations = "classpath:bean2.xml")
public class JTest5 {...}
33、Spring5框架新功能(Webflux)
1、SpringWebflux介绍
(1)是Spring5添加的新模块,用于web开发,功能与SpringMVC类似,Webflux使用当前一种比较流行的响应式编程而出现的框架。
2、响应式编程
3、Webflux执行流程和核心API
4、SpringWebflux(基于注解编程模型)
5、SpringWebflux(基于函数式编程模型)