> [TOC] # 1、Spring *** ## 1.1、简介 - Spring:春天------>给软件行业带来了春天! - 2002,首次推出了Spring框架的雏形:interface21框架! - Spring框架即以interface21框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日发布了1.0正式版。 - **Rod Johnson**,Spring Framework创始人,著名作者。很难想象Rod Johnson的学历,真的让好多人大吃一惊,他是悉尼大学的博士,然而他的专业不是计算机,而是音乐学。 - Spring理念:使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架! - SSH:Struct2 + Spring + Hibernate! - SSM:SpringMVC + Spring + Mybatis! 官网:https://spring.io/projects/spring-framework#overview 官方下载地址:https://repo.spring.io/release/org/springframework/spring/ GitHub:https://github.com/spring-projects/spring-framework ```xml org.springframework spring-webmvc 5.3.8 org.springframework spring-jdbc 5.3.8 ``` ## 1.2 优点 - Spring是一个开源的免费的框架(容器)! - Spring是一个轻量级的、非入侵式的框架! - 控制反转(IOC),面向切面编程(AOP)! - 支持事务的处理,对框架整合的支持! **总结一句话:Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架!** ## 1.3、组成 ![image-20220319142033626](assets/image-20220319142033626.png) ## 1.4 拓展 现代化的Java开发!说白了就是基于Spring的开发! - Spring Boot - 一个快速开发的脚手架 - 基于SpringBoot可以快速的开发单个微服务 - 约定大于配置 - Spring Cloud - SpringCloud是基于SpringBoot实现的 因为现在大多数公司都在使用SpringBoot进行快速开发,学习SpringBoot的前提,需要完全掌握Spring以及SpringMVC!**承上启下的作用!** **弊端:发展了太久之后,违背了原来的理念!配置十分繁琐,人称:“配置地狱”** # 2、IOC理论推导 *** 1. UserDao 接口 ```java public interface UserDao { void getUser(); } ``` 2. UserDaoImpl 实现类 ```java public class UserDaoImpl implements UserDao { public void getUser() { System.out.println("默认获取用户数据"); } } ``` 3. UserService 业务接口 ```java public interface UserService { void getUser(); } ``` 4. UserServiceImpl 业务实现类 ```java public class UserServiceImpl implements UserService { private UserDao userDao = new UserDaoImpl(); public void getUser() { userDao.getUser(); } } ``` 5. 测试 ```java public class MyTest { public static void main(String[] args) { //用户实际调用的是业务层,dao层他们不需要接触! UserService userService = new UserServiceImpl(); userService.getUser(); } } ``` 在我们之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改原代码!如果程序代码量十分大,修改一次的成本代价十分昂贵! ![image-20220319144549608](assets/image-20220319144549608.png) 我们使用一个Set接口实现,已经发生了革命性的变化! ![image-20220319144559312](assets/image-20220319144559312.png) ```java private UserDao userDao; // 利用set进行动态实现值的注入! public void setUserDao(UserDao userDao) { this.userDao = userDao; } ``` - 之前,程序是主动创建对象!控制权在程序员手上! - 使用了set注入后,程序不再具有主动性,而是变成了被动的接收对象! 这种思想,从本质上解决了问题,我们程序猿不用再去管理对象的创建了。系统的耦合性大大降低~,可以更加专注的在业务的实现上!这是IOC的原型! **IOC本质** **控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,**也有人认为DI只是IoC的另一种说法。没有IoC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。 采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。 **控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。** # 3、HelloSpring *** 1. 新建一个maven项目,编写实体类 ```java public class Hello { private String str; public String getStr() { return str; } public void setStr(String str) { this.str = str; } @Override public String toString() { return "Hello{" + "str='" + str + '\'' + '}'; } } ``` 2. 编写xml配置文件 ```xml ``` 3. 测试 ```java public class MyTest { public static void main(String[] args) { //获取Spring的上下文对象! ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); //我们的对象现在都在Spring中的管理了,我们需要使用,直接去里面取出来就可以! Hello hello = (Hello) context.getBean("hello"); System.out.println(hello.toString()); } } ``` **思考问题?** - Hello对象是谁创建的? Hello对象是由Spring创建的。 - Hello对象的属性是怎么设置的? Hello对象的属性是由Spring容器设置的。 这个过程就叫控制反转: **控制**:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的。 **反转**:程序本身不创建对象,而变成被动的接收对象。 **依赖注入**:就是利用set方法来进行注入的。 IOC是一种编程思想,由主动的编程变成被动的接收。 可以通过new ClassPathXmlApplicationContext去浏览一下底层源码。 **OK,到了现在,我们彻底不用在程序中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改,所谓的IOC,一句话搞定:对象由Spring来创建,管理,装配!** # 4、IOC创建对象的方式 *** 1. 使用无参构造创建对象,默认! 2. 假设我们要使用有参构造创建对象。 1. 下标赋值 ```xml ``` 2. 类型 ```xml ``` 3. 参数名 ```xml ``` 总结:在配置文件加载的时候,容器中管理的所有对象就已经初始化了!只有一份!!! ```java ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); User user = (User) context.getBean("user"); user.setName("123"); User user2 = (User) context.getBean("user"); System.out.println(user == user2); // true System.out.println(user2.getName()); // 123 ``` # 5、Spring配置 *** ## 5.1 别名 ```xml ``` ## 5.2 Bean配置 ```xml ``` ## 5.3 import 这个import。一般用于团队开发使用,它可以将多个配置文件,导入合并为一个。 假设,现在项目中有多个人开发,这三个人负责不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的! - 张三 - 李四 - 王五 - applicationContext.xml ```xml ``` 使用的时候,直接使用总的配置就可以了。 # 6、依赖注入 *** ## 6.1 、构造器注入 前面已经介绍过,参考**4、IOC创建对象的方式** ## 6.2 、Set方式注入【重点】 - 依赖注入:Set注入 - 依赖:bean对象的创建依赖于容器! - 注入:bean对象中的所有属性,由容器来注入! 【环境搭建】 1. 复杂类型 ```java public class Address { private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } } ``` 2. 真实测试对象 ```java public class Student { private String name; private Address address; private String[] books; private List hobbies; private Map card; private Set games; private String wife; private Properties info; } ``` 3. beans.xml ```xml ``` 4. 测试类 ```java public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); Student student = (Student) context.getBean("student"); System.out.println(student.getName()); } } ``` 5. 完善注入信息 ```xml 红楼梦 西游记 水浒传 三国演义 打篮球 看电影 敲代码 LOL COC BOB 20191029 102.0913.524.4585 黑心白莲 123456 ``` ## 6.3 、拓展方式注入 我们可以使用p命名空间和c命名空间进行注入 官方解释: ![在这里插入图片描述](assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xpNjQzOTM3NTc5,size_16,color_FFFFFF,t_70#pic_center.png) 使用: ```xml ``` 测试: ```java @Test public void test2(){ ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml"); User user = context.getBean("user",User.class); System.out.println(user); User user2 = context.getBean("user2",User.class); System.out.println(user2); } ``` 注意点:p命名空间和c命名空间不能直接使用,需要导入xml约束 ```xml xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" ``` ## 6.4 、bean的作用域 ![在这里插入图片描述](assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xpNjQzOTM3NTc5,size_16,color_FFFFFF,t_70#pic_center.png) 1. 单例模式(Spring默认机制) ```xml ``` 2. 原型模式:每次从容器中get的时候,都会产生一个新对象! ```xml ``` 3. 其余的request、session、application、这些只能在web开发中用到! # 7、bean的自动装配 *** - 自动装配是Spring满足bean依赖的一种方式 - Spring会在上下文中自动寻找,并自动给bean装配属性 在Spring中有三种装配的方式: 1. 在xml中显式的配置; 2. 在java中显式配置; 3. 隐式的自动装配bean【重要】 ## 7.1 、测试 环境搭建:创建项目,一个人有两个宠物! ```xml ``` ## 7.2、 ByName自动装配 ```xml ``` ## 7.3 、ByType自动装配 ```xml ``` 小结: - ByName的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致! - ByType的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致! ## 7.4 、使用注解实现自动装配 jdk1.5支持的注解,Spring2.5就支持注解了! 要使用注解须知: 1. 导入约束 2. 配置注解的支持 ```xml ``` **@Autowired** 直接在属性上使用即可!也可以在set方法上使用! 使用Autowried我们就可以不用编写set方法了,前提是你这个自动配置的属性在IOC(Spring)容器中存在,且符合名字ByName! **科普:** > @Nullable 字段标记了了这个注解,说明这个字段可以为null; ```java public @interface Autowired { boolean required() default true; } ``` 测试代码 ```java public class People { //如果显式定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为空 @Autowired(required = false) private Cat cat; @Autowired private Dog dog; private String name; } ``` 如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候,我们可以使用@Qualifier(value = “xxx”)去配置@Autowired的使用,指定一个唯一的bean对象注入! ```java public class People { @Autowired @Qualifier(value = "cat111") private Cat cat; @Autowired @Qualifier(value = "dog222") private Dog dog; private String name; } ``` **@Resource** ```java public class People { @Resource private Cat cat; @Resource private Dog dog; } ``` 小结: @Resource和@Autowired的区别: - 都是用来自动装配的,都可以放在属性字段上 - @Autowired通过byType的方式实现,而且必须要求这个对象存在!【常用】 - @Resource默认通过byName的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到的情况下,就报错!【常用】 - 执行顺序不同:@Autowired通过byType的方式实现。@Resource默认通过byName的方式实现 # 8、使用注解开发 *** 在Spring4之后,要使用注解开发,必须要保证aop的包导入了 ![image-20220320142136644](assets/image-20220320142136644.png) 使用注解需要导入约束,配置注解的支持! ```xml ``` > **Bean的实现** 我们之前都是使用 bean 的标签进行bean注入,但是实际开发中,我们一般都会使用注解! 1. 配置扫描那些包下的注解 ```xml ``` 2. 在指定包下编写类,增加注释 ```java @Component("user") // 相当于配置文件中 public class User { public String name = "秦疆"; } ``` 3. 测试 ```java @Test public void test(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); User user = (User) applicationContext.getBean("user"); System.out.println(user.name); } ``` > **属性注入** 使用注解注入属性 1. 可以不用提供set方法,直接在直接名上添加@value("值") ```java @Component("user") // 相当于配置文件中 public class User { @Value("秦疆") // 相当于配置文件中 public String name; } ``` 2. 如果提供了set方法,在set方法上添加@value("值") ```java @Component("user") public class User { public String name; @Value("秦疆") public void setName(String name) { this.name = name; } } ``` > **衍生注解** 我们这些注解,就是替代了在配置文件当中配置步骤而已!更加的方便快捷! **@Component三个衍生注解** 为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。 - @Controller: web层 - @Service:service层 - @Repository:dao层 写上这些注释,就相当于将这个类交给Spring管理装配了! > **自动装配注解** 在Bean的自动装配已经讲过了,可以回顾! - @Autowired:自动装配通过类型,名字。如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value = "xxx")去配置。 - @Nullable 字段标记了了这个注解,说明这个字段可以为null; - @Resource:自动装配通过名字,类型。 > **作用域** @scope - singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。 - prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收 ```java @Component @Scope("singleton") public class User { //相当于 @Value("白莲") public String name; } ``` > **小结** **xml与注解:** - xml更加万能,适用于任何场合!维护简单方便 - 注解不是自己类使用不了,维护相队复杂! **xml与注解最佳实践:** - xml用来管理bean; - 注解只负责完成属性的注入; - 我们在使用的过程中,只需要注意一个问题:必须让注解生效,就需要开启注解的支持 ```xml ``` 作用: - 进行注解驱动注册,从而使注解生效 - 用于激活那些已经在spring容器里注册过的bean上面的注解,也就是显示的向Spring注册 - 如果不扫描包,就需要手动配置bean - 如果不加注解驱动,则注入的值为null! # 9、使用Java的方式配置Spring *** JavaConfig 原来是 Spring 的一个子项目,它通过 Java 类的方式提供 Bean 的定义信息,在 Spring4 的版本, JavaConfig 已正式成为 Spring4 的核心功能 。 实体类 ```java //这里这个注解的意思,就是说明这个类被Spring接管了,注册到了容器中 @Component public class User { private String name; public String getName() { return name; } @Value("黑心白莲") //属性注入值 public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "name='" + name + '\'' + '}'; } } ``` 配置文件 ```java // 这个也会Spring容器托管,注册到容器中,因为它本来就是一个@Component // @Configuration代表这是一个配置类,就和我们之前看的beans.xml @Configuration @ComponentScan("com.kuang.pojo") // 引入多个配置类,合并 @Import(KuangConfig2.class) public class KuangConfig { // 注册一个bean,就相当于我们之前写的一个bean标签 // 这个方法的名字,就相当于bean标签中id属性 // 这个方法的返回值,就相当于bean标签中的class属性 @Bean public User user(){ return new User(); // 就是返回要注入到bean的对象! } } ``` 测试类 ```java public class MyTest { public static void main(String[] args) { //如果完全使用了配置类方式去做,我们就只能通过 AnnotationConfig 上下文来获取容器,通过配置类的class对象加载! ApplicationContext context = new AnnotationConfigApplicationContext(KuangConfig.class); User user = context.getBean("user", User.class); System.out.println(user.getName()); } } ``` 这种纯Java的配置方式,在SpringBoot中随处可见!