Introduction to Spring

I am not Euler_ 2022-09-23 09:26:48 阅读数:289

introductionspring

1. Spring是什么?

Spring是一个开放源代码的设计层面框架,解决业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用.

Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架.

2. Spring框架特点

  • Springis a lightweight and non-intrusive open source framework .
  • SpringA container that contains and manages the configuration and life cycle of application objects.
  • SpringIt is possible to configure simple components、compose frameworks for complex applications.
  • Spring通过控制反转(IoC) 促进了低耦合.
  • Spring提供了面向切面编程(AOP) 的丰富支持,允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发.

3. Spring框架组成

Spring 框架是一个分层架构,由 7 个定义良好的模块组成.Spring 模块构建在核心容器之上,The core container defines创建、配置和管理 bean 的方式.

组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现.每个模块的功能如下:

Spring Core(核心容器):核心容器提供 Spring 框架的基本功能.核心容器的主要组件是 BeanFactory,它是工厂模式的实现.BeanFactory 使用控制反转(IOC)模式将应用程序的配置和依赖性规范与实际的应用程序代码分开.

Spring Context(上下文):Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息.Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能.

Spring AOP(面向切片):通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能 , 集成到了 Spring 框架中.所以,可以很容易地使 Spring 框架管理任何支持 AOP的对象.Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务.通过使用 Spring AOP,不用依赖组件,就可以将声明性事务Management integrated into the application.

Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息.异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接).Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构.

Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map.所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构.

Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文.所以,Spring 框架支持与 Jakarta Struts 的集成.Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作.

Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现.通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI.

important expansion:Spring Boot与Spring Cloud

  • Spring Boot 是 Spring 的一套快速配置脚手架,可以基于Spring Boot 快速开发单个微服务.
  • Spring Cloud是基于Spring Boot实现的.
  • Spring Boot专注于快速、方便集成的单个微服务个体,Spring Cloud关注全局的服务治理框架.
  • Spring Boot使用了约束优于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置 , Spring Cloud很大的一部分是基于Spring Boot来实现,Spring Boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud离不开Spring Boot,属于依赖的关系.
  • SpringBoot在SpringClound中起到了承上启下的作用,如果你要学习SpringCloud必须要学习SpringBoot.

4. 控制反转(IOC)

控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法.没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转(IOC)Then transfer the creation of the object to the third-party caller,Inversion of Control can be thought of as the inversion of the way to obtain dependent objects.

通俗来讲,以前所有东西都是由程序去进行控制创建(代码写死,直接实例化对象), And now is controlled by the caller to create the object(Objects are passed in by the caller as required),把主动权交给了调用者,程序不用去管怎么创建,怎么实现了,它只负责提供一个接口.

4.1 Spring IOC容器

官方解释:

The org.springframework.context.ApplicationContext interface represents the Spring IoC container and is responsible for instantiating, configuring, and assembling the beans.

翻译:Spring IOC容器就是一个org.springframework.context.ApplicationContext的实例化对象,容器负责了实例化,配置以及装配一个bean

从代码层次来看:Spring IOC容器就是一个实现了ApplicationContext接口的对象,

从功能上来看: Spring 容器是 Spring 框架的核心,是用来管理对象的.容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁.

4.1.1 Spring IOC容器如何工作

IoC是Spring框架的核心内容,可以通过使用XML配置,Annotations can also be used to achieveIoC,新版本的Spring也可以零配置实现IoC.

Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象.

采用XML方式配置Bean的时候,BeanThe definition of information is and realize the separation,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的.

4.2 如何实例化Bean

从官网上来看,主要有以下三种方法:

  1. 构造方法
  2. 通过静态工厂方法
  3. 通过实例工厂方法 

Create with simple analysisbean实例的方法

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// 1.获取这个bean的class属性,确保beanDefinition中beanClass属性已经完成解析
// 我们通过xml从<bean>标签中解析出来的class属性在刚刚开始的时候必定是个字符串
Class<?> beanClass = resolveBeanClass(mbd, beanName);
// 省略异常判断代码.....
// 2.通过beanDefinition中的supplier实例化这个bean
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
// 3.通过FactoryMethod实例化这个bean
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// 4.下面这段代码都是在通过构造函数实例化这个Bean,分两种情况,一种是通过默认的无参构造,One is through the inferred constructor
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
if (resolved) {
if (autowireNecessary) {
return autowireConstructor(beanName, mbd, null, null);
}
else {
return instantiateBean(beanName, mbd);
}
}
// Candidate constructors for autowiring?
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
// Preferred constructors for default construction?
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
return autowireConstructor(beanName, mbd, ctors, null);
}
// No special handling: simply use no-arg constructor.
return instantiateBean(beanName, mbd);
}

