Spring的控制反转(IoC),Spring最成功的是其提出的理念,而不是技术本身。
Spring的核心理念:
1.控制反转(Inversion of Control),简称IoC.
2.面向切面编程(Aspect Oriented Programming),简称AOP.
IoC是一种通过描述来生成或者获取对象发的技术,通常我们熟悉的就是通过new关键字来创建对象,但是在Spring中全部交给Spring来管理,通过描述来创建对象。
一个系统可以生成各种对象,并且这些对象都需要进行管理,对象之间并不是孤立的,它们之间还可能存在依赖的关系,例如,一个班级是由多个老师和同学组成的,那么班级本身就依赖于多个老师和同学了,所以Spring就提出了依赖注入的功能。
为了描述上述的班级、同学和老师这三个对象之间的关系,我们需要一个容器,在Spring中把每一个需要管理的对象称为Spring Bean (简称Bean),而Spring管理这些Bean的容器,被我们称为SpringIoC容器(简称IoC容器)。
IoC容器需要具备两个基本的功能:
1.通过描述管理Bean,包括发布和获取Bean。
2.通过描述完成Bean之间的依赖关系。
IoC容器简介
Spring IoC容器是一个管理Bean的容器,在Spring的定义中,所有的IoC容器都需要实现接口BeanFactory,它是一个顶级的容器接口。
以我项目中的以附件的方式发送邮件的项目pom为例:
pom.xml内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bdyh</groupId>
<artifactId>Email</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.5.4</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
BeanFactory接口路径:
BeanFactory 接口源码:
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
package org.springframework.beans.factory;
public interface BeanFactory {
//前缀
java.lang.String FACTORY_BEAN_PREFIX = "&";
//多个getBean方法
java.lang.Object getBean(java.lang.String s) throws org.springframework.beans.BeansException;
<T> T getBean(java.lang.String s, @org.springframework.lang.Nullable java.lang.Class<T> aClass) throws org.springframework.beans.BeansException;
java.lang.Object getBean(java.lang.String s, java.lang.Object... objects) throws org.springframework.beans.BeansException;
<T> T getBean(java.lang.Class<T> aClass) throws org.springframework.beans.BeansException;
<T> T getBean(java.lang.Class<T> aClass, java.lang.Object... objects) throws org.springframework.beans.BeansException;
//是否包含Bean
boolean containsBean(java.lang.String s);
//Bean是否是单例
boolean isSingleton(java.lang.String s) throws org.springframework.beans.factory.NoSuchBeanDefinitionException;
//Bean是否是原型
boolean isPrototype(java.lang.String s) throws org.springframework.beans.factory.NoSuchBeanDefinitionException;
//是否类型匹配
boolean isTypeMatch(java.lang.String s, org.springframework.core.ResolvableType resolvableType) throws org.springframework.beans.factory.NoSuchBeanDefinitionException;
boolean isTypeMatch(java.lang.String s, @org.springframework.lang.Nullable java.lang.Class<?> aClass) throws org.springframework.beans.factory.NoSuchBeanDefinitionException;
//获取Bean的类型
@org.springframework.lang.Nullable
java.lang.Class<?> getType(java.lang.String s) throws org.springframework.beans.factory.NoSuchBeanDefinitionException;
//获取Bean的别名
java.lang.String[] getAliases(java.lang.String s);
}
方法介绍:
多个getBean方法:从IoC容器中获取Bean,有按类型(by type)获取Bean的,有按名称(by name)获取Bean的。
isSingleton方法:判断Bean是否在Spring IoC中为单例,这里需要记住的是在Spring IoC容器中,默认的情况下,Bean都是以单例存在的,也就是使用getBean方法返回的都是同一个对象。与isSingleton方法相反的是isPrototype方法,如果它返回的是true,那么当我们使用getBean方法获取Bean的时候,Spring IoC容器就会创建一个新的Bean返回给调用者。
高级接口ApplicationContext:
由于BeanFactory的功能还不够强大,因此Spring在BeanFactory的基础上,设计了更为强大的接口ApplicationContent。它是BeanFactory的子接口之一,在Spring的体系中BeanFactory和ApplicationContext是最为重要的接口设计,在现实中我们使用的大部分Spring IoC容器是ApplicationContext接口的实现类。
查找路径:
关系图如下:
从Spring IoC容器的接口设计图中可以看到,ApplicationContext接口通过继承上级接口,进而继承BeanFactory接口,但是在BeanFactory的基础上,扩展了消息国际化接口(MessageSource)、环境可配置接口(EnvironmentCapable)、应用事件发布接口(ApplicationEventPublisher)和资源模式解析接口(ResourcePatternResolver),所以它的功能会更为强大。
在Spring Boot当中我们主要是通过注解来装配Bean到Spring IoC容器中,为了贴近Spring Boot 的需要,主要介绍一个基于注解的IoC容器--AnnotationConfigApplicationContext.
1.首先定义一个Java简单对象 User.java。
package com.bdyh.entity;
/**
* @author BD-PC40
* @Title: User
* @ProjectName Email
* @Description: TODO
* @date 2018/12/25 15:06
*/
public class User {
private Long id;
private String userName;
private String note;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}
定义一个Java配置文件AppConfig.java
package com.bdyh.common;
import com.bdyh.entity.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author BD-PC40
* @Title: AppConfig
* @ProjectName Email
* @Description: 配置文件
* @date 2018/12/25 15:28
*/
@Configuration
public class AppConfig {
@Bean(name = "user")
public User initUser() {
User user = new User();
user.setId(1L);
user.setUserName("张三");
user.setNote("note_1");
return user;
}
}
注解说明:
@Configuration:代表这是一个Java配置文件,Spring的容器会根据它来生成IoC容器去装配Bean.
@Bean:代表将initUser方法返回的POJO装配到IoC容器中,而其属性name定义这个Bean的名称,如果没有配置它,则将方法名称"initUser"作为Bean的名称保存到Spring IoC容器中。现在就可以用AnnotationConfigApplicationContext来构建自己的IoC容器。
使用AnnotationConfigApplicationContext:
package com.bdyh.common;
import com.bdyh.entity.User;
import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* @author BD-PC40
* @Title: IoCTest
* @ProjectName Email
* @Description:
* @date 2018/12/25 16:03
*/
public class IoCTest {
private static Logger log = Logger.getLogger(IoCTest.class);
public static void main(String[] args){
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
User user = ctx.getBean(User.class);
log.info(user.getId());
}
}
上述代码中将Java配置文件AppConfig传递给AnnotationConfigApplicationContext的构造方法,这样它就能够读取配置了。然后将配置里面的Bean装配到IoC容器中,于是可以用getBean方法获取对应POJO。
对应的日志打印:
16:25:48.484 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
16:25:48.541 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Unable to locate LifecycleProcessor with name 'lifecycleProcessor': using default [org.springframework.context.support.DefaultLifecycleProcessor@69b2283a]
16:25:48.541 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'lifecycleProcessor'
16:25:48.568 [main] DEBUG org.springframework.core.env.PropertySourcesPropertyResolver - Could not find key 'spring.liveBeansView.mbeanDomain' in any property source
16:25:48.571 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'user'
显然,配置在配置文件中的名称为user的Bean已经被装配到IoC的容器中了,并且可以通过getBean的方法获取对应的Bean,并将Bean的属性信息输出出来,当然这是很简单的方法,而注解@Bean也不是唯一创建Bean的方法,也可以通过其他的方法将Bean装配到IoC容器中