# Spring-Study 学习笔记 **Repository Path**: Lindom_kuang/spring-study-learning-notes ## Basic Information - **Project Name**: Spring-Study 学习笔记 - **Description**: Spring的学习笔记 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2020-10-07 - **Last Updated**: 2021-11-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Spring 学习笔记 [TOC] ## 一、基本配置流程 ### 1.pom.xml 的配置 导入maven依赖: ```xml org.springframework spring-webmvc 5.2.0.RELEASE ``` 可能用到的maven: ```xml org.projectlombok lombok 1.18.12 junit junit 4.12 ``` 解决无法打包问题 ```xml src/main/java **/*.properties **/*.xml false src/main/resources **/*.properties **/*.xml false ``` ### 2. applicationContext.xml 的配置 通常使用 applicationContext.xml 通过来整合其他 .xml 配置 ```xml ``` ### 3. pojo实体类的创建 ```java public class Hello { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public void show(){ System.out.println("Hello,"+ name ); } } ``` ### 4. 将实体类注入到 applicationContext.xml 文件中 ```xml ``` ### 5. 测试类 ```java @Test public void test(){ //解析beans.xml文件 , 生成管理相应的Bean对象 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //getBean : 参数即为spring配置文件中bean的id . //在执行getBean的时候, user已经创建好了 , 通过无参构造 Hello hello = context.getBean("hello", Hello.class); hello.show(); } ``` ## 二、复杂配置详解 ### 1. 构造器注入 通过有参构造方法来创建对象 ```xml ``` ### 2. set注入 c&p命名空间 ``` 具有p-命名空间的XML快捷方式:注入属性 在xml里加入:xmlns:p="http://www.springframework.org/schema/p" 具有c-namespace的XML快捷方式:注入构造器 在xml里加入:xmlns:c="http://www.springframework.org/schema/c" ``` ```xml 苹果 香蕉 rap 三国演义 水浒传 com.jdbc.xxx http://3306 root ``` ### 3. 取别名的两种方式 ``` bean就是java对象,由Spring创建和管理 id 是bean的标识符,要唯一 如果没有配置id, name就是默认标识符 如果配置id, 又配置了name, 那么name是别名, name可以设置多个别名, 可以用逗号,分号,空格隔开 如果不配置id和name, 可以根据applicationContext.getBean(.class)获取对象 class是bean的全限定名=包名+类名 ``` ```xml ``` ### 4. Bean的作用域 ``` scope(作用域)属性: 可填的属性值为:singleton(单例模式,默认) prototype(原型模式) Request、Session(在web中才会用到) ``` ```xml ``` ### 5. Bean的自动装配 #### 1. ByType 和 ByName ``` 在应用中,我们常常使用标签为JavaBean注入它依赖的对象。但是对于一个大型的系统,这个操作将会耗费我们大量的资源,我们不得不花费大量的时间和精力用于创建和维护系统中的标签。实际上,这种方式也会在另一种形式上增加了应用程序的复杂性,那么如何解决这个问题呢?Spring为我们提供了一个自动装配的机制,尽管这种机制不是很完善,但是在应用中结合标签还是可以大大的减少我们的劳动强度。前面提到过,在定义Bean时,标签有一个autowire属性,我们可以通过指定它来让容器为受管JavaBean自动注入依赖对象。 的autowire属性有如下六个取值,他们的说明如下: 1、 No:即不启用自动装配。Autowire默认的值。 2、 byName:通过属性的名字的方式查找JavaBean依赖的对象并为其注入。比如说类Computer有个属性printer,指定其autowire属性为byName后,Spring IoC容器会在配置文件中查找id/name属性为printer的bean,然后使用Seter方法为其注入。 (如果容器中存在一个与指定属性类型相同的bean,那么将与该属性自动装配;如果存在多个该类型bean,那么抛出异常,并指出不能使用byType方式进行自动装配;如果没有找到相匹配的bean,则什么事都不发生,也可以通过设置) 3、 byType:通过属性的类型查找JavaBean依赖的对象并为其注入。比如类Computer有个属性printer,类型为Printer,那么,指定其autowire属性为byType后,Spring IoC容器会查找Class属性为Printer的bean,使用Seter方法为其注入。 // 4、5、6 尚未学习 4、 constructor:通byType一样,也是通过类型查找依赖对象。与byType的区别在于它不是使用Seter方法注入,而是使用构造子注入。 5、 autodetect:在byType和constructor之间自动的选择注入方式。 6、 default:由上级标签的default-autowire属性确定。 ``` 例子: 创建三个pojo实体类 (Person、Cat、Dog ); 其中Cat 和 Dog 类啥都没有 ```java @Data public class Person { private String name; private Cat cat; private Dog dog; } ``` ```xml ``` ```xml ``` #### 2. 使用注解自动装配 使用注解前的环境配置: 1. 需要导入spring-aop的依赖包,但已经被spring-webmvc 包所包含 2. 在spring配置文件中引入context文件头 ```xml xmlns:context="http://www.springframework.org/schema/context" http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd ``` 3. 在spring配置文件中开启属性注解支持! ```xml ``` 注解说明: ``` @Autowired 1.标注可以放在成员变量上,也可以放在成员变量的set方法上 2.注解Autowired先默认使用byType来自动装配,如果存在类型的多个实例就尝试使用byName匹配, 如果通过byName也确定不了,可以通过@Qualifier(value="xxx")或@Primary(这个暂时不会用)注解来确定。 3.@Qualifier 不能单独使用 4.@Autowired(required=false) 说明: false,对象可以为null;true,对象必须存对象,不能为null。 @Resource 1.Java自己的注解,@Resource有两个属性是比较重要的,分是name和type 2.如果使用name属性,则使用byName自动注入策略 3.如果使用type属性,则使用byType自动注入策略 4.如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略,如果没有匹配,则回退为一个原始类型进行匹配 ``` @Autowired 和 @Qualifier 使用例子: 这种情况下,由于@Autowired 默认首先使用ByType来自动装配,但是这里存在同类型的多个bean,所以无法装配; 接着使用ByName来自动装配,但是没有找着dog 和 cat 这种名字,所以也无法装配; 再接着就只能借助@Qualifier(value = "cat2") ,来找到相应的cat2 这个bean,进行自动装配! ```xml ``` ```java @Data public class Person { private String name; @Autowired @Qualifier(value = "dog2") private Dog dog; @Autowired @Qualifier(value = "cat2") private Cat cat; } ``` @Resource 使用例子: 这种情况下,由于@Resource 默认首先使用ByName来自动装配,但是这个没有根据ByName 无法找到与其相应的名字dog 和 cat ,所以无法装配; 接着,使用ByType来自动装配,由于Dog类对应的bean对象只存在一个,所以自动装配成功,但是Cat类对应的bean对象存在两个,所以也无法装配; 再接着就只能借助@Resource 的属性(name = "cat2") ,来找到相应的cat2这个bean,进行自动装配! ```xml ``` ```java @Data public class Person { private String name; @Resource private Dog dog; @Resource (name = "cat2") private Cat cat; } ``` ### 6. 使用注解开发 环境配置: 1. 需要导入spring-aop的依赖包,但已经被spring-webmvc 包所包含 2. 在spring配置文件中引入context文件头 ```xml xmlns:context="http://www.springframework.org/schema/context" http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd ``` 3. 开启属性注解支持 配置扫描哪些包下的注解 ```xml ``` 4. 在指定包下编写类,增加注解 ``` 1. @Component(value = "user5") //Bean的实现 相当于配置文件中 //加在类名上,这里的value相当于取别名,不加value默认名字为类名开头小写(user) 2. @Value(value = "锋神") //属性注入 相当于配置文件中 //可以加在属性上,也可加在SetXxx()方法上 可以直接写"xxx",不用加value 3. @scope 作用域 加在类名上 //singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂,所有的对象都会销毁。 //prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收 4.衍生注解 @Component三个衍生注解 为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。 1) @Controller:web层 2) @Service:service层 3) @Repository:dao层 ``` ```java package com.lindom.pojo; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @Component(value = "user5") @Scope("prototype") public class User { @Value(value = "锋神") public String name; public void setName(String name) { this.name = name; } } ``` ### 7. 基于Java类进行配置 ``` 1. @Configuration 告知Spring这是一个配置类,会为Spring应用上下文提供bean 2. @Bean 通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id! 3. @Import 导入合并其他配置类,类似于配置文件中的 inculde 标签 基于Java类进行配置,连spring配置文件都不需要了! JavaConfifig 原来是 Spring 的一个子项目,它通过 Java 类的方式提供 Bean 的定义信息,在 Spring4 的 版本, JavaConfifig 已正式成为 Spring4 的核心功能 。 关于这种Java类的配置方式,我们在之后的SpringBoot 和 SpringCloud中还会大量看到,我们需要知道 这些注解的作用即可! ``` 1. 编写一个实体类,Dog ```java @Component //将这个类标注为Spring的一个组件,放到容器中! public class Dog { public String name = "dog"; } ``` 2. 新建一个confifig配置包,编写一个MyConfifig配置类 ```java @Configuration //告知Spring这是一个配置类 public class MyConfig { @Bean //通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id! public Dog dog(){ return new Dog(); } } ``` 3. 导入其他配置 ```java @Configuration public class MyConfig2 { } ``` ```java @Configuration @Import(MyConfig2.class) //导入合并其他配置类,类似于配置文件中的 inculde 标签 public class MyConfig { @Bean public Dog dog(){ return new Dog(); } } ``` ### 8. AOP AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现 程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的 一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使 得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。 ``` 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 .... 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。 目标(Target):被通知对象。 代理(Proxy):向目标对象应用通知之后创建的对象。 切入点(PointCut):切面通知 执行的 “地点”的定义。 连接点(JointPoint):与切入点匹配的执行点。 ``` 使用AOP织入,需要导入一个依赖包! ```xml org.aspectj aspectjweaver 1.9.5 ``` 使用Spring实现Aop的三种方式: #### 1. 通过Spring API 实现AOP 首先编写业务接口和实现类 ```java public interface UserService { void add(); void delete(); void update(); void query(); } ``` ```java public class UserServiceImpl implements UserService{ public void add() { System.out.println("执行了add方法"); } public void delete() { System.out.println("执行了delete方法"); } public void update() { System.out.println("执行了update方法"); } public void query() { System.out.println("执行了query方法"); } } ``` 编写增强类 ,这里编写两个 , 一个前置增强 一个后置增强 ```java package com.lindom.Log; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; public class Log implements MethodBeforeAdvice { //method: 要执行的目标对象的方法 //objects: 被调用的方法的参数(args) //o: 目标对象(target) public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println("[Log] " + o.getClass().getName() + "的" + method.getName() + "方法被执行了"); } } ``` ```java package com.lindom.Log; import org.springframework.aop.AfterReturningAdvice; import java.lang.reflect.Method; public class AfterLog implements AfterReturningAdvice { //o: 返回值(returnValue) //method 被调用的方法 //objects 被调用的方法的参数(args) //o1 被调用的目标对象(target) public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable { System.out.println("[AfterLog] " + "执行了" + o1.getClass().getName() + "的" + method.getName() + "方法," + "返回值:" + o); } } ``` 最后去spring的文件中注册 , 并实现aop切入实现 , 注意导入约束 ```xml ``` 测试类: ```java public class MyTest { @Test public void test(){ ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); UserService userService = (UserService) context.getBean("userService"); userService.search(); } } ``` #### 2. 通过自定义类实现Aop 自己创建一个切入类 ```java public class DiyPointCut { public void before(){ System.out.println("方法实现前!-------------------------"); } public void after(){ System.out.println("方法实现后!--------------------------"); } } ``` 配置spring配置文件 ```xml ``` #### 3. 通过注解实现AOP 编写一个注解实现的增强类 ```java package com.lindom.anno; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect public class AnnotationPointCut { @Before("execution(* com.lindom.UserService.UserServiceImpl.*(..))") public void before(){ System.out.println("--------执行方法前!!!--------"); } @After("execution(* com.lindom.UserService.UserServiceImpl.*(..))") public void after(){ System.out.println("--------执行方法后!!!--------"); } @Around("execution(* com.lindom.UserService.UserServiceImpl.*(..))") public void around(ProceedingJoinPoint jp) throws Throwable { System.out.println("环绕前"); Object proceed = jp.proceed(); System.out.println("环绕后"); System.out.println(proceed); } } ``` 配置spring配置文件 ```xml ``` 测试类 ```java public class MyTest { @Test public void test(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //这里需要实现接口而不是类 UserService userService = context.getBean("userServiceImpl", UserService.class); userService.update(); } } ``` 运行结果 ``` 环绕前 --------执行方法前!!!-------- 执行了update方法 环绕后 null --------执行方法后!!!-------- Process finished with exit code 0 ``` ## 三、整合Mybatis 各种关联框架需要的版本: | **MyBatis-Spring** | **MyBatis** | **Spring** **框架** | **Spring Batch** | **Java** | | :----------------: | :---------: | :-----------------: | :--------------: | :------: | | 2.0 | 3.5+ | 5.0+ | 4.0+ | java 8+ | | 1.3 | 3.4+ | 3.2.2+ | 2.1+ | java 6+ | pom.xml 的配置 ```xml org.mybatis mybatis 3.5.5 mysql mysql-connector-java 8.0.19 org.springframework spring-webmvc 5.2.0.RELEASE org.springframework spring-jdbc 5.2.7.RELEASE org.aspectj aspectjweaver 1.9.5 org.mybatis mybatis-spring 2.0.5 org.projectlombok lombok 1.18.12 provided junit junit 4.13 test ``` ### 1. 整合方式一 #### 1. 配置 spring配置文件 beans.xml ```xml ``` #### 2. 配置 mybatis-config.xml ```xml ``` #### 3. 创建Mapper接口和对应的Mapper.xml ```java package com.lindom.dao; import com.lindom.pojo.User; import java.util.List; public interface UserMapper { List selectUser(); } ``` ```xml ``` #### 4. 增加Dao接口的实现类; 私有化sqlSessionTemplate ```java package com.lindom.dao; import com.lindom.pojo.User; import org.mybatis.spring.SqlSessionTemplate; import java.util.List; public class UserDaoImpl implements UserMapper { //sqlSession不用我们自己创建了,Spring来管理 private SqlSessionTemplate sqlSeesion; public void setSqlSeesion(SqlSessionTemplate sqlSeesion) { this.sqlSeesion = sqlSeesion; } public List selectUser() { UserMapper mapper = sqlSeesion.getMapper(UserMapper.class); List list = mapper.selectUser(); return list; } } ``` #### 5. 注册bean实现 applicationContext.xml (这里可以自行分工,将配置都写在beans.xml里,把bean注册之类的写在总的applicationContext.xml里) ```xml ``` #### 6. 测试类 ```java public class MyTest { @Test //整合方式一 public void selectUser2(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserMapper mapper = (UserMapper) context.getBean("userDao"); List list = mapper.selectUser(); for (User user : list) { System.out.println(user); } } } ``` ### 2. 整合方式二 1. 配置 spring配置文件 (无需注册sqlSessionTemplate,其他不变) 2. 配置 mybatis-config.xml (不变) 3. 创建Mapper接口和对应的Mapper.xml (不变) 4. 增加Dao接口的实现类 ( 继承 SqlSessionDaoSupport类,直接可以使用getSqlSession()方法获取sqlSession对象) ```java package com.lindom.dao; import com.lindom.pojo.User; import org.mybatis.spring.support.SqlSessionDaoSupport; import java.util.List; //整合方式二: public class UserDaoImpl2 extends SqlSessionDaoSupport implements UserMapper { public List selectUser() { UserMapper mapper = getSqlSession().getMapper(UserMapper.class); List list = mapper.selectUser(); return list; } } ``` 5. 注册bean实现 ( applicationContext.xml ) ```xml ``` 6. 测试类 ```java public class MyTest { @Test //整合方式二: public void selectUser3(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserMapper mapper = (UserMapper) context.getBean("userDao2"); List list = mapper.selectUser(); for (User user : list) { System.out.println(user); } } } ``` ### 3. 声明式事务 *事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用* ``` 事务四个属性ACID 1. 原子性(atomicity) 事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用 2. 一致性(consistency) 一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中 3. 隔离性(isolation) 可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏 4. 持久性(durability) 事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中 ``` **Spring中的事务管理** ``` 1. 编程式事务管理 将事务管理代码嵌到业务方法中来控制事务的提交和回滚 缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码 2. 声明式事务管理 一般情况下比编程式事务好用。 将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。 将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务管 ``` **配置事务的流程** 配置spring配置文件 (bean.xml) 使用Spring管理事务,注意头文件的约束导入 : tx ```xml ``` *成功配置事务,这时我们执行一个包含增和删方法的事务,如果其中的一个方法出错,则整个事务都会回滚*