Spring是分层的 Java SE/EE 应用 full-stack 轻量级开源框架,以IoC(Inverse Of Control:反转控制)和AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层Spring MVC和持久层Spring JDBC以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架
spring官网:http://spring.io/
1997年IBM提出了EJB的思想
1998年,SUN制定开发标准规范EJB1.0
1999年,EJB1.1发布
2001年,EJB2.0发布
2003年,EJB2.1发布
2006年,EJB3.0发布
Rod Johnson(spring之父)
2017年9月份发布了spring的最新版本spring5.0通用版
框架的定义:拥有一整套解决方案
使用框架使得我们的程序开发变得更高效
1、spring是一个容器,这个容器会管理很多的组件(java bean)
2、spring可以很好的解决程序间的耦合
3、spring可以集成很多第三方框架,可以充当众多技术栈之间的一个粘合剂
方便解耦,简化开发
通过Spring提供的IoC容器,可以将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合,用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于业务层上的应用
AOP编程的支持
通过Spring的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付
声明式事务的支持
可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明方式灵活的进行事务的管理,提高开发效率和质量
方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情
方便集成各种优秀框架
Spring可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的直接支持
降低 JavaEE API 的使用难度
Spring 对 JavaEE API (如JDBC、JavaMail、远程调用等)进行了薄薄的封装,使这些API的使用难度大为降低
Java源码是经典学习范例
Spring 的源代码设计精妙,结构清晰,匠心独用,处处体现着大师对Java设计模式灵活运用以及对 Java 技术的高深造诣,它的源代码无疑是Java技术的最佳实践范例
Core Container (spring核心容器)
为其它模块提供支持
Data Access/Integration(持久层相关)
Test单元测试模块
工厂模式解耦
spring中工厂的类结构图
控制反转 IoC
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来降低计算机代码之间的耦合度,其中最常见的方式叫做依赖注入(Dependency Injection,简称DI)
还有一种方式叫依赖查找(Dependency Lookup),通过控制反转,对象在被创建的时候,依赖被注入到对象中
明确IoC的作用:削减计算机程序的耦合(解除代码中的依赖关系)
IOC是解耦的一种思想:解耦的途径有很多:比如依赖注入DI(只是解耦的一种解决方案)
程序中核心业务代码由主动创建对象变成被动的接收对象
★创建对象的控制权发生了转变(控制反转)
依赖注入:不在类中直接new对象,在外部需要使用本类时提供对象(可以通过工厂获取对象) (set注入,构造注入)
例如在业务层不直接提供持久层的实例
xxxxxxxxxx
161public class TestServiceImpl implements TestService {
2
3 private TestDao testDao;
4
5 //提供一个构造方法覆盖掉原有构造
6 public TestServiceImpl(TestDao testDao) {
7 this.testDao = testDao;
8 }
9
10
11 public void findData() {
12
13 testDao.findData();
14
15 }
16}
Inversion of Control
基于XML的配置
导入配置文件约束
xxxxxxxxxx
61
2<beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:schemaLocation="http://www.springframework.org/schema/beans
5 http://www.springframework.org/schema/beans/spring-beans.xsd">
6</beans>
创建一个xml文件,导入spring核心容器相关的约束 ==> 创建核心容器对象 ==> 从容器中去取出实例
1、spring是一个容器,里面存放了很多的实例
2、可以把spring理解成一个工厂,我们需要从工厂中获取实例
3、创建一个xml文件,导入spring核心容器相关的约束,可以使用spring提供的相关标签(标签里面是存放数据的)
4、spring会解析这个xml标签中的数据
ApplicationContext 接口的实现类:
ClassPathXmlApplicationContext:
它是从类的根路径下加载配置文件 推荐使用这种
FileSystemXmlApplicationContext:
它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置
AnnotationConfigApplicationContext:
当我们使用注解配置容器对象时,需要使用此类来创建spring容器,它用来读取注解,需要提供核心配置类的字节码
在spring中,如何将一个类的实例装载到spring容器中?
基于xml的方式来创建spring核心容器对象:
bean标签的作用:将一个类的实例通过反射机制创建出来,装载进核心容器中,默认情况下它调用的是类中的无参构造函数,如果没有无参构造函数则不能创建成功
id属性:唯一标识,不能重复的,给对象在容器中提供唯一标识,用于获取对象 class属性:指定类的全限定类名,用于反射创建对象,默认情况下调用无参构造函数
scope属性:可以去设置该实例的作用范围
xxxxxxxxxx
11 <bean id="aaa" class="com.beans.User"></bean>
从容器中取出实例
xxxxxxxxxx
201public static void main(String[] args) {
2
3 //创建核心容器对象
4 ApplicationContext applicationContext =
5
6 new ClassPathXmlApplicationContext("xml/beans.xml");
7
8 //要从容器中取出对应的实例
9 Object bean = applicationContext.getBean("aaa");
10
11 if (bean instanceof User){
12
13 User user = (User)bean;
14
15 //com.beans.User@42d80b78
16 System.out.println(user);
17
18 }
19
20}
sping在获取实例时的执行流程:
会解析xml文件,去读取每一个标签,如果发现有bean标签,那么会解析bean标签对应的属性,会去读取class属性的属性值,然后通过反射机制创建实例,保存到容器中,会将id属性作为该实例的标识
问:如果将一个实例存放进核心容器中,在程序中多次获取该实例,取得的实例是不是单例的?
默认情况下是单例的,但是我们可以将其修改为多例的
在标签下修改scope属性
singleton:默认值,单例的
prototype:多例的
request:WEB项目中,Spring创建一个Bean的对象,将对象存入到request域中
session:WEB项目中,Spring创建一个Bean的对象,将对象存入到session域中
global session:WEB项目中,应用在Portlet环境,如果没有Porlet环境那么globalSession相当于session
xxxxxxxxxx
51<!--和默认情况相同,是单例的-->
2<bean id="user" class="com.beans.User" scope="singleton"></bean>
3
4<!--修改为多例的-->
5<bean id="user" class="com.beans.User" scope="prototype"></bean>
DI—Dependency Injection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中
依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台
通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现
property标签的作用:
要通过set方法在xml配置文件中实现依赖注入,在类中必须实现set方法
name属性:类的属性名
value属性:类的属性值
ref属性:可以去引入容器中别的实例,属性值就是需要传入的实例的唯一标识
xxxxxxxxxx
81<bean id="user" class="com.beans.User" scope="prototype">
2
3 <!--给id属性赋值-->
4 <property name="id" value="1001"></property>
5 <property name="name" value="root"></property>
6 <property name="password" value="123456"></property>
7
8</bean>
给数组赋值
xxxxxxxxxx
81<!--给数组注入数据-->
2<property name="objects">
3 <array value-type="java.lang.String">
4 <value>a</value>
5 <value>b</value>
6 <value>c</value>
7 </array>
8</property>
给自定义引用数据类型赋值
xxxxxxxxxx
41<!--注入自定义的引用数据类型-->
2<property name="student" ref="student"></property>
3<!--将学生实例装载进核心容器中-->
4<bean id="student" class="com.beans.Student"></bean>
List
xxxxxxxxxx
91<!--给list类型的属性注入数据-->
2<property name="list">
3 <list>
4 <value>123456</value>
5 <value>ture</value>
6 <value>false</value>
7 <value>1100</value>
8 </list>
9</property>
Map
xxxxxxxxxx
91<!--给map类型的属性注入数据-->
2<property name="map">
3 <map>
4 <entry key="key1" value="value1"></entry>
5 <entry key="key2" value="value2"></entry>
6 <entry key="key3" value="value3"></entry>
7 <entry key="key4" value="value4"></entry>
8 </map>
9</property>
Date
xxxxxxxxxx
61<!--注入日期类型数据-->
2<property name="date" ref="date"></property>
3
4<!--注册一个日期实例-->
5<bean id="date" class="java.util.Date"></bean>
6
基于构造方法完成对值的注入
constructor-arg
根据有参构造来创建实例
xxxxxxxxxx
71<!--注册User实例-->
2<bean id="user" class="com.os467.beans.User">
3
4 <!--通过构造方法来为属性完成注入-->
5 <constructor-arg name="username" value="zs001"></constructor-arg>
6
7</bean>
多个参数构造创建实例
xxxxxxxxxx
101<!--注册User实例-->
2<bean id="user" class="com.os467.beans.User">
3
4 <!--通过构造方法来为属性完成注入-->
5 <constructor-arg name="username" value="zs001"></constructor-arg>
6 <constructor-arg name="date" ref="date"></constructor-arg>
7</bean>
8
9<!--注册Date实例-->
10<bean name="date" class="java.util.Date"></bean>
组件的作用范围是单例的:
init-method属性:对象在初始化时会执行的对应方法
destroy-method属性:对象在销毁时会执行的对应方法
xxxxxxxxxx
11<bean id="user" class="com.os467.beans.User" scope="singleton" init-method="initMethod" destroy-method="destroyMethod">
需要在类中定义方法
xxxxxxxxxx
111public void initMethod(){
2
3 System.out.println("初始化的方法执行了");
4
5}
6
7public void destroyMethod(){
8
9 System.out.println("销毁的方法执行了");
10
11}
关闭容器对象的方法
但是要用ClassPathXmlApplicationContext去接收才能调用此方法
xxxxxxxxxx
21//关闭容器对象
2applicationContext.close();
关闭后,容器中的实例会被销毁
组件的作用范围是多例的:
BeanFactory才是Spring容器中的顶层接口
ApplicationContext是它的子接口
创建对象的时间点不一样
通过BeanFactory创建的容器,其中的实例在被用到的时候才会创建
xxxxxxxxxx
51//通过BeanFactory创建容器
2BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("xml/beans.xml"));
3
4//此行执行User类实例的初始化函数
5User user = beanFactory.getBean("user", User.class);
xxxxxxxxxx
181<!--注册表现层实例-->
2<bean id="accountServlet" class="com.os467.account.servlet.AccountServlet">
3
4 <!--注入业务层的引用-->
5 <property name="accountService" ref="accountService"></property>
6
7</bean>
8
9<!--注册业务层引用-->
10<bean id="accountService" class="com.os467.account.service.AccountServiceImpl">
11
12 <!--注入持久层的引用-->
13 <property name="accountDao" ref="accountDao"></property>
14
15</bean>
16
17<!--注册持久层引用-->
18<bean id="accountDao" class="com.os467.account.dao.AccountDaoImpl"></bean>
首先程序会去读取配置文件,然后获取文件中的全类名数据,根据反射创建对象(单例的),保存在容器里面,后面如果有用到实例的地方,我们从容器中去获取
xxxxxxxxxx
931import java.io.FileReader;
2import java.io.IOException;
3import java.util.Enumeration;
4import java.util.HashMap;
5import java.util.Map;
6import java.util.Properties;
7
8/**
9 * 工厂类
10 */
11public class BeanFactory {
12
13 //模拟一个容器
14 private static Map beansMap;
15
16 private static Properties properties;
17
18 //在静态代码块中去创建容器对象,为了保证存入容器中的实例是单例的,
19 //所有的存放实例的步骤也要在类加载的时候去完成
20 static {
21
22 //实例化容器
23 beansMap = new HashMap();
24
25 //实例化properties集合
26 properties = new Properties();
27
28 FileReader fileReader = null;
29
30 try {
31
32 //创建流对象
33 fileReader = new FileReader("src/beans");
34
35 //读取数据到集合中
36 properties.load(fileReader);
37
38 //获取properties集合中所有的key
39 Enumeration keys = properties.keys();
40
41 //遍历所有的key
42 while (keys.hasMoreElements()){
43
44 //先获取对应key的字符串
45 String key = keys.nextElement().toString();
46
47 //根据key来获取value
48 String propertyClass = properties.getProperty(key);
49
50 //根据反射机制创建对象
51 Object value = Class.forName(propertyClass).newInstance();
52
53 //将实例存到容器里
54 beansMap.put(key,value);
55
56 }
57
58 } catch (Exception e) {
59
60 e.printStackTrace();
61
62 }finally {
63
64 try {
65
66 if (fileReader != null){
67
68 //关闭流对象
69 fileReader.close();
70
71 }
72
73 } catch (IOException e) {
74 e.printStackTrace();
75 }
76
77 }
78
79
80 }
81
82
83 /**
84 * 定义一个获取实例的方法
85 */
86 public static Object getBean(String beanId){
87
88 //从集合中获取实例
89 return beansMap.get(beanId);
90
91 }
92
93}
通过工厂获得业务层的实例
xxxxxxxxxx
191package com.os467.factory.servlet;
2
3import com.os467.factory.BeanFactory2;
4import com.os467.factory.service.AccountService;
5
6public class AccountServlet {
7
8 //聚合业务层引用
9 private AccountService accountService;
10
11 public void saveAccount() {
12
13 accountService = (AccountService) BeanFactory2.getBean("accountService");
14
15 accountService.saveAccount();
16
17 }
18
19}
注解 | 作用 |
---|---|
@Component | 把资源让spring来管理,相当于在xml中配置一个bean |
@Controller | 一般用于表现层的注解 |
@Service | 一般用于业务层的注解 |
@Repository | 一般用于持久层的注解 |
1、在配置文件中开启spring对注解的支持
2、直接在类上加对应的注解即可
在使用注解的IOC配置前需要先导入springAOP的jar包(底层需要)
配置文件约束
名称空间:xmlns:context
xxxxxxxxxx
121
2<beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xmlns:context="http://www.springframework.org/schema/context"
5 xsi:schemaLocation="http://www.springframework.org/schema/beans
6 http://www.springframework.org/schema/beans/spring-beans.xsd
7 http://www.springframework.org/schema/context
8 http://www.springframework.org/schema/context/spring-context.xsd">
9
10
11
12</beans>
开启spring对注解的支持,其实就是一个扫描包的过程
spring程序在执行的时候会去解析配置文件的数据,然后找到对应的包结构,并且扫描该包下对应的子包,然后会去检查每一个类上面有没有spring提供的注解,如果有,会将该类的实例装载进spring核心容器中
base-package:指定的包结构
xxxxxxxxxx
21<!--开启spring对注解的支持-->
2<context:component-scan base-package="com.os467"></context:component-scan>
在取出实例的时候,要注意,默认的标识是类的类名(首字符要小写)
xxxxxxxxxx
71//创建核心容器对象
2ApplicationContext applicationContext = new ClassPathXmlApplicationContext("xml/beans02.xml");
3
4//取出实例
5Student student = applicationContext.getBean("student", Student.class);
6
7System.out.println(student);
类注解中的value值代表该类在获取时需要提供的id,给Student类自定义的提供一个id值
xxxxxxxxxx
41"sss") (
2public class Student {
3
4}
改变作用范围的注解
@Scope
指定bean 的作用范围
取值:
singleton
prototype
request
session
globalession
生命周期相关
@PostConstruct
用于指定初始化的方法
@PreDestroy
用于指定销毁的方法
xxxxxxxxxx
171public class Student {
2
3
4 public void initMethod(){
5
6 System.out.println("初始化的方法执行了");
7
8 }
9
10
11 public void destroyMethod(){
12
13 System.out.println("销毁的方法执行了");
14
15 }
16
17}
用于注入数据的注解
注解 | 作用 |
---|---|
@Autowired | 根据类型注入 |
@Qualifier | 按名称注入,不能单独使用 |
@Resource | 根据id注入的 |
@Value | 用于注入基本数据类型和String类型 |
@Autowired注解
注入流程:首先spring在扫描到该属性上有Autowired注解之后,会去容器中找到该属性对应的类型与之注入
如果spring容器中与该属性类型匹配的实例有多个,那么究竟该注入哪个实例,这种情况下程序会报错
给这个类的实例起标识,起的标识一定要跟被注入属性的属性名称一致
@Qualifier注解
跟Autowired结合使用,指由Qualifier来决定究竟要注入哪个实例,需要提供注入实例的标识
Qualifier不能单独使用,只能和Autowried配合使用
@Resource注解
根据id去注入,相当于Autowired和Qualifier的结合
xxxxxxxxxx
21//指定id
2name = "accountService") (
@Value注解
给属性赋值,能赋值基本数据类型和String类型
xxxxxxxxxx
21"jack") (
2String name;
通过设计模式+反射机制来模拟
1、spring的配置文件:beans.xml(开启扫描包的过程)spring会去扫描该包下所有子包的类,为了检查有没有spring提供的注解
2、要在指定的类上加注解,如果spring检查到该类上有注解的话,就会将该类的实例通过反射机制创建出来,装进spring中
3、检查该类中的属性上有没有spring提供的注解,如果有的话,会通过反射的方式去注入实例
修饰持久层的注解
xxxxxxxxxx
121package com.os467.ioc.annotation;
2
3import java.lang.annotation.ElementType;
4import java.lang.annotation.Retention;
5import java.lang.annotation.RetentionPolicy;
6import java.lang.annotation.Target;
7
8
9ElementType.TYPE) (
10RetentionPolicy.RUNTIME) (
11public @interface Repository {
12}
修饰业务层的注解
xxxxxxxxxx
121package com.os467.ioc.annotation;
2
3import java.lang.annotation.ElementType;
4import java.lang.annotation.Retention;
5import java.lang.annotation.RetentionPolicy;
6import java.lang.annotation.Target;
7
8
9ElementType.TYPE) (
10RetentionPolicy.RUNTIME) (
11public @interface Service {
12}
修饰表现层的注解
xxxxxxxxxx
121package com.os467.ioc.annotation;
2
3import java.lang.annotation.ElementType;
4import java.lang.annotation.Retention;
5import java.lang.annotation.RetentionPolicy;
6import java.lang.annotation.Target;
7
8
9ElementType.TYPE) (
10RetentionPolicy.RUNTIME) (
11public @interface Controller {
12}
根据属性类型注入属性实例的注解
xxxxxxxxxx
111package com.os467.ioc.annotation;
2
3import java.lang.annotation.ElementType;
4import java.lang.annotation.Retention;
5import java.lang.annotation.RetentionPolicy;
6import java.lang.annotation.Target;
7
8ElementType.FIELD) (
9RetentionPolicy.RUNTIME) (
10public @interface AutoWired {
11}
工厂类,用于储存实例
xxxxxxxxxx
281package com.os467.ioc.factory;
2
3import java.util.HashMap;
4import java.util.Map;
5
6/**
7 * 工厂类,用于存放组件的实例(相当于容器)
8 */
9public class BeanFactory {
10
11 //创建Map集合对象
12 private static Map beansMap = new HashMap();
13
14 public static Map getBeansMap() {
15 return beansMap;
16 }
17
18 /**
19 * 获取实例的方法
20 */
21
22 public static Object getBean(String beanId){
23
24 return beansMap.get(beanId);
25
26 }
27
28}
模拟spring底层基于注解Ioc解耦(扫描指定包结构下的类,创建所需要的实例)
xxxxxxxxxx
2021package com.os467.ioc.scan;
2
3import com.os467.ioc.annotation.AutoWired;
4import com.os467.ioc.annotation.Controller;
5import com.os467.ioc.annotation.Repository;
6import com.os467.ioc.annotation.Service;
7import com.os467.ioc.factory.BeanFactory;
8
9import java.io.File;
10import java.io.FileFilter;
11import java.lang.annotation.Annotation;
12import java.lang.reflect.Field;
13import java.util.ArrayList;
14import java.util.List;
15import java.util.Map;
16import java.util.Set;
17
18/**
19 * 模拟spring底层基于注解的Ioc解耦
20 */
21public class ComponentScan {
22
23 //创建一个集合对象,用于存放全类名
24 private static List<String> classNameList = new ArrayList();
25
26 /**
27 * 模拟装配实例,注入实例的过程
28 * @param pathName
29 */
30 public static void getComponentScan(String pathName){
31
32 //把"."替换成"/"
33 pathName = pathName.replace(".","/");
34
35 //获取当前工程的绝对路径
36 String path = ClassLoader.getSystemResource("").getPath() + pathName;
37
38 //创建一个File对象
39 File file = new File(path);
40
41 //过滤指定路径下的文件
42 addFiles(file);
43
44 //遍历集合
45 for (String className : classNameList) {
46
47 try {
48
49 //创建字节码对象
50 Class aClass = Class.forName(className);
51
52 //根据反射检测类上有没有自定义的注解
53 Controller controller = (Controller)aClass.getAnnotation(Controller.class);
54 Service service = (Service)aClass.getAnnotation(Service.class);
55 Repository repository = (Repository)aClass.getAnnotation(Repository.class);
56
57 //如果类上有以上任意一个注解,我们就把该类添加到容器里面
58 if (controller != null || service != null || repository != null){
59
60 //根据反射创建实例
61 Object obj = aClass.newInstance();
62
63 //获取类的简类名
64 String simpleName = aClass.getSimpleName();
65
66 //将实例添加到工厂
67 BeanFactory.getBeansMap().put(simpleName,obj);
68
69 }
70
71
72 } catch (ClassNotFoundException e) {
73 e.printStackTrace();
74 } catch (IllegalAccessException e) {
75 e.printStackTrace();
76 } catch (InstantiationException e) {
77 e.printStackTrace();
78 }
79
80 }
81
82 //继续遍历集合,将需要注入的属性进行注入
83 for (String className : classNameList) {
84
85 try {
86
87 //创建字节码对象
88 Class aClass = Class.forName(className);
89
90 //根据反射检测类上有没有自定义的注解
91 Controller controller = (Controller) aClass.getAnnotation(Controller.class);
92 Service service = (Service) aClass.getAnnotation(Service.class);
93 Repository repository = (Repository) aClass.getAnnotation(Repository.class);
94
95 //如果类上有以上任意一个注解,继续检查类中的属性有没有自定义的注解
96 if (controller != null || service != null || repository != null) {
97
98 //获取属性字节码对象
99 Field[] declaredFields = aClass.getDeclaredFields();
100
101 //遍历属性数组
102 for (Field declaredField : declaredFields) {
103
104 //检查属性上有没有自定义的注解
105 AutoWired autoWired = declaredField.getAnnotation(AutoWired.class);
106
107 //如果这个注解不为空,就意味着该属性需要注入
108 if (autoWired != null){
109
110 //获取容器对象
111 Map beansMap = BeanFactory.getBeansMap();
112
113 //将容器对象转成set集合
114 Set<Map.Entry> entrySet = beansMap.entrySet();
115
116 //遍历set集合
117 for (Map.Entry entry : entrySet) {
118
119 //获取每个实例实现的接口
120 Class[] interfaces = entry.getValue().getClass().getInterfaces();
121
122 // 遍历接口数组,因为AutoWired是根据类型注入的,我们要保证注入的实例跟聚合的属性是同一类型
123 for (Class anInterface : interfaces) {
124
125 if (anInterface == declaredField.getType()){
126
127 //打破封装
128 declaredField.setAccessible(true);
129
130 //给属性赋值
131 declaredField.set(BeanFactory.getBean(aClass.getSimpleName()),entry.getValue());
132
133 }
134
135 }
136
137 }
138
139 }
140
141 }
142
143 }
144
145
146 } catch (Exception e) {
147 e.printStackTrace();
148 }
149
150 }
151
152 }
153
154 /**
155 * 定义一个递归的方法,用于过滤符合条件的文件
156 * file.listFiles():将所有的文件或者是文件夹切成一个数组
157 * @param file
158 */
159 public static void addFiles(File file){
160
161 //获取该文件夹下面所有的子文件夹,FileFilter接口实现类用于返回符合条件的文件
162 File[] files = file.listFiles(new FileFilter() {
163
164 public boolean accept(File pathname) {
165
166 //判断文件的类型
167 if (pathname.isDirectory()){
168
169 //继续调用递归的方法
170 addFiles(pathname);
171
172 }
173
174 //我们过滤的文件一般是以.class结尾的
175 return pathname.getPath().endsWith(".class");
176
177 }
178 });
179
180 //遍历文件的数组,将符合要求的文件路径切割成全类名的形式
181 for (File f : files) {
182
183 String path = f.getPath();
184
185 //将所有的"\"替换成"."
186
187 path = path.replace("\\",".");
188
189 //将com前面的字符串删了
190 path = path.substring(path.lastIndexOf("com"),path.length());
191
192 //将".class"切割掉
193 path = path.replace(".class","");
194
195 //将切割好的全类名添加到集合里面
196 classNameList.add(path);
197
198 }
199
200 }
201
202}
1、之前创建核心容器对象都是基于xml的,必须得有配置文件
2、spring5.0版本之后也是支持基于注解的方式创建核心容器对象,这样我们的程序就可以脱离xml配置文件
sping5.0版本后新的注解:
@Configuration
声明当前的类是一个配置类,仅起到标识作用
@ComponentScan
传入一个指定的包结构路线,开启扫描包,如果被@Component
或其它特定注解修饰的类就会被添加到核心容器中
@Bean
@Autowired
是一样的,都是根据类型去匹配的
@Qualifier
来修饰参数指定实例标识
注意:如果方法名称相同,即使用@Bean起了别名,有参的也会覆盖掉无参的
配置类
xxxxxxxxxx
221
2public class SpringConfig {
3
4
5 public User getUser(Student student){
6
7 User user = new User();
8
9 user.setStudent(student);
10
11 return user;
12
13 }
14
15 "student") (
16 public Student getStudent(){
17
18 return new Student();
19
20 }
21
22}
测试类
xxxxxxxxxx
161public static void main(String[] args) {
2
3 //创建核心容器对象
4 ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
5
6 //从容器中取实例
7 Object obj = applicationContext.getBean("getUser");
8
9 if (obj instanceof User){
10
11 User user = (User)obj;
12
13
14 }
15
16}
@PropertySource
引入配置文件,可以通过@Value
注解读取数据
classpath: 读取的配置文件资源需要放在src文件下,可以通过el表达式取到值
xxxxxxxxxx
231
2"classpath:jdbc") (
3public class JdbcConfig {
4
5 "${name}") (
6 private String name;
7
8 "${password}") (
9 private String password;
10
11 "user") (
12 public User getUser(){
13
14 User user = new User();
15
16 user.setName(name);
17 user.setPassword(password);
18
19 return user;
20
21 }
22
23}
配置文件jdbc
xxxxxxxxxx
21name=root
2password=root
@Import
可以让两个核心配置类之间存在继承的关系
引入其它核心配置类,需要提供一个配置类的字节码属性
xxxxxxxxxx
11SpringConfig.class) (
基于核心配置类配置三层架构
xxxxxxxxxx
601package com.os467.testRe.config;
2
3
4import com.os467.Work.annotation.AutoWired;
5import com.os467.testRe.dao.TestDao;
6import com.os467.testRe.dao.TestDaoImpl;
7import com.os467.testRe.service.TestServiceImpl;
8import com.os467.testRe.servlet.TestServlet;
9import org.springframework.beans.factory.annotation.Qualifier;
10import org.springframework.context.annotation.Bean;
11import org.springframework.context.annotation.ComponentScan;
12import org.springframework.context.annotation.Configuration;
13
14import javax.annotation.Resource;
15
16"com.os467.testRe") (
17
18public class SpringConfiguration2 {
19
20 /**
21 * 获取实例
22 */
23 name = "TestServlet") (
24 private TestServlet testServlet;
25
26 name = "TestServiceImpl") (
27 private TestServiceImpl testService;
28
29 name = "TestDaoImpl") (
30 private TestDaoImpl testDao;
31
32 /**
33 * 为实例注入属性
34 * @param testService
35 * @return
36 */
37
38 public TestServlet getTestServlet( ("getTestServiceImpl")TestServiceImpl testService) {
39
40 testServlet.setTestService(testService);
41
42 return testServlet;
43
44 }
45
46
47 public TestServiceImpl getTestServiceImpl( ("getTestDaoImpl") TestDaoImpl testDao) {
48
49 testService.setTestDao(testDao);
50
51 return testService;
52 }
53
54
55 public TestDaoImpl getTestDaoImpl() {
56
57 return testDao;
58
59 }
60}
总结
XML开发:
注解开发:
需要配置bean , @Bean与
需要扫描包,
AOP:全称是 Aspect Oriente Programming
(面向切面编程)
作用:
在程序运行期间,不修改源码对已有方法进行增强
优势:
Joinpoint(连接点):
连接点是指那些被拦截到的点,在spring中,这些点指的是方法,因为spring只支持方法类型的连接点,指的就是动态代理在底层可以访问到的所有方法
Pointcut(切入点):
所谓切入点是指我们要对哪些Joinpoint进行拦截的定义,就是要对哪些连接点(方法)进行增强,即需要被增强的方法
Advice(通知/增强):
所谓通知是指拦截到Joinpoint之后所要做的事情就是通知,通知的类型:
前置通知:
后置通知:
异常通知:
最终通知:
环绕通知:
Target(目标对象):
代理的目标对象
Weaving(织入):
是指把增强应用到目标对象来创建新的代理对象的过程(增强的过程)
spring采用动态代理织入,而AspectJ采用编译器织入和类装载器织入
Proxy(代理):
一个类被AOP织入增强后,就产生一个结果代理类
Aspect(切面):
是切入点(要增强的方法)和通知(怎么增强)之间的关系
场景:
之前学习jdbc的时候我们接触到了事务,A账户给B账户赚钱,我们需要加入事务的操作,要满足事务的一致性
如果用代理模式来完成对事务的织入,程序该如何去写?
使用JDK动态代理来为目标类创建代理类
xxxxxxxxxx
971package com.os467.proxy;
2
3import com.os467.account.service.AccountService;
4import com.os467.utils.TransactionMangerUtils;
5import org.springframework.beans.factory.annotation.Autowired;
6import org.springframework.stereotype.Component;
7
8import java.lang.reflect.InvocationHandler;
9import java.lang.reflect.Method;
10import java.lang.reflect.Proxy;
11
12/**
13 * 为业务层做代理
14 */
15
16public class AccountProxy {
17
18 //目标对象,需要被代理的对象
19
20 private AccountService accountService;
21
22 /**
23 * 获取代理对象
24 * @return
25 */
26 public Object getProxy(){
27
28 return Proxy.newProxyInstance(accountService.getClass().getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() {
29
30 /**
31 * 完成对目标方法的增强
32 *
33 * 在springAop中:
34 *
35 * 前置通知:调用目标方法之前执行的内容
36 * 后置通知:调用目标方法之后执行的内容
37 * 异常通知:在catch语句块中织入的内容
38 * 最终通知:在finally语句块中织入的内容
39 * 环绕通知:整个invoke方法执行的过程叫做环绕通知
40 *
41 * @param proxy
42 * @param method
43 * @param args
44 * @return
45 * @throws Throwable
46 */
47
48 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
49
50 Object invoke = null;
51
52 try {
53
54 if (method.getName().equals("transfer")){
55
56 //开启事务
57 TransactionMangerUtils.beginTransaction();
58
59 }
60
61 //调用目标类方法
62 invoke = method.invoke(accountService, args);
63
64 if (method.getName().equals("saveAccount")){
65
66 //提交事务
67 TransactionMangerUtils.commitTransaction();
68
69 }
70
71 }catch (Exception e){
72
73 //回滚事务
74 TransactionMangerUtils.rollBackTransaction();
75
76 //关闭事务
77 TransactionMangerUtils.closeTransaction();
78
79 e.printStackTrace();
80 }finally {
81
82 if (method.getName().equals("saveAccount")){
83
84 //关闭事务
85 TransactionMangerUtils.closeTransaction();
86
87 }
88
89 }
90
91 return invoke;
92
93 }
94 });
95
96 }
97}
测试类
xxxxxxxxxx
181public class AopTest01 {
2
3 private static ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
4
5
6 public static void main(String[] args) {
7
8 //从容器中取出代理对象
9 AccountProxy accountProxy = applicationContext.getBean("accountProxy", AccountProxy.class);
10
11 AccountService proxy = (AccountService)accountProxy.getProxy();
12
13 proxy.transfer();
14 proxy.saveAccount();
15
16 }
17
18}
通过xml来配置aop(切面):我们配置的是切入点和通知之间的关系
我们想在项目中去加入收集日志的功能,但是不能在核心模块中出现,会通过aop配置切面的方式来完成对核心模块中核心方法的增强
引入约束
xxxxxxxxxx
101
2<beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xmlns:aop="http://www.springframework.org/schema/aop"
5 xsi:schemaLocation="http://www.springframework.org/schema/beans
6 http://www.springframework.org/schema/beans/spring-beans.xsd
7 http://www.springframework.org/schema/aop
8 http://www.springframework.org/schema/aop/spring-aop.xsd">
9
10</beans>
标签:
aop:config:
作用:用于声明开始aop的配置
aop:aspect:
作用:配置切面
aop:pointcut:
作用:配置切入点表达式
xxxxxxxxxx
11<aop:before method="beforeAdvice" pointcut="execution(* com.os467.aop.method.AopMethod.*(..))"></aop:before>
第一个*标识当前工程,后面是资源路径,类.*标识该类下的所有方法,(..)代表匹配所有无参,(String,int)表示匹配指定参数的方法
aop:before:
作用:用于配置前置通知
aop:after-returning:
作用:用于配置后置通知
配置切面
xxxxxxxxxx
371<!--配置一个目标类的实例-->
2<bean id="aopMethod" class="com.os467.aop.method.AopMethod"></bean>
3
4<!--配置通知类的实例-->
5<bean id="aopLogger" class="com.os467.aop.logger.Logger"></bean>
6
7<!--开始配置Aop-->
8<aop:config>
9
10 <!--
11 配置切面:
12
13 要去引入一个通知类
14
15 -->
16 <aop:aspect id="idAspect" ref="aopLogger">
17
18 <!--
19 开始配置通知:
20 pointcut:配置切入点表达式,就是要设置通知的作用范围
21 -->
22
23 <!--配置前置通知-->
24 <aop:before method="beforeAdvice" pointcut="execution(* com.os467.aop.method.AopMethod.*(..))"></aop:before>
25
26 <!--配置后置通知-->
27 <aop:after-returning method="afterAdvice" pointcut="execution(* com.os467.aop.method.AopMethod.*(..))"></aop:after-returning>
28
29 <!--配置异常通知-->
30 <aop:after-throwing method="exceptionAdvice" pointcut="execution(* com.os467.aop.method.AopMethod.*(..))"></aop:after-throwing>
31
32 <!--最终通知-->
33 <aop:after method="endAdvice" pointcut="execution(* com.os467.aop.method.AopMethod.*(..))"></aop:after>
34
35 </aop:aspect>
36
37</aop:config>
外部声明切入点表达式
xxxxxxxxxx
81<!--声明一个切入点表达式-->
2<aop:pointcut id="pt01" expression="execution(* com.os467.aop.method.AopMethod.*(..))"/>
3
4<!--配置前置通知-->
5<aop:before method="beforeAdvice" pointcut-ref="pt01"></aop:before>
6
7<!--配置后置通知-->
8<aop:after-returning method="afterAdvice" pointcut-ref="pt01"></aop:after-returning>
通知类
xxxxxxxxxx
441package com.os467.aop.logger;
2
3/**
4 * 日志管理类(通知类)
5 */
6public class Logger {
7
8 /**
9 * 前置通知
10 */
11 public void beforeAdvice(){
12
13 System.out.println("前置通知开始记录日志了");
14
15 }
16
17 /**
18 * 后置通知
19 */
20 public void afterAdvice(){
21
22 System.out.println("后置通知开始记录日志了");
23
24 }
25
26 /**
27 * 异常通知
28 */
29 public void exceptionAdvice(){
30
31 System.out.println("异常通知开始记录日志了");
32
33 }
34
35 /**
36 * 最终通知
37 */
38 public void endAdvice(){
39
40 System.out.println("最终通知开始记录日志了");
41
42 }
43
44}
需要被增强的核心类
xxxxxxxxxx
261package com.os467.aop.method;
2
3/**
4 * 需要加入收集日志功能的核心类
5 */
6public class AopMethod {
7
8 public void getMethod01(){
9
10 System.out.println("getMethod01方法正在执行");
11
12 }
13
14 public void getMethod02(){
15
16 System.out.println("getMethod02方法正在执行");
17
18 }
19
20 public void getMethod03(){
21
22 System.out.println("getMethod03方法正在执行");
23
24 }
25
26}
测试类
xxxxxxxxxx
171public class AopTest {
2
3 public static void main(String[] args) {
4
5 //从容器中把目标类的实例取出来
6 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("xml/beans.xml");
7
8 //取出实例
9 AopMethod aopMethod = applicationContext.getBean("aopMethod", AopMethod.class);
10
11 //调用需要增强的方法
12 aopMethod.getMethod01();
13
14 }
15
16
17}
开启AOP对注解的支持
xxxxxxxxxx
181
2<beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xmlns:aop="http://www.springframework.org/schema/aop"
5 xmlns:context="http://www.springframework.org/schema/context"
6 xsi:schemaLocation="http://www.springframework.org/schema/beans
7 http://www.springframework.org/schema/beans/spring-beans.xsd
8 http://www.springframework.org/schema/aop
9 http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
10
11 <!--开启spring的扫描包-->
12 <context:component-scan base-package="com.os467"></context:component-scan>
13
14 <!--开启AOP对注解支持-->
15 <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
16
17
18</beans>
@Aspect
声明当前的类是一个切面类
@Pointcut
修饰一个方法,注解中的值为切入点表达式
@Before
前置通知
@AfterReturning
后置通知
@AfterThrowing
异常通知
@After
最终通知
通过注解配置通知类
xxxxxxxxxx
541/**
2 * 日志管理类(通知类)
3 */
4"logger") (
5
6public class Logger {
7
8 /**
9 * 声明一个切入点表达式
10 */
11 "execution(* com.os467.aop.method.AopMethod.*(..))") (
12 public void pt01(){};
13
14 /**
15 * 前置通知
16 */
17 "pt01()") (
18 public void beforeAdvice(){
19
20 System.out.println("前置通知开始记录日志了");
21
22 }
23
24 /**
25 * 后置通知
26 */
27 "pt01()") (
28 public void afterAdvice(){
29
30 System.out.println("后置通知开始记录日志了");
31
32 }
33
34 /**
35 * 异常通知
36 */
37 "pt01()") (
38 public void exceptionAdvice(){
39
40 System.out.println("异常通知开始记录日志了");
41
42 }
43
44 /**
45 * 最终通知
46 */
47 "pt01()") (
48 public void endAdvice(){
49
50 System.out.println("最终通知开始记录日志了");
51
52 }
53
54}
测试类
xxxxxxxxxx
131public class AopTest02 {
2
3 public static void main(String[] args) {
4
5 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("xml/beans02.xml");
6
7 AopMethod aopMethod = applicationContext.getBean("aopMethod", AopMethod.class);
8
9 aopMethod.getMethod01();
10
11 }
12
13}
环绕通知
在配置环绕通知的时候可以不去配置前置,后置,异常,结束这些通知,因为可以在环绕通知中定义这些通知
xml配置环绕通知
xxxxxxxxxx
21<!--配置环绕通知-->
2<aop:around method="aroundAdvice" pointcut-ref="pt01"></aop:around>
注解配置环绕通知
xxxxxxxxxx
21"pt01()") (
2public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint){环绕通知具体内容}
增强方法内容
xxxxxxxxxx
311/**
2 * 环绕通知,需要给它一个接口参数,spring会自动提供实例
3 */
4public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
5
6 //获取调用目标方法时所对应的参数
7 Object[] args = proceedingJoinPoint.getArgs();
8
9 //调用目标方法
10 try {
11
12 System.out.println("前置通知");
13
14 //类似于invoke方法
15 proceedingJoinPoint.proceed(args);
16
17 System.out.println("后置通知");
18
19 } catch (Throwable throwable) {
20
21 System.out.println("异常通知");
22
23 throwable.printStackTrace();
24 }finally {
25
26 System.out.println("结束通知");
27
28 }
29
30
31}
单元测试在底层是封装了main函数的,在不写主函数的情况下,可以去让程序跑起来
通过spring来集成单元测试(可以在不创建核心容器的情况下,取到容器中的实例)
@Test
在方法上加上Test注解,该方法就会变成像主函数一样可以运行
xxxxxxxxxx
61
2public void test01(){
3
4 System.out.println("成功运行了该方法");
5
6}
在程序运行的时候动态的获取核心容器对象
@RunWith
这个注解可以替换运行器,我们选用spring提供的运行器
@ContextConfiguration
指定如何创建核心容器对象,需要提供xml路径或者是配置类class
xxxxxxxxxx
231/**
2 * @RunWith 这个注解可以替换运行器
3 * SpringJUnit4ClassRunner 这个类实现了运行器接口,可以在spring程序运行的时候获取核心容器对象
4 * @ContextConfiguration 以什么方式创建核心容器对象(xml/注解)
5 */
6SpringJUnit4ClassRunner.class) (
7locations = "classpath:xml/beans.xml") (
8public class JunitTest01 {
9
10
11 private ApplicationContext applicationContext;
12
13
14 public void test01(){
15
16 //取出实例
17 AccountServiceImpl service = applicationContext.getBean("service", AccountServiceImpl.class);
18
19 service.saveAccount();
20
21 }
22
23}
基于核心配置类创建容器(注解注册)
xxxxxxxxxx
11classes = SpringConfig.class) (
jdbcTemplate对JDBC做了封装,相当于是一个工具类
xxxxxxxxxx
251//创建jdbcTemplate模板对象
2JdbcTemplate jdbcTemplate = new JdbcTemplate();
3
4//创建数据源的示例
5DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
6
7//设置连接地址
8driverManagerDataSource.setUrl("jdbc:mysql://localhost:3306/web_test?serverTimezone=GMT&characterEncoding=utf-8");
9
10//设置驱动全类名
11driverManagerDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
12
13//设置用户名
14driverManagerDataSource.setUsername("root");
15
16//设置密码
17driverManagerDataSource.setPassword("root");
18
19//给模板对象注入数据源的实例
20jdbcTemplate.setDataSource(driverManagerDataSource);
21
22//执行一个添加的功能
23int num = jdbcTemplate.update("insert into tb_user(username,password)values('tom','10010011')");
24
25System.out.println(num == 1 ?"添加成功":"添加失败");
表达式 | 符号 |
---|---|
< | < |
> | > |
& | & |
' | ' |
" | " |
通过xml配置注入jdbcTemplate模板实例
xxxxxxxxxx
351
2<beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xmlns:aop="http://www.springframework.org/schema/aop"
5 xmlns:context="http://www.springframework.org/schema/context"
6 xsi:schemaLocation="http://www.springframework.org/schema/beans
7 http://www.springframework.org/schema/beans/spring-beans.xsd
8 http://www.springframework.org/schema/aop
9 http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
10
11 <!--开启对注解的支持-->
12 <context:component-scan base-package="com.os467"></context:component-scan>
13
14 <!--配置一个jdbcTemplate实例-->
15 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
16
17 <!--注入数据源的实例-->
18 <property name="dataSource" ref="dataSource"></property>
19
20 </bean>
21
22 <!--注入一个数据源的实例-->
23 <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
24
25 <!--注入源数据-->
26 <property name="url" value="jdbc:mysql://localhost:3306/web_test?serverTimezone=GMT&characterEncoding=utf-8"></property>
27 <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
28 <property name="username" value="root"></property>
29 <property name="password" value="root"></property>
30
31 </bean>
32
33
34
35</beans>
junit测试类
xxxxxxxxxx
971package com.os467.test;
2
3import com.os467.account.service.AccountService;
4import com.os467.account.service.AccountServiceImpl;
5import com.os467.beans.User;
6import com.os467.config.SpringConfig;
7import com.os467.user_test.service.UserService;
8import org.junit.Test;
9import org.junit.runner.RunWith;
10import org.springframework.beans.factory.annotation.Autowired;
11import org.springframework.context.ApplicationContext;
12import org.springframework.context.support.ClassPathXmlApplicationContext;
13import org.springframework.jdbc.core.JdbcTemplate;
14import org.springframework.jdbc.datasource.DriverManagerDataSource;
15import org.springframework.test.context.ContextConfiguration;
16import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
17
18import java.util.List;
19
20/**
21 * @RunWith 这个注解可以替换运行器
22 * SpringJUnit4ClassRunner 这个类实现了运行器接口,可以在spring程序运行的时候获取核心容器对象
23 * @ContextConfiguration 以什么方式创建核心容器对象(xml/注解)
24 */
25SpringJUnit4ClassRunner.class) (
26locations = "classpath:xml/beans.xml") (
27public class JunitTest01 {
28
29
30 private AccountService accountService;
31
32
33 private UserService userService;
34
35
36 public void test01(){
37
38 accountService.saveAccount();
39
40 }
41
42 /**
43 * 通过jdbcTemplate模板连接数据库
44 */
45
46 /**
47 * 测试添加的功能
48 */
49 public void test02(){
50
51 int i = userService.addUser();
52
53 System.out.println(i == 1 ?"添加成功":"添加失败");
54
55 }
56
57 /**
58 * 测试删除功能
59 */
60
61 public void test03(){
62
63 int i = userService.deleteUserById();
64
65 System.out.println(i == 1 ?"删除成功":"删除失败");
66
67 }
68
69 /**
70 * 测试修改功能
71 */
72
73 public void test04(){
74
75 int i = userService.updateUserById();
76
77 System.out.println(i == 1 ?"修改成功":"修改失败");
78
79 }
80
81 /**
82 * 测试查询的功能
83 */
84
85 public void test05(){
86
87 List<User> userList = userService.findAllByUsers();
88
89 for (User user : userList) {
90
91 System.out.println(user);
92
93 }
94
95 }
96
97}
持久层
xxxxxxxxxx
581package com.os467.user_test.dao;
2
3import com.os467.beans.User;
4import org.springframework.beans.factory.annotation.Autowired;
5import org.springframework.jdbc.core.JdbcTemplate;
6import org.springframework.jdbc.core.RowMapper;
7import org.springframework.stereotype.Repository;
8
9import java.sql.ResultSet;
10import java.sql.SQLException;
11import java.util.List;
12
13
14public class UserDaoImpl implements UserDao {
15
16
17 private JdbcTemplate jdbcTemplate;
18
19
20 public List<User> findAllByUsers() {
21 return jdbcTemplate.query("select * from tb_user", new RowMapper<User>() {
22
23 public User mapRow(ResultSet resultSet, int i) throws SQLException {
24
25 int id = resultSet.getInt("id");
26
27 String username = resultSet.getString("username");
28
29 String password = resultSet.getString("password");
30
31 //对java对象的封装,创建User实例
32 User user = new User(id, username, password);
33
34 return user;
35 }
36 });
37 }
38
39
40 public int deleteUserById() {
41 return jdbcTemplate.update("delete from tb_user where id = 7");
42 }
43
44 /**
45 * 修改
46 * @return
47 */
48
49 public int updateUserById() {
50 return jdbcTemplate.update("update tb_user set username = ?,password = ? where id = ?","tom","123",6);
51 }
52
53
54 public int addUser() {
55
56 return jdbcTemplate.update("insert into tb_user(username,password)values('tom','111')");
57 }
58}
此接口是spring的事务管理器,它里面提供了我们常用的操作事务的方法
管理事务的子类对象:
1.DataSourceTransactionManager使用SpringJDBC或iBatis进行持久化数据时使用
2.HibernateTransactionManager使用Hibernate版本进行持久化数据时使用
配置步骤:
1、配置事务管理器
2、配置事务的通知引用事务管理器
3、配置事务的属性
4、配置AOP切入点表达式
5、配置切入点表达式和事务通知的对应关系
引入xmlns:tx相关约束
xxxxxxxxxx
111xmlns:tx="http://www.springframework.org/schema/tx"
2http://www.springframework.org/schema/tx
3http://www.springframework.org/schema/tx/spring-tx.xsd
4
5<!--配置一个事务管理器-->
6<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
7
8 <!--注入数据源实例-->
9 <property name="dataSource" ref="dataSource"></property>
10
11</bean>
场景:银行转账的场景
isolation:
获取事务的隔离级别
默认值是DEFAULT,数据库级别的
propagation:
获取事务的传播行为,表示是否开启事务,以什么样的策略开启
REQUIRED表示一定会开启事务,无论是增删改查都会开启
SUPPORTS表示有事务就会开启事务,没有事务就不会开启,会在增删改 的场景下开启事务,查询场景不会开启
timeout:
获取超时时间,默认值是-1,永不超时,如果是正数的话,可以以秒为单 位设置超时时间
read-only:
会影响事务是否开启
获取是否是只读事务(true/false),默认值是false,在查询的业务场景下,会把该属性设置成true
rollback-for:
是否开启回滚,默认值是true 自定义一个异常,除了该异常回滚,所有异常都不回滚 no-rollback-for:
自定义一个异常,除了该异常不回滚,所有异常都回滚
默认情况下,所有异常都回滚
只有在增删改的业务场景下,才会取开启事务,查询业务场景下不用开启事务,或者是只读就行
注意:spring中单个事务的生命周期只发生在在一个方法中(需要被AOP增强的每个方法)
如何配置事务管理器
xxxxxxxxxx
71<!--配置一个事务管理器-->
2<bean id="dataSourceManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
3
4 <!--注入数据源实例-->
5 <property name="dataSource" ref="dataSource"></property>
6
7</bean>
如何配置事务的通知
xxxxxxxxxx
171<!--配置一个事务的通知-->
2<tx:advice transaction-manager="dataSourceManager">
3
4 <!--配置事务的属性-->
5 <tx:attributes>
6
7 <!--
8 <tx:method name=""/>
9 name属性:用于匹配需要织入事务的方法
10 设置匹配规则,匹配方法名称,*代表通配符
11 isolation:事务的隔离级别
12 -->
13
14 <tx:method name="update*" isolation="DEFAULT" />
15
16 </tx:attributes>
17</tx:advice>
完整的spring事务织入xml配置
xxxxxxxxxx
681
2<beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xmlns:aop="http://www.springframework.org/schema/aop"
5 xmlns:context="http://www.springframework.org/schema/context"
6 xmlns:tx="http://www.springframework.org/schema/tx"
7 xsi:schemaLocation="http://www.springframework.org/schema/beans
8 http://www.springframework.org/schema/beans/spring-beans.xsd
9 http://www.springframework.org/schema/tx
10 http://www.springframework.org/schema/tx/spring-tx.xsd
11 http://www.springframework.org/schema/aop
12 http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
13
14
15
16 <!--注册业务层实例-->
17 <bean id="service" class="com.os467.tx.account.service.AccountServiceImpl"></bean>
18
19 <!--注册持久层的实例-->
20 <bean id="dao" class="com.os467.tx.account.dao.AccountDaoImpl"></bean>
21
22 <!--注册jdbc模板对象-->
23 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
24 <!--注入数据源实例-->
25 <property name="dataSource" ref="dataSource"></property>
26
27 </bean>
28
29 <!--注册数据源实例-->
30 <bean name="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
31 <!--注入数据源信息-->
32 <property name="url" value="jdbc:mysql://localhost:3306/web_test?serverTimezone=GMT&characterEncoding=utf-8"></property>
33 <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
34 <property name="username" value="root"></property>
35 <property name="password" value="root"></property>
36 </bean>
37
38 <!--配置一个事务管理器-->
39 <bean id="dataSourceManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
40
41 <!--注入数据源实例-->
42 <property name="dataSource" ref="dataSource"></property>
43
44 </bean>
45
46 <!--配置一个事务的通知-->
47 <tx:advice id="txAdvice" transaction-manager="dataSourceManager">
48
49 <!--配置事务的属性-->
50 <tx:attributes>
51
52 <tx:method name="*" propagation="REQUIRED" read-only="false"/>
53
54 </tx:attributes>
55 </tx:advice>
56
57 <!--配置aop-->
58 <aop:config>
59
60 <!--配置一个切入点表达式-->
61 <aop:pointcut id="pt01" expression="execution(* com.os467.tx.account.service.*.*(..))"/>
62
63 <!--配置切面-->
64 <aop:advisor advice-ref="txAdvice" pointcut-ref="pt01"></aop:advisor>
65
66 </aop:config>
67
68</beans>
配置步骤:
1、配置事务管理器并注入数据源
2、在业务层使用@Transactional注解
3、在配置文件中开启spring对注解事务的支持
@Transactional
事务注解,可以修饰类,也可以修饰方法
修饰类:该类下所有的方法都会加入事务的支持
修饰方法:只有指定方法会加入事务支持,方法的优先级比类要高
注意:使用注解,事务管理器还需要在xml中配置,但是事务通知可以不用配置了
xxxxxxxxxx
201package com.os467.account.service;
2import com.os467.account.dao.AccountDao;
3import org.springframework.beans.factory.annotation.Autowired;
4import org.springframework.transaction.annotation.Propagation;
5import org.springframework.transaction.annotation.Transactional;
6
7propagation = Propagation.REQUIRED) (
8public class AccountServiceImpl implements AccountService {
9
10
11 private AccountDao accountDao;
12
13
14 public void saveAccount() {
15
16 accountDao.savAccount();
17
18 }
19}
20