Spring个人笔记
课程流程
- Spring概念
- IOC容器
- AOP
- JDBCTemplate
- 事务管理
- Spring5新特性
Spring框架概述
-
Spring是轻量级的,开源的JaveEE框架
-
Spring可以解决企业应用开发的复杂性
-
Spring有两个核心部分:AOC和AOP
- IOC:控制反转,把创建对象过程交给Spring进行管理
- AOP:面向切面,不修改源代码的情况下进行功能增强
-
Spring特点
- 方便解耦,简化开发
- AOP编程的支持
- 方便程序的测试
- 方便集成整合其它框架
- 方便进行事务的操作
- 降低API开发难度
入门案例
Spring5模块
-
导入jar包,使用maven
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.9.RELEASE</version> </dependency> -
创建普通类,在这个类创建普通方法
package com.yang; public class User { public void add(){ System.out.println("add..."); } } -
创建spring配置文件,在配置文件中配置创建的对象
-
spring配置文件使用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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--配置User类对象创建--> <bean id="user" class="com.yang.User"/> </beans>
-
-
进行测试代码编写
public class TestSpring5 { @Test public void testAdd() { //加载spring的配置文件 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml"); //传值为xml中的id值 User user = applicationContext.getBean("user", User.class); user.add(); //获取配置创建的对象 } }
IOC容器(控制反转)
1. IOC底层原理
- xml解析
- 工厂模式
- 反射
概念和原理
什么是IOC
- 控制反转,把对象的创建和对象之间的调用过程,交给Spring进行管理
- 使用IOC目的:为了耦合度降低
- 做入门的案例就是IOC实现
工厂模式
目的:降低耦合度到最低限度

IOC过程,进一步降低耦合度
-
xml配置文件,配置创建的对象
<bean id="dao" class="com.yang.UserDao"/> -
第二步 有service和dao类,创建工厂类
class UserFactory{ public static UserDao getDao{ String classValue = class属性值;//1.xml解析得到 //2.通过反射得到对象 Class clazz = Class.forName(classValue); return (UserDao)clazz.newInstance(); } }
2. IOC接口(BeanFactory)
- IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
- Spring提供IOC容器实现的两种方式:(两个接口)
- BeanFactory:IOC容器基本实现,是Spring内部使用的接口,不提供给开发人员使用,加载配置文件的时候不会创建对象,获取或使用时才去创建对象。
- ApplicationContext: BeanFactory接口的子接口,提供更强大的功能。在加载配置文件的时候就会把配置对象进行创建。
- ApplicationContext接口有实现类

3. IOC操作,Bean管理(基于xml)
什么是Bean管理?包括创建对象与注入属性
- Spring创建对象
- Spring注入属性
3.1 基于xml方式创建对象
<bean id="user" class="com.yang.User"/>
- 在spring配置文件中,使用bean标签,标签中添加上对应属性,就可以实现对象创建
- 在bean标签中有很多属性,介绍常用属性
- id属性:唯一标识
- class属性:类的全路径
- 创建对象的时候默认执行无参的构造方法
3.2 基于xml方式注入属性
DI:依赖注入,就是注入属性,DI是IOC的一种实现方式
第一种注入方式,使用set注入
-
实体类创建
package com.yang; public class Book { private String bname; private String author; //set方法注入 public void setBname(String bname, String author) { this.bname = bname; this.author = author; } public Book() { } public void setBname(String bname) { this.bname = bname; } public void setAuthor(String author) { this.author = author; } //有参构造注入 public Book(String bname) { this.bname = bname; } } -
bean文件配置
<!-- set方法注入属性--> <bean id="book" class="com.yang.Book"> <!-- 使用property完成属性注入 name属性代表字段名称 value为注入的值 --> <property name="bname" value="天龙八部"/> <property name="author" value="杨剑"/> </bean>
第二种注入方式,使用有参构造注入
-
创建类,定义属性,创建构造方法
public class Orders { private String name; private String address; public Orders(String name, String address) { this.name = name; this.address = address; } } -
在spring配置文件中配置
<bean id="orders" class="com.yang.Orders"> <constructor-arg name="name" value="电脑"/> <constructor-arg name="address" value="中国"/> </bean>
第三种注入方式:p名称空间注入(了解即可)
-
使用p名称空间注入可以简化基于xml的配置方式
-
在配置文件中添加p名称空间
xmlns:p="http://www.springframework.org/schema/p" -
进行属性注入,在bean标签里进行操作
<bean id="book" class="com.yang.Book" p:bname="名字" p:author="作者"/>
IOC操作Bean管理(XML注入其他类型属性)
- 字面量
- 注入属性-外部bean
- 注入属性-内部bean
- 级联赋值
字面量
-
空值
<property name="author"> <null/> </property> -
属性值包含特殊符号
-
转义:> <
-
CDATE
<property name="author"> <value> <![CDATA[ <<南京>> ]]> </value> </property>
-
注入属性-外部bean
-
创建两个类service和dao类
-
在service调用dao里面的方法
-
在Spring配置文件中进行配置
<!-- service和dao对象的创建--> <bean id="userService" class="com.yang.service.UserService"> <!-- 注入userDao对象 name:属性值:类里面的属性名称 --> <property name="userDao" ref="userDaoImpl"/> </bean> <bean id="userDaoImpl" class="com.yang.dao.UserDaoImpl"/>
注入属性-内部bean和级联赋值
-
一对多关系:部门和员工
一个部门有多个员工,一个员工属于某一个部门
-
在实体类中体现一对多关系
员工表示所属部门,用对象表示
//部门类 public class Department { private String dName; public void setdName(String dName) { this.dName = dName; } }//员工类 public class Emp { private String ename; private String gender; //员工属于某一个部门,使用对象形式表示 private Department department; public void setEname(String ename) { this.ename = ename; } public void setGender(String gender) { this.gender = gender; } } -
在spring配置文件中进行相关配置
<!--内部bean--> <bean id="emp" class="com.yang.bean.Emp"> <!-- 先设置两个普通属性--> <property name="ename" value="ya"/> <property name="gender" value="男"/> <!-- 对象类型属性--> <property name="department"> <bean class="com.yang.bean.Department"> <property name="dName" value="公司"/> </bean> </property> </bean>注入属性:级联赋值
-
第一种方式
<bean id="emp" class="com.yang.bean.Emp">
<!-- 先设置两个普通属性-->
<property name="ename" value="ya"/>
<property name="gender" value="男"/>
<!-- 对象类型属性-->
<!-- 级联赋值-->
<property name="department" ref="deparment"/>
</bean>
<bean id="deparment" class="com.yang.bean.Department">
<property name="dName" value="财务部"/>
</bean>
- 第二种方式,需要生成deparment的get方法
<bean id="emp" class="com.yang.bean.Emp">
<!-- 先设置两个普通属性-->
<property name="ename" value="ya"/>
<property name="gender" value="男"/>
<!-- 对象类型属性-->
<!-- 级联赋值-->
<property name="department" ref="department"/>
<property name="department.dName" value="cai"/>
</bean>
<bean id="department" class="com.yang.bean.Department">
<property name="dName" value="财务部"/>
</bean>
xml注入集合属性
- 注入数组类型属性
- 注入List集合
- 注入Map集合类型的属性
-
创建类,定义数组, list,map,set集合属性,生成对应的set方法
-
在spring配置文件中配置
<bean id="student" class="com.yang.collectionType.Student"> <!-- 数组类型--> <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 key="PHP" value="php"/> </map> </property> <!-- set--> <property name="set"> <set> <value>123</value> <value>mysql</value> </set> </property> </bean> -
在集合里设置对象类型值
<property name="courseList"> <list> <ref bean="course1"/> <ref bean="course2"/> </list> </property><bean id="course1" class="com.yang.collectionType.Course"> <property name="cname" value="Spring5框架"/> </bean> <bean id="course2" class="com.yang.collectionType.Course"> <property name="cname" value="MyBatis框架"/> </bean> -
把集合注入部分提取出来
-
在Spring配置文件中引入名称空间 util
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:util="http://www.springframework.org/schema/util" 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 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd "> -
使用util标签提取
<util:list id="bookList"> <value>三国</value> <value>水浒</value> </util:list> <bean id="book" class="com.yang.collectionType.Book"> <property name="list" ref="bookList"/> </bean>
-
IOC操作Bean管理(FactoryBean)
-
Spring有两种类型的Bean,一种普通Bean,另一种:工厂Bean(FactoryBean)
-
普通Bean:在配置文件中定义的Bean类型就是你的返回类型
-
工厂Bean:在配置文件中定义的Bean类型可以和返回类型不一样
第一步:让这个类作为工厂Bean,实现接口FactoryBean
<bean id="myBean" class="com.yang.factorybean.MyBean"> </bean>第二步:实现接口里的方法,在实现的方法中定义返回的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; } }IOC操作Bean管理(Bean作用域)
-
在Spring里面,设置创建bean实例是单实例还是多实例
-
在Spring里面,默认情况下,bean是单实例对象


-
如何设置单实例还是多实例
-
spring配置文件中bean标签里有属性(scope)用于设置单实例还是多实例
-
scope属性值
第一个值 默认值,singleton,表示单实例对象
第二个值 prototype,多实例对象

-

scope和prototype的区别
第一:singleton:单实例
prototype:多实例
第二:设置scope为singleton的时候,加载配置文件的时候就会创建一个单实例对象
设置scope值是prototype的时候,不是加载配置文件时创建对象,在调用getBean方法的时候去创建多实例对象
还有request,session,一般不用,了解即可
生命周期: 从对象创建到对象销毁的的过程
bean生命周期
- 通过构造器创建bean实例(无参构造)
- 为bean的属性设置值和对其他bean的引用(调用set方法)
- 调用bean的初始化的方法(需要进行配置)
- bean可以使用了(对象获取到了)
- 当容器在关闭的时候,调用bean的销毁方法(需要进行配置销毁的方法)
演示bean的生命周期
package com.yang.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实例");
}
//创建执行的初始化方法
public void initMethod(){
System.out.println("第三步,执行初始化的方法");
}
//创建销毁的方法
public void destroyMethod(){
System.out.println("第五步,执行销毁的方法");
}
}
public void test2(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Orders orders = context.getBean("orders", Orders.class);
System.out.println("第四部,获取创建bean实例的对象");
System.out.println(orders);
//手动销毁bean实例
context.close();
}
bean的后置处理器,bean生命周期变为七步
bean生命周期
- 通过构造器创建bean实例(无参构造)
- 为bean的属性设置值和对其他bean的引用(调用set方法)
- 把bean实例传给bean后置处理器的方法postProcessBeforeInitialization
- 调用bean的初始化的方法(需要进行配置)
- 把bean实例传给bean后置处理器的方法postProcessAfterInitialization
- bean可以使用了(对象获取到了)
- 当容器在关闭的时候,调用bean的销毁方法(需要进行配置销毁的方法)
添加后置处理器效果
-
创建类,实现接口BeanPostProcessor,创建后置处理器
package com.yang.bean; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; public class MyBeanPost implements BeanPostProcessor { public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("在初始化之前执行的方法"); return bean; } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("在初始化之后执行的方法"); return bean; } } -
在bean中配置,自动为所有bean加上
<bean id="myBeanPost" class="com.yang.bean.MyBeanPost"/>
xml自动装配
什么是自动装配?
根据指定装配规则(属性名称或属性类型),Spring自动将匹配的属性值进行注入
演示自动装配过程
-
根据属性名称进行自动注入 byName
<bean id="emp" class="com.yang.autowire.Emp" autowire="byName"> <!-- <property name="dept" ref="dept"/>--> </bean> <bean id="dept" class="com.yang.autowire.Dept"/> -
根据属性类型进行自动注入 byType
<bean id="emp" class="com.yang.autowire.Emp" autowire="byType"> <!-- <property name="dept" ref="dept"/>--> </bean> <bean id="dept" class="com.yang.autowire.Dept"/>
外部属性文件
-
直接配置数据库信息,配置druid连接池
-
配置druid连接池
-
引入druid连接池依赖
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true"/> <property name="username" value="root"/> <property name="password" value="123456"/> </bean>
-
-
引入外部属性文件配置数据库连接池
-
创建外部属性文件,properties格式文件,写数据库信息
prop.driverClass=com.mysql.jdbc.Driver prop.url=jdbc:mysql://localhost:3306/mybatis?useSSL=true prop.userName=root prop.password=123456 -
把外部properties属性文件引入到Spring配置文件中
引入context名称空间
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">在spring配置文件中使用标签引入外部属性文件
写入
<context:property-placeholder location="classpath:jdbc.properties"/> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${prop.driverClass}"/> <property name="url" value="${prop.url}"/> <property name="username" value="${prop.userName}"/> <property name="password" value="${prop.password}"/> </bean>
-
4. IOC操作,Bean管理(基于注解)
- 什么是注解
- 使用注解的目的:简化xml操作
spring针对Bean管理中创建对象提供注解
-
@Component
-
@Service
-
@Controller
-
@Respository
上面的四个注解功能是一样的,都可以用来创建bean实例
基于注解方式实现对象的创建
-
引入AOP依赖
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.2.9.RELEASE</version> </dependency> -
开启组件扫描
<!-- 开启组件扫描 1.如果扫描多个包,多个包用逗号隔开 2.扫描上层目录 --> <context:component-scan base-package="com.yang"/> -
创建类,在类上面添加对象注解
//在注解里的value属性值可以省略不写 //默认值是类的名称,把首字母小写 @Service(value = "userService") //<bean id="userService" class=""/> public class UserService { public void add() { System.out.println("service add.............."); } }
开启组件扫描细节配置
<!-- 示例1
use-default-filters="false" 表示现在不使用默认的filter,而是使用自己配置的filter
context:include-filter:设置要扫描哪些内容
-->
<context:component-scan base-package="com.yang" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
<!-- 事例2:
context:exclude-filter 设置哪些内容不进行扫描
-->
<context:component-scan base-package="com.yang">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
注解方式属性注入
- @Autowired:根据属性类型进行自动装配
- @Qualifier:根据属性名称进行注入
- @Resource:可以根据类型注入,也可以根据名称注入
- @Value:注入普通类型属性
@Autowired
-
把service和dao对象创建,在service和dao类添加创建对象的注解
-
在service注入dao,在service类添加dao类型的属性,在属性上面使用注解
//在注解里的value属性值可以省略不写 //默认值是类的名称,把首字母小写 @Service(value = "userService") //<bean id="userService" class=""/> public class UserService { //定义dao类型属性 //不需要添加set方法 //添加属性注解 @Autowired //根据类型进行注入 private UserDao userDao; public void add() { System.out.println("service add.............."); userDao.add(); } }@Repository public class UserDaoImpl implements UserDao{ public void add() { System.out.println("dao add...."); } }
@Qualifier:根据属性名称进行注入
-
这个@Qualifier注解的使用要和上面的@Autowired一起使用
@Repository(value = "userDaoImpl1") public class UserDaoImpl implements UserDao{ public void add() { System.out.println("dao add...."); } }@Autowired //根据类型进行注入 @Qualifier(value = "userDaoImpl1") //根据名称及逆行注入
@Resource:可以根据类型注入,也可以根据名称注入,包:javax.annotation.Resource
@Service(value = "userService") //<bean id="userService" class=""/>
public class UserService {
//@Resource //根据类型进行注入
@Resource(name = "userDaoImpl1")//根据名称进行注入
private UserDao userDao;
public void add() {
System.out.println("service add..............");
userDao.add();
}
}
@Value:注入普通属性
@Value(value = "yj")
private String name;
完全注解开发
-
创建配置类,替代xml配置文件
@Configuration //作为配置类 ,替代xml配置文件 @ComponentScan(basePackages = {"com.yang"}) public class SpringConfig { } -
测试类
@Test public void test2(){ //加载配置类 ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); UserService userService = context.getBean("userService", UserService.class); userService.add(); }
AOP(面向切面编程)
概念:面向切面编程(面向方面编程)利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
通俗描述:不通过修改源代码方式,在主干功能里添加新功能
底层原理
- AOP底层使用动态代理方式
- 有两种情况的动态代理
- 有接口情况,使用JDK动态代理
- 没有接口情况,使用CGLIB动态代理
有接口情况,使用JDK动态代理
创建UserDao接口实现类代理对象,增强类的方法
没有接口情况,使用CGLIB动态代理
创建当前类子类的代理对象
AOP(JDK动态代理)
- 使用JDK动态代理,使用Proxy类里面的方法创建代理对象

- 调用newProxyInstance方法来实现,有三个参数
- 类加载器
- 增强方法所在的类,类实现的接口,支持多个接口
- 实现这个接口InvocationHandler,创建代理对象,写增强的方法

-
编写JDK动态代理代码
-
创建接口,定义方法
public interface UserDao { int add(int a,int b); String update(String id); } -
创建接口实现类,实现方法
public class UserDaoImpl implements UserDao{ public int add(int a, int b) { return a+b; } public String update(String id) { return id; } } -
使用Proxy创建接口的代理对象
public class JDKProxy { public static void main(String[] args) { //创建接口实现类的代理对象 UserDao userDao = new UserDaoImpl(); Class[] interfaces = {UserDao.class}; UserDao dao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao)); int res = dao.add(1, 2); System.out.println("result = "+res); } } //创建代理对象代码 class UserDaoProxy implements InvocationHandler { //把创建的代理对象的本体传递过来 //有惨构造进行传递 private Object obj; public UserDaoProxy(Object obj) { this.obj = obj; } //增强的逻辑 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; } }
-
AOP术语
- 连接点,类里面哪些方法可以被增强,这些方法称为连接点
- 切入点,实际被增强的方法,称为切入点
- 通知(增强),
- 实际增强的逻辑部分称为通知(增强)
- 通知有多种类型,
前置通知,后置通知,环绕通知,异常通知,最终通知(finally)
- 切面:把通知应用到切入点的过程
AOP操作(准备)
-
Spring框架中,一般基于AspectJ实现AOP操作
AspectJ:不是spring组成部分,独立AOP框架,一半把AspectJ与Spring框架一起使用。进行AOP相关操作
-
基于AspectJ实现AOP操作
- 基于xml配置文件
- 基于注解方式(使用)
-
在项目工程中引入AOP相关依赖
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.2.9.RELEASE</version> </dependency> -
切入点表达式
- 切入点表达式的作用:知道对哪个类里面的哪个方法进行增强
- 语法结构
execution([权限修饰符][返回类型][全类名][方法名称]([参数列表])
举例1:对com.yang.dao.Book类里面的add进行增强
execution(* com.yang.dao.Book.add(..))举例2:对com.yang.dao.Book类里面的所有方法进行增强
execution(* com.yang.dao.Book.*(..))举例3:对com.yang.dao包里面的所有类里面的所有包进行增强
execution(* com.yang.dao.*.*(..))
AOP操作(AspectJ注解)
-
创建类,在类里面定义方法,
public class User { public void add(){ System.out.println("add,,,,"); } } -
创建增强类(编写增强的逻辑)
在增强类里面创建方法,让不同方法代表不同通知类型
//增强的类 public class UserProxy { //前置通知 public void before(){ System.out.println("before........"); } } -
进行通知的配置
-
在Spring配置文件中,开启注解扫描
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--开启注解扫描--> <context:component-scan base-package="com.yang.aopanno"/> </beans> -
使用注解创建Userh和UserProxy对象
package com.yang.aopanno; import org.springframework.stereotype.Component; //被增强类 @Component public class User { public void add(){ System.out.println("add,,,,"); } }package com.yang.aopanno; import org.springframework.stereotype.Component; //增强的类 @Aspect @Component public class UserProxy { //前置通知 public void before(){ System.out.println("before........"); } } -
在增强类上面添加注解@Aspect
-
在Spring配置文件中,开启生成代理对象
<aop:aspectj-autoproxy/> -
配置不同类型的通知
在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式
-
package com.yang.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.yang.aopanno.User.add(..))")
public void before() {
System.out.println("before........");
}
//最终通知
@After(value = "execution(* com.yang.aopanno.User.add(..))")
public void after() {
System.out.println("after.........");
}
//后置通知(返回通知),有异常不执行
@AfterReturning(value = "execution(* com.yang.aopanno.User.add(..))")
public void afterReturning() {
System.out.println("afterReturning....");
}
@AfterThrowing(value = "execution(* com.yang.aopanno.User.add(..))")
public void afterThrowing() {
System.out.println("afterThrowing....");
}
@Around(value = "execution(* com.yang.aopanno.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("环绕之前。。。");
proceedingJoinPoint.proceed();
System.out.println("环绕之后。。。");
}
}
公共切入点提取
//相同切入点抽取
@Pointcut(value = "execution(* com.yang.aopanno.User.add(..))")
public void pointDemo(){
}
@Before(value = "pointDemo()")
public void before() {
System.out.println("before........");
}
有多个增强类对同一个方法进行增强,设置增强类优先级
-
在增强类上面添加注解
@Order(数字类型值),数字类型值越小,优先级越高@Component//对象创建 @Aspect//代理对象创建 @Order(1) public class PersonProxy { @Before(value = "execution(* com.yang.aopanno.User.add(..))") public void before(){ System.out.println("Person before......"); } } -
@ComponentScan(basePackages = {"com.yang"}) @Configuration @EnableAspectJAutoProxy(proxyTargetClass = true) public class MyConfig { }
AOP操作(AspectJ配置文件)
-
创建两个类,增强类和被增强类,创建方法
-
在spring配置文件中创建两个类对象
<!-- 创建对象--> <bean id="book" class="com.yang.aopxml.Book"/> <bean id="bookProxy" class="com.yang.aopxml.BookProxy"/> -
在spring配置文件中配置切入点
<!-- 创建对象--> <bean id="book" class="com.yang.aopxml.Book"/> <bean id="bookProxy" class="com.yang.aopxml.BookProxy"/> <!-- 配置aop增强--> <aop:config> <!-- 配置切入点--> <aop:pointcut id="p" expression="execution(* com.yang.aopxml.Book.buy(..))"/> <!-- 配置切面--> <aop:aspect ref="bookProxy"> <!-- 增强在具体的方法上--> <aop:before method="before" pointcut-ref="p"/> </aop:aspect> </aop:config>
JDBCTemplate
概念和准备
Spring框架对JDBC进行封装,使用JDBCTemplate方便实现对数据库操作
-
引入依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>org.springframework.orm</artifactId> <version>3.1.2.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.21</version> </dependency> -
在Spring配置文件配置连接池
<context:property-placeholder location="classpath:database.properties"/> <!-- 数据库连接池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> <property name="url" value="${prop.url}"/> <property name="username" value="${prop.userName}"/> <property name="password" value="${prop.password}"/> <property name="driverClassName" value="${prop.driverClass}"/> </bean> -
配置JdbcTemplate对象,注入dataSource
<!-- 创建JDBCTemplate对象--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <!--注入dataSource--> <property name="dataSource" ref="dataSource"/> </bean> -
创建service类与dao类,在dao注入jdbctemplate对象
配置文件中开启组件扫描
<context:component-scan base-package="com.yang"/>Dao层
@Repository public class BookDaoImpl implements BookDao{ //注入JDBCTemplate @Autowired private JdbcTemplate jdbcTemplate; }Service层
@Service public class BookService { //注入Dao @Autowired private BookDao bookDao; }
JdbcTemplate操作数据库(添加)
-
创建实体类
package com.yang.entity; public class User { private String id; private String name; private String pwd; @Override public String toString() { return "User{" + "id='" + id + '\'' + ", name='" + name + '\'' + ", pwd='" + pwd + '\'' + '}'; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } } -
编写service和dao
-
在dao进行数据库添加操作

两个参数:1. sql语句
2.可变长参数
@Repository public class BookDaoImpl implements BookDao{ //注入JDBCTemplate @Autowired private JdbcTemplate jdbcTemplate; public void add(Book book) { // 1.创建sql语句 String sql = "insert into mybatis.book values(?,?,?)"; // 2.调用方法实现 Object[] args = {book.getId(), book.getName(), book.getPwd()}; int update = jdbcTemplate.update(sql,args); System.out.println(update); } }
-
修改与删除操作
public void updateBook(Book book) {
String sql = "update mybatis.book set name=?,pwd=? where id=?";
Object[] args = {book.getName(),book.getPwd(),book.getId()};
int update = jdbcTemplate.update(sql,args);
System.out.println(update);
}
public void deleteBook(Integer id) {
String sql = "delete from mybatis.book where id = ?";
Object[] args = {id};
int delete = jdbcTemplate.update(sql,args);
System.out.println(delete);
}
查询返回某个值
-
查询表里有多少条记录,返回某个值
-
使用JDBCTemplate实现查询返回某个值

两个参数:sql语句,返回类型的Class
public int findCount() { String sql = "select count(*) from mybatis.book"; int count = jdbcTemplate.queryForObject(sql,Integer.class); return count; }
查询返回对象

第一个参数:sql语句
第二个参数:RowMapper,是接口,返回不同类型的数据,使用这个接口里面的实现类可以完成数据封装
第三个参数:可变长参数
查询返回集合
- 查询图书列表分页
- 调用JDBCTemplate方法实现查询返回集合

批量操作
-
JdbcTemplate实现批量添加的操作

有两个参数
第一个参数:sql语句
第二个参数:List集合,添加多条记录数据
public void batchAddBook(List<Object[]> batchArgs) { String sql = "insert into mybatis.book values (?,?,?)"; int[] res = jdbcTemplate.batchUpdate(sql, batchArgs); System.out.println(Arrays.toString(res)); }
实现批量修改操作
注意:在传值的时候要按照问号的顺序传
@Override
public void batchUpdate(List<Object[]> batchArgs) {
String sql = "update mybatis.book set name=?,pwd=? where id=?";
int[] res = jdbcTemplate.batchUpdate(sql, batchArgs);
System.out.println(Arrays.toString(res));
}
实现批量删除操作
@Override
public void batchDelete(List<Object[]> batchArgs) {
String sql = "delete from mybatis.book where id = ?";
int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
System.out.println(Arrays.toString(ints));
}
事务操作
概念
什么是事务
- 事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败,所有操作都失败
- 典型场景:银行转账
事务四个特性(ACID):
- 原子性:不可分割
- 一致性:总量不变
- 隔离性:两个人同时操作,不会对彼此产生影响
- 持久性:提交后表中数据发生真正变化
搭建环境

-
创建数据库表,添加记录

-
创建service,搭建dao,完成对象的创建和注入。
- service注入dao
- dao注入jdbctemplate,在JDBCTemplate注入dataSource
- 在dao创建两个方法,多钱和少钱,在service创建转账方法
- 问题,在有异常时一半的方法执行
事务操作过程
try {
userDao.reduceMoney();
// 模拟异常
int i = 1 / 0;
userDao.addMoney();
//没有发生异常,事务提交
} catch (Exception e) {
//出现了异常,事务回滚
e.printStackTrace();
}
Spring事务管理介绍
- 事务一般添加在javaee三层结构里的Service层
- 在Spring进行事务管理操作有两种方式
- 编程式事务管理
- 声明式事务管理(使用)基于注解方式,基于xml配置文件方式
声明式事务管理
Spring进行声明式事务管理,底层使用AOP
Spring事务管理API
1. 提供了一个接口,代表事务管理器,这个接口针对不同的框架提供了不同的实现类
注解方式实现声明式事务管理
-
在Spring配置文件中配置事务管理器
<!-- 配置事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> -
在spring配置文件中开启事务注解
-
在spring配置文件中引入名称空间tx
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" 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"> -
开启事务
<!-- 开启事务注解--> <tx:annotation-driven transaction-manager="transactionManager"/>
-
-
在Service类上面(或者service类方法上面)添加事务注解
-
@Transactional加在类和方法上均可 -
如果添加在类上面,代表类里面的所有方法都添加了事务
-
如果放在方法上面,只是为这个方法添加事务
@Service(value = "userServiceImpl") @Transactional //加在类和方法上均可 public class UserServiceImpl implements UserService { @Autowired @Qualifier("userDaoImpl") private UserDao userDao; public void accountMoney() { userDao.reduceMoney(); int i = 1 / 0; userDao.addMoney(); } }
-
声明式事务管理参数配置
-
在service类上面添加注解
@Transactional,在这个注解里面可以配置事务相关参数
-
propagation:事务传播行为
多事务方法之间执行调用,这个过程中事务是如何进行管理的
事务方法:对数据库表进行变化的操作
七种传播行为

-
isolation:事务的隔离级别
事务有特性:隔离性,多事务操作之间不会产生影响。不考虑隔离性会产生很多问题
有三个读的问题,脏读,不可重复读,虚(幻)读
脏读:一个未提交事务读取到另一个未提交事务的数据

不可重复读:一个未提交事务读取到一个提交事务修改数据

虚读:一个未提交事务读取到另一提交事务添加数据
通过设置事务隔离级别,就能解决三个读的问题

@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ) -
timeout超时时间。
事务需要在一定时间内进行提交,如果不提交,就会回滚
默认值是-1,设置时间以秒(s)为单位
-
readonly:是否只读
读:查询操作,写:添加修改删除
readOnly默认为false
设置为true后,只能查询
-
rollbackfor,回滚
设置出现哪些异常进行事务回滚
-
norollbackfor:不回滚
设置出现哪些异常不进行回滚
XML声明式事务管理
-
在Spring配置文件中进行配置
第一步:配置事务管理器
第二步:配置通知
第三步:配置切入点和切面
<!-- 配置事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 配置通知--> <tx:advice id="txadvice"> <!-- 配置事务相关参数--> <tx:attributes> <!-- 指定在哪种规则的方法上添加事务--> <tx:method name="accountMoney" propagation="REQUIRED"/> <!-- <tx:method name="account*"/>--> </tx:attributes> </tx:advice> <!-- 配置切入点和切面--> <aop:config> <!-- 配置切入点--> <aop:pointcut id="pt" expression="execution(* com.yang.service.UserServiceImpl.*(..))"/> <!-- 配置切面--> <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/> </aop:config>
完全注解开发
-
创建配置类,使用配置类替代xml配置文件
@Configuration @ComponentScan(basePackages = "com.yang") @EnableTransactionManagement //开启事务 public class TxConfig { //创建数据库连接池 @Bean public DruidDataSource dataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/mybatis?useSSL=true&serverTimezone=Asia/Shanghai&CharacterEncoding=utf-8"); dataSource.setUsername("root"); dataSource.setPassword("123456"); return dataSource; } //创建jdbcTemplate模板对象 @Bean public JdbcTemplate jdbcTemplate(DataSource dataSource) { //到ioc容器中根据类型找到dataSource JdbcTemplate jdbcTemplate = new JdbcTemplate(); jdbcTemplate.setDataSource(dataSource); return jdbcTemplate; } //创建事务管理器 @Bean public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource) { DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); dataSourceTransactionManager.setDataSource(dataSource); return dataSourceTransactionManager; } }
Spring5框架新功能
- 整个Spring5框架基于java8,运行时兼容jdk9,许多不建议使用的类和方法在代码库中删除
- spring5框架自带了通用的日志封装
- Spring5已经移除了Log4jConfigListener,官方建议使用Log4j2
Spring5框架整合Log4j2
-
引入依赖
<dependencies> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.12.1</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.12.1</version> </dependency> </dependencies> -
创建log4j2.xml
<?xml version="1.0" encoding="UTF-8"?> <Configuration> <!--<Configuration status="WARN" monitorInterval="30"> --> <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> <Loggers> <!-- 根日志设置 --> <root level="info"> <appender-ref ref="Console"/> </root> </Loggers> </Configuration>
Spring5框架核心容器支持@Nullable注解
@Nullable可以使用在方法上,属性上面,参数上面
- 用在方法上,表示方法返回值可以为空
- 注解用在方法参数里面,表示参数值可以为空
- 用在属性上,属性值可以为空
- Spring5核心容器里面支持函数式风格
支持整合JUnit5
第一步 引入Spring相关针对测试依赖

浙公网安备 33010602011771号