The instantiation can be found by the above codebean的方法主要分为三类:

1. 通过instanceSupplier获取,这个方法一般不常用,平常我们也使用不到,就不做过多探究,笔者认为,这应该是Spring提供的一种方便外部扩展的手段,让开发者能够更加灵活的实例化一个bean.

2. FactoryMethod,Debugging through breakpoints can be [email protected]、@Service、普通XML的方式(同@compent注解)、@Configuration等方式创建bean都是走instantiateUsingFactoryMethod(beanName, mbd, args)方法,Through the factory method to instantiatebean(Including static factory methods and instance factory methods).

public static void main(String[] args) {
ClassPathXmlApplicationContext cc =
new ClassPathXmlApplicationContext("application.xml");
System.out.println(cc.getBean("service"));
}
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- <bean id="myServiceImpl" class="com.dmz.official.service.Service"/>-->
<!-- the factory bean, which contains a method called get() -->
<bean id="myFactoryBean" class="com.dmz.official.service.MyFactoryBean">
<!-- inject any dependencies required by this locator bean -->
</bean>
<!-- 测试实例工厂方法创建对象-->
<bean id="clientService"
factory-bean="myFactoryBean"
factory-method="get"/>
<!--测试静态工厂方法创建对象-->
<bean id="service"
class="com.dmz.official.service.MyFactoryBean"
factory-method="staticGet"/>
</beans>

通过静态工厂方法这种方式特殊之处在于,包含这个静态方法的类,不需要实例化,不需要被Spring管理.Spring的调用逻辑大概是:

  1. 通过<bean>标签中的class属性得到一个Class对象
  2. 通过Class对象获取到对应的方法名称的Method对象
  3. 最后反射调用Method.invoke(null,args)获取bean

3. 构造方法.

4.3 实例化总结

1. 对象实例化,只是得到一个对象,还不是一个完全的Spring中的Bean,我们实例化后的这个对象还没有完成依赖注入,没有走完一系列的声明周期,这里需要大家注意.

2. Spring官网上指明了,在Spring中实例化一个对象有三种方式:

  • 构造函数
  • 实例工厂方法
  • 静态工厂方法

3. 流程图

5. 依赖注入(DI)

5.1 依赖注入概念

依赖注入(Dependency Injection,DI):

  • 依赖 : 指Bean对象的创建依赖于容器.
  • 注入 : 指Bean对象所依赖的资源 , 由容器来设置和装配.

5.2 依赖注入方式

依赖注入主要分为两种方式:

官方解释:

  • 构造函数注入
  • Setter方法注入

5.3 代码测试

public class Main02 {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new
// config类主要完成对类的扫描
AnnotationConfigApplicationContext(Config.class);
Service service = (Service) ac.getBean("service");
service.test();
}
}
// setter方法注入
@Component
public class Service {
private LuBanService luBanService;
public Service() {
System.out.println("service create");
}
public void test(){
System.out.println(luBanService);
}
// 通过autowired指定使用set方法完成注入
@Autowired
public void setLuBanService(LuBanService luBanService) {
System.out.println("注入luBanService by setter");
this.luBanService = luBanService;
}
}
// 构造方法注入
@Component
public class Service {
private LuBanService luBanService;
public Service() {
System.out.println("service create by no args constructor");
}
// 通过Autowired指定使用这个构造函数,否则默认会使用无参
@Autowired
public Service(LuBanService luBanService) {
System.out.println("注入luBanService by constructor with arg");
this.luBanService = luBanService;
System.out.println("service create by constructor with arg");
}
public void test(){
System.out.println(luBanService);
}
}
@Component
public class LuBanService {
LuBanService(){
System.out.println("luBan create ");
}
}

上述代码中,@Autowired 注解加在setter Methods and the difference of property field:

