IoC(Inversion of Control,IoC)是Spring的核心,能夠說Spring是一種基於IoC容器編程的框架。因爲Spring Boot 是基於註解開發Spring IoC,因此本文使用全註解的方式對IoC進行講述。java
一個系統的開發離不開許許多多的類,經過整合業務邏輯,咱們組織這麼多類進行業務的展開,不一樣類之間存在或多或少的依賴關係。之前咱們實例化一個類是經過 new 關鍵字,如今咱們把這個工做交給IoC,由它進行統一的管理。web
IoC(Inversion of Control),控制反轉,用白話來說,就是由容器控制程序之間的關係,而非傳統實現中,由程序代碼直接控制,這也就是所謂「控制反轉」的概念所在:控制權由應用代碼中轉到了外部容器,控制權的轉移,就是所謂反轉。
spring
傳統方式:數據庫
決定使用哪個具體實現是由應用程序負責的,在編譯階段就肯定了。express
Spring方式:apache
調用類只依賴接口,而不依賴具體的實現類,減小了耦合。控制權交給了容器,在運行期才由容器決定將具體的實現動態的「注入」到調用類的對象中。這也是使用IoC的根本緣由。編程
IoC容器是一個管理Bean的容器,在Spring的定義中,它要求全部的IoC容器都要實現BeanFactory接口,它是頂級容器接口,下面是BeanFactory的源碼。tomcat
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
boolean containsBean(String name);
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
String[] getAliases(String name);
}複製代碼
源碼中有不少getBean方法,它們是Ioc容器重要的方法之一,咱們能夠按類型或者名稱獲取Bean,理解這個內容對後面依賴注入十分重要。bash
isSingleton判斷Bean是否爲單例,在IoC中,默認的Bean都是單例存在,也就是getBean返回的都是同一個對象。app
isPrototype方法若是返回true,那麼IoC容器老是返回一個新的Bean。
它是一個基於註解的IoC容器,介紹它的緣由是,咱們能夠直觀演示Spring Boot裝配和獲取Bean。咱們下面演示手動裝配Bean到該容器,而後從容器獲取Bean。
package com.example.acutator.entity;
public class User {
private int id;
private String username;
private String phone;
private String email;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' + ", phone='" + phone + '\'' + ", email='" + email + '\'' +
'}';
}
}
複製代碼
package com.example.acutator.config;
import com.example.acutator.entity.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean(name = "user")
public User getUser() {
User user = new User();
user.setId(1);
user.setUsername("張三瘋");
user.setPhone("1129882512");
user.setEmail("1129882512@qq.com");
return user;
}
}
複製代碼
@SpringBootApplication
public class AcutatorApplication {
public static void main(String[] args) {
SpringApplication.run(AcutatorApplication.class, args);
ApplicationContext applicationContext=new AnnotationConfigApplicationContext(AppConfig.class);
User user=applicationContext.getBean(User.class);
System.out.println("user info>>"+user);
}
}複製代碼
2019-09-29 17:07:25.250 INFO 9148 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.24]
2019-09-29 17:07:25.359 INFO 9148 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2019-09-29 17:07:25.359 INFO 9148 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1094 ms
2019-09-29 17:07:25.747 INFO 9148 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2019-09-29 17:07:25.939 INFO 9148 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 14 endpoint(s) beneath base path '/actuator'
2019-09-29 17:07:26.004 INFO 9148 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2019-09-29 17:07:26.006 INFO 9148 --- [ main] c.example.acutator.AcutatorApplication : Started AcutatorApplication in 2.076 seconds (JVM running for 2.85)
user info>>User{id=1, username='張三瘋', phone='1129882512', email='1129882512@qq.com'}
2019-09-29 17:07:27.803 INFO 9148 --- [on(2)-127.0.0.1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2019-09-29 17:07:27.803 INFO 9148 --- [on(2)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2019-09-29 17:07:27.809 INFO 9148 --- [on(2)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Completed initialization in 6 ms
複製代碼
能夠看到咱們裝配的User信息已經打印出來了。
@Configuration註解的類表示這是一個java配置文件,Spring 容器會根據它來生成IoC容器去裝配Bean。
@Bean 表明方法返回的Bean會裝配到IoC容器中,若是不給定name值,則默認裝配到容器的Bean名稱是方法名(本例中是getUser)
AnnotationConfigApplicationContext構造方法傳入@Configuration註解的類,至關於載入它裏面的配置信息,根據配置信息將Bean裝配到IoC容器中。固然也能夠根據名稱或者類型取出Bean。
前面使用AnnotationConfigApplicationContext體驗了一把手動裝配,可是開發中那麼多的類,若是按照這種方式去裝配,那將是多麼麻煩。因此Spring 提供了註解方式,經過掃描裝配Bean。
咱們將使用@Component和@ComponentScan這兩個註解完成掃描裝配Bean。
@Component:表示這個類被標記能夠被掃描進入IoC容器。
@ComponentScan:表示用掃描樣的策略去掃描咱們裝配的Bean。
這裏在類名上面加上@Component註解,表示能夠被掃描到
package com.example.acutator.entity;
import org.springframework.stereotype.Component;
@Component
public class Student {
private String num;
private String name;
private int score;
public String getNum() {
return num;
}
public void setNum(String num) {
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"num='" + num + '\'' + ", name='" + name + '\'' + ", score=" + score + '}'; } } 複製代碼
加上掃描策略:@ComponentScan("com.example.acutator.entity"),表明掃描該包下面全部的類,凡是類名有@Component註解的都將掃描裝配到IoC容器。
@Configuration
@ComponentScan("com.example.acutator.entity")
public class AppConfig {
@Bean(name = "user")
public User getUser() {
User user = new User();
user.setId(1);
user.setUsername("張三瘋");
user.setPhone("1129882512");
user.setEmail("1129882512@qq.com");
return user;
}
}
複製代碼
加入Student對象打印信息
@SpringBootApplication
public class AcutatorApplication {
public static void main(String[] args) {
SpringApplication.run(AcutatorApplication.class, args);
ApplicationContext applicationContext=new AnnotationConfigApplicationContext(AppConfig.class);
// User user=applicationContext.getBean(User.class);
// System.out.println("user info>>"+user);
Student student=applicationContext.getBean(Student.class);
System.out.println("student info>>"+student);
}
}複製代碼
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.1.8.RELEASE) 2019-09-29 17:32:10.600 INFO 14844 --- [ main] c.example.acutator.AcutatorApplication : Starting AcutatorApplication on XXW4VSPSAQE4OR2 with PID 14844 (D:\ideaProject2\acutator\target\classes started by Administrator in D:\ideaProject2\acutator) 2019-09-29 17:32:10.602 INFO 14844 --- [ main] c.example.acutator.AcutatorApplication : No active profile set, falling back to default profiles: default 2019-09-29 17:32:11.539 INFO 14844 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2019-09-29 17:32:11.559 INFO 14844 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2019-09-29 17:32:11.560 INFO 14844 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.24] 2019-09-29 17:32:11.680 INFO 14844 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2019-09-29 17:32:11.680 INFO 14844 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1047 ms 2019-09-29 17:32:12.082 INFO 14844 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2019-09-29 17:32:12.267 INFO 14844 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 14 endpoint(s) beneath base path '/actuator' 2019-09-29 17:32:12.328 INFO 14844 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2019-09-29 17:32:12.330 INFO 14844 --- [ main] c.example.acutator.AcutatorApplication : Started AcutatorApplication in 2.018 seconds (JVM running for 2.761) student info>>Student{num='null', name='null', score=0} 2019-09-29 17:32:14.207 INFO 14844 --- [on(2)-127.0.0.1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' 2019-09-29 17:32:14.207 INFO 14844 --- [on(2)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' 2019-09-29 17:32:14.213 INFO 14844 --- [on(2)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Completed initialization in 6 ms 複製代碼
能夠看到Student對象信息,咱們在裝配的時候沒有賦值,因此打印的都是默認值。
(Dependency Injection)和控制反轉(Inversion of Control)是同一個概念。具體含義是:當某個角色(多是一個Java實例,調用者)須要另外一個角色(另外一個Java實例,被調用者)的協助時,在 傳統的程序設計過程當中,一般由調用者來建立被調用者的實例。但在Spring裏,建立被調用者的工做再也不由調用者來完成,所以稱爲控制反轉;建立被調用者 實例的工做一般由Spring容器來完成,而後注入調用者,所以也稱爲依賴注入。
舉例:人有時候會依賴於動物替咱們作一些事情,好比貓抓老鼠,狗看家等。貓和狗都屬於動物。下面以代碼的角度去描述人依賴動物作事的邏輯。
它是動物的接口,封裝一個方法,咱們形象的給它一個方法 work
public interface Animal {
public void work();
}複製代碼
它是人的頂級接口
public interface Person {
public void service();
public void setAnimal(Animal animal);
}複製代碼
package com.example.acutator.entity;
import org.springframework.stereotype.Component;
@Component
public class Dog implements Animal{
@Override
public void work() {
System.out.println("***狗正在看門***");
}
}複製代碼
package com.example.acutator.entity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class BusinessPerson implements Person{
@Autowired
private Animal animal=null;
@Override
public void service() {
this.animal.work();
}
@Override
public void setAnimal(Animal animal) {
this.animal=animal;
}
}
複製代碼
@SpringBootApplication
public class AcutatorApplication {
public static void main(String[] args) {
SpringApplication.run(AcutatorApplication.class, args);
ApplicationContext applicationContext=new AnnotationConfigApplicationContext(AppConfig.class);
// User user=applicationContext.getBean(User.class);
// System.out.println("user info>>"+user);
// Student student=applicationContext.getBean(Student.class);
// System.out.println("student info>>"+student);
Person person=applicationContext.getBean(BusinessPerson.class);
person.service();
}
}複製代碼
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.1.8.RELEASE) 2019-09-30 08:49:24.093 INFO 11792 --- [ main] c.example.acutator.AcutatorApplication : Starting AcutatorApplication on XXW4VSPSAQE4OR2 with PID 11792 (D:\ideaProject2\acutator\target\classes started by Administrator in D:\ideaProject2\acutator) 2019-09-30 08:49:24.101 INFO 11792 --- [ main] c.example.acutator.AcutatorApplication : No active profile set, falling back to default profiles: default 2019-09-30 08:49:26.992 INFO 11792 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2019-09-30 08:49:27.046 INFO 11792 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2019-09-30 08:49:27.046 INFO 11792 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.24] 2019-09-30 08:49:27.191 INFO 11792 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2019-09-30 08:49:27.191 INFO 11792 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 2972 ms 2019-09-30 08:49:27.948 INFO 11792 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2019-09-30 08:49:28.236 INFO 11792 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 14 endpoint(s) beneath base path '/actuator' 2019-09-30 08:49:28.314 INFO 11792 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2019-09-30 08:49:28.331 INFO 11792 --- [ main] c.example.acutator.AcutatorApplication : Started AcutatorApplication in 5.185 seconds (JVM running for 6.756) ***狗正在看門*** 2019-09-30 08:49:30.709 INFO 11792 --- [on(9)-127.0.0.1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' 2019-09-30 08:49:30.710 INFO 11792 --- [on(9)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' 2019-09-30 08:49:30.851 INFO 11792 --- [on(9)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Completed initialization in 141 ms 複製代碼
@Autowired 它會根據屬性的類型找到對應的Bean進行注入。這裏Dog類是動物的一種,因此IoC容器會把Dog的實例注入到BusinessPerson中,這樣,Dog就能夠爲咱們工做了。測試結果中能夠看到打印的信息,表明咱們注入依賴成功。
這裏有個問題,以前咱們說過,IoC容器的頂級接口是BeanFactory,它獲取Bean的方式不少,其中一個是根據類型獲取Bean,而後本例中Animal的實現類只有Dog,可是動物並不止一種,還有貓狼象鼠等,那麼根據類型獲取Bean是否是有問題呢,IoC怎麼知道咱們要使用哪一種類型注入到調用者實例中呢?請下面內容。
package com.example.acutator.entity;
import org.springframework.stereotype.Component;
@Component
public class Cat implements Animal {
@Override
public void work() {
System.out.println("***貓正在抓老鼠***");
}
}複製代碼
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.1.8.RELEASE) 2019-09-30 09:10:51.358 INFO 10716 --- [ main] c.example.acutator.AcutatorApplication : Starting AcutatorApplication on XXW4VSPSAQE4OR2 with PID 10716 (D:\ideaProject2\acutator\target\classes started by Administrator in D:\ideaProject2\acutator) 2019-09-30 09:10:51.361 INFO 10716 --- [ main] c.example.acutator.AcutatorApplication : No active profile set, falling back to default profiles: default 2019-09-30 09:10:52.313 INFO 10716 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2019-09-30 09:10:52.329 INFO 10716 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2019-09-30 09:10:52.329 INFO 10716 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.24] 2019-09-30 09:10:52.432 INFO 10716 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2019-09-30 09:10:52.432 INFO 10716 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1042 ms 2019-09-30 09:10:52.684 WARN 10716 --- [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'businessPerson': Unsatisfied dependency expressed through field 'animal'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.example.acutator.entity.Animal' available: expected single matching bean but found 2: cat,dog 2019-09-30 09:10:52.687 INFO 10716 --- [ main] o.apache.catalina.core.StandardService : Stopping service [Tomcat] 2019-09-30 09:10:52.707 INFO 10716 --- [ main] ConditionEvaluationReportLoggingListener : Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled. 2019-09-30 09:10:52.783 ERROR 10716 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPLICATION FAILED TO START *************************** Description: Field animal in com.example.acutator.entity.BusinessPerson required a single bean, but 2 were found: - cat: defined in file [D:\ideaProject2\acutator\target\classes\com\example\acutator\entity\Cat.class] - dog: defined in file [D:\ideaProject2\acutator\target\classes\com\example\acutator\entity\Dog.class] Action: Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed Process finished with exit code 複製代碼
當咱們建立了Cat實例以後,程序拋出異常,這是由於咱們在注入的時候,IoC容器不知道你要注入什麼動物,它本身混亂了,因此在這裏報錯。繼續看下面內容,咱們解決這種問題。
Spring IoC容器在注入依賴中,因爲某個實例的多種類型而產生的注入引用的混亂或者困擾,咱們把這個問題成爲歧義性。
把注入的Animal屬性名稱修改成dog或者cat
package com.example.acutator.entity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class BusinessPerson implements Person{
@Autowired
private Animal dog=null;
@Override
public void service() {
this.dog.work();
}
@Override
public void setAnimal(Animal animal) {
this.dog=animal;
}
}複製代碼
結果
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.1.8.RELEASE) 2019-09-30 09:15:05.128 INFO 11684 --- [ main] c.example.acutator.AcutatorApplication : Starting AcutatorApplication on XXW4VSPSAQE4OR2 with PID 11684 (D:\ideaProject2\acutator\target\classes started by Administrator in D:\ideaProject2\acutator) 2019-09-30 09:15:05.130 INFO 11684 --- [ main] c.example.acutator.AcutatorApplication : No active profile set, falling back to default profiles: default 2019-09-30 09:15:06.081 INFO 11684 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2019-09-30 09:15:06.100 INFO 11684 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2019-09-30 09:15:06.100 INFO 11684 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.24] 2019-09-30 09:15:06.211 INFO 11684 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2019-09-30 09:15:06.211 INFO 11684 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1047 ms 2019-09-30 09:15:06.566 INFO 11684 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2019-09-30 09:15:06.756 INFO 11684 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 14 endpoint(s) beneath base path '/actuator' 2019-09-30 09:15:06.819 INFO 11684 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2019-09-30 09:15:06.821 INFO 11684 --- [ main] c.example.acutator.AcutatorApplication : Started AcutatorApplication in 2.011 seconds (JVM running for 2.758) ***狗正在看門*** 2019-09-30 09:15:08.689 INFO 11684 --- [on(8)-127.0.0.1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' 2019-09-30 09:15:08.689 INFO 11684 --- [on(8)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' 2019-09-30 09:15:08.694 INFO 11684 --- [on(8)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Completed initialization in 5 ms 複製代碼
這個時候系統就正常了,爲何?
緣由:
@Autowired提供了這樣的規則,首先它會根據類型找到對應的Bean,若是對應的類型不是惟一的,那麼它會根據屬性名稱和Bean的名稱進行匹配,若是匹配上,就會使用該Bean,若是仍是沒法匹配就拋出異常。
@Autowired註解還有個須要注意的點,就是它默認必需要找到對應Bean,若是不能肯定其標註屬性必定會存在而且容許這個標註的屬性爲null,那麼能夠配置@Autowired屬性required爲false
雖然方式一的作法能夠作到消除歧義,可是直接把animal修改成dog,好好的一個動物,硬是被咱們改爲了狗,感受這種作法太不符合正常的邏輯。
這裏咱們使用@Primary註解,修改Cat.java,在類名上面加上該註解。它告訴IoC容器,若是在注入的過程當中,發現同實例多種類型,請優先注入帶有@Primary的這個。(把dog屬性名稱改回原來的animal)
package com.example.acutator.entity;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
@Component
@Primary
public class Cat implements Animal {
@Override
public void work() {
System.out.println("***貓正在抓老鼠***");
}
}複製代碼
運行結果
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.1.8.RELEASE) 2019-09-30 09:31:52.588 INFO 14992 --- [ main] c.example.acutator.AcutatorApplication : Starting AcutatorApplication on XXW4VSPSAQE4OR2 with PID 14992 (D:\ideaProject2\acutator\target\classes started by Administrator in D:\ideaProject2\acutator) 2019-09-30 09:31:52.591 INFO 14992 --- [ main] c.example.acutator.AcutatorApplication : No active profile set, falling back to default profiles: default 2019-09-30 09:31:53.488 INFO 14992 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2019-09-30 09:31:53.506 INFO 14992 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2019-09-30 09:31:53.506 INFO 14992 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.24] 2019-09-30 09:31:53.619 INFO 14992 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2019-09-30 09:31:53.619 INFO 14992 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1000 ms 2019-09-30 09:31:54.019 INFO 14992 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2019-09-30 09:31:54.232 INFO 14992 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 14 endpoint(s) beneath base path '/actuator' 2019-09-30 09:31:54.292 INFO 14992 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2019-09-30 09:31:54.294 INFO 14992 --- [ main] c.example.acutator.AcutatorApplication : Started AcutatorApplication in 2.011 seconds (JVM running for 2.745) ***貓正在抓老鼠*** 2019-09-30 09:31:56.073 INFO 14992 --- [on(8)-127.0.0.1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' 2019-09-30 09:31:56.074 INFO 14992 --- [on(8)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' 2019-09-30 09:31:56.079 INFO 14992 --- [on(8)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Completed initialization in 5 ms複製代碼
若是咱們在Dog.java上面也加上這個註解,則在編譯器就經過不了,因此這個方法有種治標不治標本的感受。因此這種方法能解決,可是仍是很差。
@Qualifier(),它須要配置一個value去定義,它將與@Autowired組合在一塊兒,經過類型和名稱一塊兒找到Bean,由於Bean名稱在Spring IoC容器中是惟一的,經過這種方式就能夠消除歧義性。
修改BusinessPerson.java,(將Cat.java中@Primary註解去掉)
package com.example.acutator.entity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
public class BusinessPerson implements Person{
@Autowired
@Qualifier("cat")
private Animal animal=null;
@Override
public void service() {
this.animal.work();
}
@Override
public void setAnimal(Animal animal) {
this.animal=animal;
}
}複製代碼
運行結果
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.1.8.RELEASE) 2019-09-30 09:41:57.385 INFO 8276 --- [ main] c.example.acutator.AcutatorApplication : Starting AcutatorApplication on XXW4VSPSAQE4OR2 with PID 8276 (D:\ideaProject2\acutator\target\classes started by Administrator in D:\ideaProject2\acutator) 2019-09-30 09:41:57.388 INFO 8276 --- [ main] c.example.acutator.AcutatorApplication : No active profile set, falling back to default profiles: default 2019-09-30 09:41:58.442 INFO 8276 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2019-09-30 09:41:58.463 INFO 8276 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2019-09-30 09:41:58.463 INFO 8276 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.24] 2019-09-30 09:41:58.581 INFO 8276 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2019-09-30 09:41:58.582 INFO 8276 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1163 ms 2019-09-30 09:41:58.940 INFO 8276 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2019-09-30 09:41:59.119 INFO 8276 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 14 endpoint(s) beneath base path '/actuator' 2019-09-30 09:41:59.192 INFO 8276 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2019-09-30 09:41:59.195 INFO 8276 --- [ main] c.example.acutator.AcutatorApplication : Started AcutatorApplication in 2.072 seconds (JVM running for 2.762) ***貓正在抓老鼠*** 2019-09-30 09:42:00.875 INFO 8276 --- [n(10)-127.0.0.1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' 2019-09-30 09:42:00.875 INFO 8276 --- [n(10)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' 2019-09-30 09:42:00.880 INFO 8276 --- [n(10)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Completed initialization in 5 ms複製代碼
徹底沒問題,說明咱們的歧義問題解決。
前面咱們已經作到了如何裝配Bean到容器中,如何注入到引用實例中使用,可是咱們不清楚IoC容器是如何裝配和銷燬Bean的過程,有時候咱們須要在實例的初始化過程當中取作一些特殊的工做,好比初始化數據庫鏈接,關閉鏈接資源等等。因此咱們須要去了解下Bean的生命週期,瞭解了生命週期能夠幫助咱們進一步加深對Spring IoC和DI的理解。
在完成上面三步以後,默認狀況下,spring會繼續去完成Bean的實例化和依賴注入,這樣從IoC容器中就能夠獲得一個依賴注入完成的Bean。
當咱們調用BeanFactory的getBean方法的時候,這時候IoC容器纔開始實例化Bean。Ioc根據Bean的定義信息,經過實現不一樣的功能接口,對Bean進行實例化,流程以下。
Spring對bean進行實例化,默認bean是單例;
Spring對bean進行依賴注入;
若是bean實現了BeanNameAware接口,spring將bean的id傳給setBeanName()方法;
若是bean實現了BeanFactoryAware接口,spring將調用setBeanFactory方法,將BeanFactory實例傳進來;
若是bean實現了ApplicationContextAware接口,它的setApplicationContext()方法將被調用,將應用上下文的引用傳入到bean中;
若是bean實現了BeanPostProcessor接口,它的postProcessBeforeInitialization方法將被調用;
若是bean實現了InitializingBean接口,spring將調用它的afterPropertiesSet接口方法,相似的若是bean使用了init-method屬性聲明瞭初始化方法,該方法也會被調用;
若是bean實現了BeanPostProcessor接口,它的postProcessAfterInitialization接口方法將被調用;
此時bean已經準備就緒,能夠被應用程序使用了,他們將一直駐留在應用上下文中,直到該應用上下文被銷燬;
若bean實現了DisposableBean接口,spring將調用它的distroy()接口方法。一樣的,若是bean使用了destroy-method屬性聲明瞭銷燬方法,則該方法被調用;
修改BusinessPerson.java
package com.example.acutator.entity;
import com.sun.org.apache.xml.internal.security.Init;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Component
public class BusinessPerson implements Person,BeanNameAware,BeanFactoryAware,ApplicationContextAware,InitializingBean,DisposableBean {
@Autowired
@Qualifier("cat")
private Animal animal=null;
@Override
public void service() {
this.animal.work();
}
@Override
public void setAnimal(Animal animal) {
this.animal=animal;
}
@PostConstruct
public void init(){
System.out.println("Bean的初始化》》》init");
}
@PreDestroy
public void destroy1(){
System.out.println("Bean的初始化》》》destroy1");
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("Bean的初始化》》》setBeanFactory");
}
@Override
public void setBeanName(String name) {
System.out.println("Bean的初始化》》》setBeanName");
}
@Override
public void destroy() throws Exception {
System.out.println("Bean的初始化》》》destroy");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Bean的初始化》》》afterPropertiesSet");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("Bean的初始化》》》setApplicationContext");
}
}複製代碼
修改啓動類
@SpringBootApplication
public class AcutatorApplication {
public static void main(String[] args) {
SpringApplication.run(AcutatorApplication.class, args);
ApplicationContext applicationContext=new AnnotationConfigApplicationContext(AppConfig.class);
// User user=applicationContext.getBean(User.class);
// System.out.println("user info>>"+user);
// Student student=applicationContext.getBean(Student.class);
// System.out.println("student info>>"+student);
Person person=applicationContext.getBean(BusinessPerson.class);
person.service();
((AnnotationConfigApplicationContext) applicationContext).close();
}
}複製代碼
運行結果
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.1.8.RELEASE) 2019-09-30 10:50:09.272 INFO 5256 --- [ main] c.example.acutator.AcutatorApplication : Starting AcutatorApplication on XXW4VSPSAQE4OR2 with PID 5256 (D:\ideaProject2\acutator\target\classes started by Administrator in D:\ideaProject2\acutator) 2019-09-30 10:50:09.275 INFO 5256 --- [ main] c.example.acutator.AcutatorApplication : No active profile set, falling back to default profiles: default 2019-09-30 10:50:10.167 INFO 5256 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2019-09-30 10:50:10.184 INFO 5256 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2019-09-30 10:50:10.185 INFO 5256 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.24] 2019-09-30 10:50:10.291 INFO 5256 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2019-09-30 10:50:10.291 INFO 5256 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 986 ms Bean的初始化》》》setBeanName Bean的初始化》》》setBeanFactory Bean的初始化》》》setApplicationContext Bean的初始化》》》init Bean的初始化》》》afterPropertiesSet 2019-09-30 10:50:10.731 INFO 5256 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2019-09-30 10:50:10.919 INFO 5256 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 14 endpoint(s) beneath base path '/actuator' 2019-09-30 10:50:10.974 INFO 5256 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2019-09-30 10:50:10.976 INFO 5256 --- [ main] c.example.acutator.AcutatorApplication : Started AcutatorApplication in 1.994 seconds (JVM running for 2.725) Bean的初始化》》》setBeanName Bean的初始化》》》setBeanFactory Bean的初始化》》》setApplicationContext Bean的初始化》》》init Bean的初始化》》》afterPropertiesSet ***貓正在抓老鼠*** Bean的初始化》》》destroy1 Bean的初始化》》》destroy 2019-09-30 10:50:12.794 INFO 5256 --- [on(8)-127.0.0.1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' 2019-09-30 10:50:12.794 INFO 5256 --- [on(8)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' 2019-09-30 10:50:12.800 INFO 5256 --- [on(8)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Completed initialization in 6 ms複製代碼
爲何會出現屢次初始化呢,上面的初始化爲何沒有執行銷燬方法呢?
分析:
咱們以前使用AppConfig.java這個類進行了一個配置,並且使用註解掃描了entity包下面的全部類。若是類名帶註解@Component,那麼此時會有兩個IoC容器對該類進行管理。一個是默認的IoC容器,另一個是AnnotationConfigApplicationContext經過構造建立的容器,也就是ApplicationContext,咱們知道全部的容器頂級接口是BeanFactory,ApplicationContext是在BeanFactory上面擴展的,它具備更強大的功能,若是咱們把BeanFactory比喻爲人體心臟,那麼ApplicationContext就是軀體。
全部能夠認爲同時有兩個容器對Bean進行了裝配和注入,故有兩個初始化的信息。
以上只是生命週期簡單的描述,具體的能夠對照官方文檔描述,而後對比本文慢慢去理解,相信收穫會更多。
有誤請指正,不勝感激!