Spring
简介
Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。
然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。
- 2002,首次推出了Spring框架的雏形:interface21框架。
- Spring框架即以interface21框架为基础,经过重新设计,并不断丰富其内涵,与2004年3月24日发布了1.0正式版!
- Rod Johnson,Spring框架的创始人,同时也是SpringSource的联合创始人。Spring是面向切面编程(AOP)和控制反转(IoC)的容器框架。
- Spring的基本理念:使现有的技术更加容易地使用,其本身就是一个大杂烩,整合了现有的技术框架。
- SSH:Struts2+Spring+Hibernate
- SSM:SpringMVC+Spring+Mybatis
官网:https://spring.io/
GitHub:https://github.com/spring-projects/spring-framework/find/main
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.16</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.16</version>
</dependency>
优点:
- Spring是一个开源的免费容器(框架)。
- Spring是一个轻量级的、非入侵式的框架。
- 控制反转(IOC),面向切面编程(AOP)。
- 支持事务的处理,对框架整合的支持!
缺点:
- 因为发展了很久,导致现在糅杂了太多的技术与框架,使得配置变得十分繁琐,人称配置地域!
小结:Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架!
Spring7大模块:
扩展
- Spring Boot
- 一个快速开发的脚手架。
- 基于SpringBoot可以快速开发单个微服务。
- 约定大于配置。
- Spring Cloud
- SpringCloud是基于SpringBoot实现的。
因为现在大多数公司都在使用SpringBoot进行快速开发,学习SpringBoot的前提,需要完全掌握Spring和SpringMVC。
1、IOC理论推导
传统Dao层与服务层:
public interface UserDao {
void getUser();
}
public class UserDaoImpl implements UserDao {
@Override
public void getUser() {
System.out.println("默认调用UserDao接口!");
}
}
public interface UserService {
void getUser();
}
public class UserServiceImpl implements UserService {
UserDao userDao = new UserDaoImpl();
@Override
public void getUser() {
userDao.getUser();
}
}
此时,用户实际是调用了Service层的业务,但当用户需求有变动时,例如此时用户并不想要调用默认的UserDao接口,而是想要调用特殊的接口去链接Oracle数据库,解决方法可以是重新写一个UserDao的实现类,继承接口后重写方法。但是在代码量十分庞大的情况下,这种措施无疑是最坏的做法。
假设此时有一个UserDao实现类:
public class UserDaoOracleImpl implements UserDao {
@Override
public void getUser() {
System.out.println("Oracle调用UserDao接口!");
}
}
而这次,并不直接创建一个新的业务实现类,而是在原有的业务实现类进行一些修改:
public class UserServiceImpl implements UserService {
private UserDao userDao;
// 利用Set进行动态实现值的注入!
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void getUser() {
userDao.getUser();
}
}
可以发现,以上实例将UserDao作为业务层的属性,并重写了set方法,于是在测试类中,可以这样进行测试:
public class UserServiceTest {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDao(new UserDaoOracleImpl());
userService.getUser();
}
}
运行的结果并无二异,但此时可以通过setUserDao方法,可以自行选择走哪个UserDao,代码虽然看上去没有进行多大的变动,但是逻辑性却已经发生了翻天覆地的变化。
- 在更改之前:程序是主动创建的对象,控制权在程序员手中!
- 使用了Set注入之后:程序不再具有主动性,而是变成了被动接受的对象!
也就是说,这种思想已经从本质上解决了问题,程序员不再需要去管理对象的创建,系统的耦合性大大降低,可以更加专注于业务的实现!这也就是IOC的原型!
1.1 IOC的本质
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。
Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。
1.2 HelloSpring
1、准备元数据:
@Data
public class Hello {
private String str;
}
2、编写beans.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- id相当于一个变量名, class为需要实例化的对象-->
<!-- property为对象中的属性,而value相当于为属性设置了一个值 -->
<!-- 使用Spring来创建对象,在Spring中这些都成为Bean -->
<bean id="Hello" class="com.atayin.pojo.Hello">
<property name="str" value="Spring"/>
</bean>
</beans>
3、实例化容器
public class HelloTest {
public static void main(String[] args) {
// 获取spring的上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 取出对象
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello);
}
}
思考:
-
Hello 对象是谁创建的 ?
hello 对象是由Spring创建的。
-
Hello 对象的属性是怎么设置的 ?
hello 对象的属性是由Spring容器设置的。
这个过程就叫控制反转 :
-
控制 : 谁来控制对象的创建 , 传统应用程序的对象是由程序本身控制创建的 , 使用Spring后 , 对象是由Spring来创建的 .
-
反转 : 程序本身不创建对象 , 而变成被动的接收对象 。
依赖注入 : 就是利用set方法来进行注入的。假设实体类没有set方法,那么配置文件会报错!
IOC是一种编程思想 , 由主动的编程变成被动的接收,也可以通过newClassPathXmlApplicationContext去浏览一下底层源码。
所谓的IoC,对象由Spring 来创建,管理,装配 !
1.3 IOC创建对象的方式
1、IOC创建对象的默认方式为使用无参构造方法创建对象,当实体类不存在无参构造时,会报错!
<bean id="user" class="com.atayin.pojo.User">
<property name="name" value="Ayin"/>
</bean>
2、假设需要使用有参构造方法创建对像,一共有以下三种方式:
- 使用下标赋值:
<bean id="user" class="com.atayin.pojo.User">
<constructor-arg index="0" value="Ayin"/>
</bean>
- 使用类型赋值:这种方式并不建议使用,当构造方法拥有数个相同类型的参数时,使用该方法将会出错!
<bean id="user" class="com.atayin.pojo.User">
<constructor-arg type="java.lang.String" value="Ayin"/>
</bean>
- 使用参数名赋值:
<bean id="user" class="com.atayin.pojo.User">
<constructor-arg name="name" value="Ayin"/>
</bean>
2、Spring配置
别名
<alias name="user" alias="ksjfksdjaf"/>
添加别名之后,可以通过别名获取到这个对象:
ApplicationContext context = new ClassPathXmlApplicationContext();
context.getBean("ksjfksdjaf");
当然,并不是取了别名之后,原本的属性名就失效了,调用原属性名依然可以取到这个对象!
Bean
<bean id="user" class="com.atayin.pojo.User" name="user2,u2"></bean>
在bean标签中,name属性也可以取别名,也可以起多个别名。
import
这个标签可以将多个配置文件,导入合并为一个applicationConfig.xml!
<import resource="beans1.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>
3、依赖注入
依赖:bean对象的创建依赖于容器!
注入:bean对象中所有的属性,由容器来注入!
映射键或值的值或设置值也可以是以下任一元素:
bean | ref | idref | list | set | map | props | value | null
环境搭建
复杂对象:
@Data
public class Address {
private Address address;
}
真实测试对象:
@Data
public class Student {
private String name;
private Address address;
private String[] book;
private List<String> hobbys;
private Map<String, String> card;
private Set<String> games;
private String wife;
private Properties info;
}
注入:
<bean id="address" class="com.atayin.pojo.Address">
<property name="address" value="北京"/>
</bean>
<bean id="student" class="com.atayin.pojo.Student">
<!-- 1、普通值注入,value-->
<property name="name" value="Ayin"/>
<!-- 2、Bean注入,ref-->
<property name="address" ref="address"/>
<!-- 数组-->
<property name="book" >
<array>
<value>红楼梦</value>
<value>西游记</value>
<value>三国演义</value>
</array>
</property>
<!-- List-->
<property name="hobbys">
<list>
<value>唱</value>
<value>跳</value>
</list>
</property>
<!-- Map-->
<property name="card">
<map>
<entry key="身份证" v