1. 直接添加@Autowired注解到字段上,不需要提供setter方法也能完成注入.以上面的例子来说,Spring会通过反射获取到Service中luBanService这个字段,然后通过反射包的方法,Filed.set(Service,luBanService)这种方式来完成注入.

2. 将@Autowired添加到setter方法时,我们可以通过断点看一下方法的调用栈,如下:

对于这种方式来说,最终是通过Method.invoke(object,args)的方式来完成注入的,这里的method对象就是我们的setter方法.

At the same time using tectonic injection attributes injection?

Spring虽然能在构造函数里完成属性注入,但是这属于实例化对象阶段做的事情,那么在后面真正进行属性注入的时候,Property injection overrides constructor injected properties.

总结:Constructor injection and property injection use official recommendations:

1. 构造函数注入跟setter方法注入可以混用,对于一些强制的依赖,我们最好使用构造函数注入,对于一些可选依赖我们可以采用setter方法注入.

2. Spring团队推荐使用构造函数的方式完成注入.但是对于一些参数过长的构造函数,Spring是不推荐的.

5.3 方法注入

5.3.1 Bean的作用域

类别说明

Singleton

在Spring IoC容器中仅存在一个Bean实例,Bean以单例方式存在,Spring IoCAll objects in the container are singleton by default

Prototype

原型模式,每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行new XxxBean()

Request

每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境

Session

同一个Http Session 共享一个Bean,不同的Session use withoutBean,仅适用于WebApplicationContext环境

5.3.2 method injection

有了上面BeanAfter the scope of the basic knowledge,can understand the role of method injection.

@Component
public class MyService {
@Autowired
private LuBanService luBanService;
public void test(int a){
luBanService.addAndPrint(a);
}
}
@Component
// 原型对象
@Scope("prototype")
public class LuBanService {
int i;
LuBanService() {
System.out.println("luBan create ");
}
// 每次将当前对象的属性i+a然后打印
public void addAndPrint(int a) {
i+=a;
System.out.println(i);
}
}
public class Main02 {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
MyService service = (MyService) ac.getBean("myService");
service.test(1);
service.test(2);
service.test(3);
}
}

如上述代码,Our goal is to get a different LuBanService 对象,However, it is actually called every timeLuBanService是同一个对象.当然,这也很好理解,因为在依赖注入阶段我们就完成了LuBanService的注入,之后我们在调用测试方法时,不会再去进行注入,所以我们一直使用的是同一个对象.

The role of method injection is to solve this problem,每次使用BeanWhen to go to get.

实现方式:

1. 通过注入上下文(applicationContext对象)获取Bean

