主要内容整合自狂神说spring课程
1. Spring简单介绍
1.1 Spring是什么
Spring是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架,是为了解决企业级应用开发的复杂性而创建的。
核心 | 描述 |
---|---|
IOC | Inverse of Control 的简写,译为“控制反转”,指把创建对象过程交给 Spring 进行管理。 |
AOP | Aspect Oriented Programming 的简写,译为“面向切面编程”。AOP 用来封装多个类的公共行为,将那些与业务无关,却为业务模块所共同调用的逻辑封装起来,减少系统的重复代码,降低模块间的耦合度。另外,AOP 还解决一些系统层面上的问题,比如日志、事务、权限等。 |
1.2 Spring的优点
- 方便解耦,简化开发
- 方便集成各种优秀框架
- 降低JavaEE API的使用难度
- 方便程序的测试
- AOP编程的支持
- 声明式事务的支持
1.3 Spring的架构模块
上图中包含了 Spring 框架的所有模块,这些模块可以满足一切企业级应用开发的需求,在开发过程中可以根据需求有选择性地使用所需要的模块。
2. 控制反转(IOC)原理讲解
2.1 概念解释
在传统的 Java 应用中,一个类想要调用另一个类中的属性或方法,通常会先在其代码中通过 new Object() 的方式将后者的对象创建出来,然后才能实现属性或方法的调用。为了方便理解和描述,我们可以将前者称为“调用者”,将后者称为“被调用者”。也就是说,调用者掌握着被调用者对象创建的控制权。
IoC 带来的最大改变不是代码层面的,而是从思想层面上发生了“主从换位”的改变。原本调用者是主动的一方,它想要使用什么资源就会主动出击,自己创建;但在 Spring 应用中,IoC 容器掌握着主动权,调用者则变成了被动的一方,被动的等待 IoC 容器创建它所需要的对象(Bean)。
这个过程在职责层面发生了控制权的反转,把原本调用者通过代码实现的对象的创建,反转给 IoC 容器来帮忙实现,因此我们将这个过程称为 Spring 的“控制反转”。
狂神说:控制反转,就是获得依赖对象的方式反转了
2.2 IOC原型——代码说明
首先new一个新的maven项目,在pom.xml中导包;在依赖处导入下面这个包
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.21</version>
</dependency>
</dependencies>
先创建dao层和service层,通过传统方式调用业务层;
在传统业务中,我们需要根据用户需求的变化来修改代码;而代码量越庞大,代码修改代价就会越高,因此我们需要避免这种情况。
传统业务层代码:
public class UserServiceImpl implements UserService{
//1----传统方法
private UserDao userDao = new UserDaoMysqlImpl();
@Override
public void getUser() {
userDao.getUser();
}
}
通过set接口实现:
public class UserServiceImpl implements UserService{
//2----set动态注入
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void getUser() {
userDao.getUser();
}
}
之前是程序主动创建对象,控制权掌握在程序员手上;
在set注入之后,程序不具有主动性,而是被动接受,主动权交给客户;
好处:降低系统耦合性,更好实现业务
2.3 如何实现IOC
解耦图解:
在 Spring 应用中,Java 对象创建的控制权是掌握在 IoC 容器手里的,其大致步骤如下。
- 开发人员通过 XML 配置文件、注解、Java 配置类等方式,对 Java 对象进行定义,例如在 XML 配置文件中使用 标签、在 Java 类上使用 @Component 注解等。
- Spring 启动时,IoC 容器会自动根据对象定义,将这些对象创建并管理起来。这些被 IoC 容器创建并管理的对象被称为 Spring Bean。
- 当我们想要使用某个 Bean 时,可以直接从 IoC 容器中获取(例如通过 ApplicationContext 的 getBean() 方法),而不需要手动通过代码(例如 new Obejct() 的方式)创建。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。
2.4 IOC思想的代码实现
2.4.1 HelloSpring
创建hello类—— 新建spring配置文件(xml)—— 新建测试类,获取spring的容器,通过容器获取所需的类
2.4.2 改造原型代码
增加set方法——新建配置文件(xml)—— 注册bean —— 新建测试类,获取spring的容器,通过容器获取所需的类
2.5 IOC创建对象的方式
1.默认方法是使用无参构造创建对象。
2.有参构造有三种方法
有参构造方法1 —————— 下标赋值
<bean id="user" class="com.spring.pojo.User">
<constructor-arg index="0" value="小明"/>
</bean>
有参构造方法2(不建议)—————— 通过类型赋值
<bean id="user" class="com.spring.pojo.User">
<!--class的值:基本类型可以直接用,引用类型需要全限定名 -->
<constructor-arg type="java.lang.String" value="小明"/>
</bean>
有参构造方式3 —————— 直接通过参数名赋值
<bean id="user" class="com.spring.pojo.User">
<constructor-arg name="name" value="小明"/>
</bean>
总结: 在配置文件加载时,spring容器中管理的所有对象都会被初始化;而对象的创建遵循单例模式。
3.Spring配置
3.1 别名
<!--可以使用别名获取到对象 -->
<alias name="user" alias="people"/>
3.2 Bean的配置
<!--
id : bean的唯一标识符,也就是相当于我们学的对象名
cLass : bean对象所对应的全限定名:包名+类型
name : 也是别名,可以取多个别名(比alias好用很多),多个别名之间可以通过 空格 逗号 分号 做分割
-->
<bean id="user" class="com.spring.pojo.User" name="user2,u2 u3;u4" scope="singleton">
</bean>
3.3 import
一般用于团队开放,可以将多个配置文件导入合并。
即使用时使用总的配置文件,在配置文件中导入其他配置文件
导入格式:
<import resource="beans.xml"/>
4. 依赖注入(DI)
4.1 构造器注入
4.2 set方式注入(重点)
set注入是依赖注入的本质
依赖:bean对象的创建依赖于容器
注入:bean对象中的所有属性,由容器来注入
4.3 拓展方式
P命名空间和c命名空间
配置文件(需要添加约束):
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
完整配置文件:
<?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- p命名空间注入,可以直接注入属性 -->
<bean id="user" class="com.spring.pojo.User" p:name="小明" p:age="18"/>
<!-- c命名注入,通过构造器注入:construct-args -->
<bean id="user2" class="com.spring.pojo.User" c:age="18" c:name="小明"/>
</beans>
4.4 bean作用域
5. bean的自动装配
- 自动装配是Spring满足bean依赖的一种方式
- Spring会在上下文中自动寻找,并自动给bean装配属性
Spring有三种装配方式
- 在xml中显示的配置
- 在java中显示配置
- 隐式的自动装配bean(重要)
5.1 ByName自动装配
需要保证bean的id唯一,并且与set方法的值一致
<!--自动装配1
byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanid
-->
<bean id="person" class="com.spring.pojo.Person" autowire="byName">
<property name="name" value="小明"/>
</bean>
5.1 ByType自动装配
需要保证所有bean的class唯一,类型和set方法的类型一致
<!--自动装配2
byType:会自动在容器上下文中查找,和自己对象set属性类型(class)相同的bean
-->
<bean id="person" class="com.spring.pojo.Person" autowire="byType">
<property name="name" value="小明"/>
</bean>
5.2 使用注解实现装配
注解相比于xml哪个方法更好,需要视情况而定
使用注解须知:
1.导入约束
2.配置注解的支持
3.指定要扫描的包,指定包下的注解才能生效
<?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"
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">
<context:annotation-config/>
</beans>
@Autowired
可以在属性上直接使用,也可以用于set方法上(更推荐)
在属性上使用时,set方法可以被省略,前提是自动装配的属性在IOC中存在
注:Autowired注解是按照类型(ByType)装配依赖对象,如果多个相同类型,可以搭配@Qualifier,再按照id(ByName)装配依赖对象
拓展
@Nullable 字段标记了这个注解,说明这个字段可以为null
public class Person {
// 如果显示定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为空
@Autowired(required = false)
private Cat cat;
@Autowired
private Dog dog;
}
如果装配环境复杂,有多个同类的对象需要被自动装配时(ByType无法处理);可以通过
@Qualifier(value=“id”)去辅助配置@Autowired的使用。
@Resource
public class People{
@Resource(name = "cat2")
private Cat cat;
@Resource
private Dog dog;
}
区别于@Autowire:@Resource默认使用ByName装配,如果找不到对应的id,则采用ByType的方式,如果找不到对应的类型或者对应的类型有两个及以上,则会报错
6.使用注解开发
要保证aop的包导入,需要导入context约束
@Component:组件,放在类上,说明这个类被spring管理了
而Component有几个衍生注解,根据web开发的mvc分层:
- dao [@Repository]
- service [@Service]
- controller [@Controller]
以上注解的功能都是把类注册到容器中
注解和xml的对比
- xml 更加万能,维护简单方便
- 注解 灵活性较差,维护相对复杂
最好的实践方式:
xml用于bean的管理,注解负责完成属性的注入。
根据使用环境和实际需求决定
7.使用JavaConfig实现配置
方法1:在配置类中定义一个方法,并使用@Bean注解声明。
public class User {
private String name;
public String getName() {
return name;
}
@Value("rax")
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
//@Import(UserConfig2.class)
public class UserConfig {
/*
注册一个bean,就相当于之前xml里面的一个bean标签,
这个方法的名字,就相当于bean标签中的id属性
这个方法的返回值,就相当于bean标签中的class属性
*/
@Bean
public User getUser(){
return new User();
}
}
public void test(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(UserConfig.class);
User bean = (User) context.getBean("getUser");
System.out.println(bean.getName());
}
方法2:在Developer类上使用@Component注解,并在配置类上先声明@Configuration,然后再声明@ComponentScan(“User类的路径”),这样会自动扫描@Component并生成Bean。
//@Component的目的是将该类和属性注入
@Component
public class Developer {
private String language;
public String getLanguage() {
return language;
}
@Value("java")
public void setLanguage(String language) {
this.language = language;
}
@Override
public String toString() {
return "Developer{" +
"language='" + language + '\'' +
'}';
}
}
//这个也会被Spring容器托管,注册到容器中,因为他本来就是一个@Component,@Configuration代表这是一个配置类,就和我们之前看到的beans.xml是一样的
@Configuration
@ComponentScan("com.spring.pojo")
public class DeveloperConfig {
}
public void test2(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DeveloperConfig.class);
Developer bean = (Developer) context.getBean("developer");
System.out.println(bean.getLanguage());
}
如果两种方法都使用,会建两个对象,@Component建立的对象用getBean(“user”)获取,配置类中@Bean声明的用getBean(“getUser”)获取,这两个对象是不同的