// 实现org.springframework.context.ApplicationContextAware接口
@Component
public class MyService implements ApplicationContextAware {
private ApplicationContext applicationContext;
public void test(int a) {
LuBanService luBanService = ((LuBanService) applicationContext.getBean("luBanService"));
luBanService.addAndPrint(a);
}
@Override
public void setApplicationContext(@Nullable ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
// 直接注入上下文
@Component
public class MyService{
@Autowired
private ApplicationContext applicationContext;
public void test(int a) {
LuBanService luBanService = ((LuBanService) applicationContext.getBean("luBanService"));
luBanService.addAndPrint(a);
}
}

2. 通过@LookUp的方式(也分为注解跟XML两种方式,这里只演示注解的)

@Component
public class MyService{
public void test(int a) {
LuBanService luBanService = lookUp();
luBanService.addAndPrint(a);
}
@Lookup
public LuBanService lookUp(){
return null;
}
}

3. 方法注入 之 replace-method(使用很少)

5.4 依赖注入总结

SpringDependency injection in is property injection.

  • 我们知道一个对象由两部分组成:属性+行为(方法),可以说Spring通过属性注入+方法注入的方式掌控的整个bean.
  • 属性注入跟方法注入都是Spring提供给我们用来处理Bean之间协作关系的手段.
  • 属性注入有两种方式:构造函数,Setter方法.
  • 方法注入(LookUp Method跟Replace Method)需要依赖动态代理完成.
  • 方法注入对属性注入进行了一定程度上的补充,因为属性注入的情况下,原型对象可能会失去原型的意义.

6. 自动注入

官方说明:

翻译:

Spring可以自动注入互相协作的bean之间的依赖.自动注入有以下两个好处:

  • 自动注入能显著的减少我们指定属性或构造参数的必要,That is, you can inject related dependent objects into classes without dependency injection..
  • 自动装配可以随着对象的演化更新配置.例如,如果需要向类添加依赖项,则可以自动满足该依赖项,而不需要修改配置.因此,自动装配在开发过程中特别有用,但是当我们的代码库变的稳定时,Automatic assembly will also will not affect our assembly way to switch to the precise injection.

Automatic injection of four models:

模型解释
no

这是目前Spring默认的注入模型,也可以说默认情况下Spring是关闭自动注入,必须要我们通过setter方法或者构造函数完成依赖注入,并且Spring也不推荐修改默认配置.It is recommended that we inject dependencies in a precise way.

byName

这种方式,我们为了让Spring完成自动注入需要提供两个条件:

  1. 提供setter方法
  2. 如果需要注入的属性为xxx,那么setter方法命名必须是setXxx,也就是说,命名必须规范

在找不到对应名称的bean的情况下,Spring也不会报错,只是不会给我们完成注入.

byType

测试代码跟之前唯一不同的就是修改配置autowire="byType",这里我们测试以下三种异常情况:

  1. 找不到合适类型的bean,发现不报异常,同时不进行注入
  2. 找到了多个合适类型的bean,Spring会直接报错Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.dmz.official.service.DmzService' available: expected single matching bean but found 2: dmzService,dmzService2
  3. set方法中有两个参数,切两个参数都能找到唯一一个类型符合的bean,不报异常,也不进行注入

另外需要说明的是,我在测试的过程,将set方法仅仅命名为set,像这样public void set(DmzService dmzService),这种情况下Spring也不会进行注入.

我们可以发现,对于这两种注入模型都是依赖setter方法完成注入的,并且对setter方法命名有一定要求(只要我们平常遵从代码书写规范,一般也不会踩到这些坑).第一,不能有多个参数;第二,不能仅仅命名为set

constructor当我们使用这种注入模型时,Spring会根据构造函数查找有没有对应参数名称的bean,有的话完成注入(跟前文的byName差不多),如果根据名称没找到,那么它会再根据类型进行查找,如果根据类型还是没找到,就会报错.

自动注入的缺陷:

1. 精确注入会覆盖自动注入.并且我们不能注入基本数据类型,字符串,Class类型(这些数据的数组也不行).而且这是Spring故意这样设计的;

2. 自动注入不如精确注入准确.而且我们在使用自动注入时,对象之间的依赖关系不明确;

3. 对于一些为Spring容器生成文档的工具,无法获取依赖关系;

4. 容器中的多个bean定义可能会与自动注入的setter方法或构造函数参数指定的类型匹配.对于数组、集合或映射实例,这可能不会产生什么问题.但是,对于期望单个值的依赖项,我们无法随意确定到底有谁进行注入.如果没有唯一的bean定义可用,则会抛出异常.

Add some knowledge of annotations:

@Autowired 与 @Qualifier

1. @Autowired(required=false) 说明:false,对象可以为null;true,对象必须存对象,不能为null.

2. @Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配.

3. @Qualifier不能单独使用.

@Resource

1. @Resource如有指定的name属性,specified by this attributename进行byName方式查找装配;其次再进行默认的byName方式进行装配.
2. 如果以上都不成功,则按byType的方式自动装配.
3. 都不成功,则报异常.

@Autowired与@Resource异同:

1. @Autowired与@Resource都可以用来装配bean.can be written on the property,或写在setter方法上.
2. @Autowired 默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用.
3. @Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定.如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配.当找不到与名称匹配的bean时才按照类型进行装配.但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配.
4. 它们的作用相同都是用注解方式注入对象,但执行顺序不同[email protected]先byType,@Resource先byName.

总结:

This article is just a brief introductionSpring重要的知识点,Many places are not dig deep,对Spring感兴趣的同学可以参考Spring官网阅读 | 总结篇_程序员DMZ的博客-CSDN博客,阅读Spring官方文档.

copyright:author[I am not Euler_],Please bring the original link to reprint, thank you. https://en.javamana.com/2022/266/202209230906023195.html