做爲一名java開發人員,咱們接觸最先最多的框架確定就是spring了,一次次不停的使用spring框架提供的功能幫助咱們快速開發,然而其中的核心功能IOC(控制反轉)和AOP能詳細闡明的卻很少,咱們知其然,殊不知其因此然,那麼下面咱們就一塊兒探索一下spring中的IOC吧java
例:在現實生活中個人頭髮長了,影響了個人帥氣,那麼我就須要理髮,我不可能本身給本身理髮,首先我沒有理髮的工具,其次我可能把本身的頭髮剪得像狗啃的同樣,那麼影響我帥氣的外表,就沒有妹子願意跟我一塊兒約會了,這確定是我不肯意發生的。其次,我也不是王思聰,我也不願能擁有一名只爲本身服務的人員,那麼正確的作法就是去村口的理髮店找Tony老師來給我打造帥氣的髮型。咱們來分解一下剪髮的過程,我是需求提出者,頭髮變帥是要的結果,首先若是我本身爲本身理髮,不只可能達不到預期的效果,並且還費時費力,那麼我就須要一名專業的理髮師來爲我服務,達到最終的目的,這個過程咱們可能稱之爲解耦合的過程。
在Java開發中,一個功能的實現每每是由多個類和方法來共同協做完成的,在沒有IOC以前咱們建立一個類的依賴類的方式是new object(),控制權在本類手中,這些依賴關係將會使系統的複雜度提升,不利於維護和開發,這是咱們不肯意看到的,在有了IOC以後,一個類所須要的依賴類由IOC來管理,那麼咱們只要關心自己類的功能和方法便可,將控制權交給了IOC容器,這就是我理解的控制反轉。web
其中BeanFactory是IOC容器的基本實現,ApplicationContext是BeanFactory的子接口,提供更高級的特性。 而下面幾個類則是具體的實現類,能夠加載配置文件中定義的bean,管理全部加載的bean,有請求的時候分配bean。spring
建立應用對象之間的關聯關係的傳統方法(經過構造器或者查找)一般會致使結構複雜的代碼,這些代碼難以被複用也很難進行單元測試,若是狀況不嚴重的話,這些對象所作的事情只是超出了應該作的範圍,而最壞的狀況是,這些對象的彼此之間高度耦合,難以複用和測試。
在spring中,對象無需本身查找或建立與其所關聯的其餘對象。相反,容器負責把須要互相協做的對象引用賦予各個對象。例如,一個訂單管理組件須要信用卡認證組件,但它不須要本身建立信用卡認證組件。訂單管理組件只須要代表本身兩手空空,容器就會主動賦予它一個信用卡認證組件。建立應用對象之間協做關係的行爲一般稱爲裝配(wiring),這也是依賴注入(DI)的本質。(摘錄spring實戰第四版第二章)編程
1:在Java中進行顯式配置 (我的喜歡)
咱們定義接口以及其實現類springboot
package com.lly.springtest1.ioc;
/**
* @Author lly
* @Description 水果接口類
* @Date 2019/1/29 10:23 AM
* @Param
* @return
**/
public interface IFruitService {
/**
* @Author lly
* @Description 顯示水果信息
* @Date 2019/1/29 10:25 AM
* @Param []
* @return
**/
void showFruitInfo();
}
複製代碼
package com.lly.springtest1.ioc;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @ClassName OrangeServiceImpl
* @Description 水果實現類-橘子
* @Author lly
* @Date 2019/1/29 10:17 AM
* @Version 1.0
**/
@Component
@Data
@Slf4j
public class OrangeServiceImpl implements IFruitService {
@Override
public void showFruitInfo() {
log.info("橘子的重量是:{}kg",10);
}
}
複製代碼
掃描組件配置類,@ComponentScan註解會默認掃描與其配置類相同的包以及這個包下面的全部的子包,查找帶有@Component註解的類,固然咱們能夠直接定@ComponentScan掃描的包bash
#單個包
@ComponentScan("包名")
@ComponentScan(basePackage="包名")
#多個包
@ComponentScan(basePackage={"包名","包名"})
複製代碼
package com.lly.springtest1.ioc;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* @ClassName TestIoc
* @Description 掃描組件配置類
* @Author lly
* @Date 2019/1/29 10:30 AM
* @Version 1.0
**/
@ComponentScan
@Configuration
public class TestIoc {
}
複製代碼
單元測試session
package com.lly.springtest1.ioc;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestIoc.class)
public class TestIocTest {
@Autowired
private IFruitService iFruitService;
@Test
public void showInfo(){
iFruitService.showFruitInfo();
}
}
複製代碼
結果框架
2:隱式的bean發現機制和自動裝配 (我的喜歡)
其實這種方式咱們在 探索Spring系列(一)Spring容器和Bean的生命週期 這裏章節已經見到過了,下面貼出核心代碼ide
package com.lly.springtest1.entity;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class BeanLifeCycle {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanLifeCycle.class);
context.close();
}
@Bean
public MyBeanPostProcessor getBean() {
return new MyBeanPostProcessor();
}
@Bean(initMethod = "myInit",destroyMethod = "myDestroy")
public GirlFriendEntity getGirl() {
GirlFriendEntity girl = new GirlFriendEntity();
girl.setName("穎寶");
return girl;
}
}
複製代碼
這種配置的方式也能夠將bean歸入spring容器的管理中,下面咱們來測試一下工具
package com.lly.springtest1.entity;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = BeanLifeCycle.class)
public class BeanLifeCycleTest {
@Autowired
private MyBeanPostProcessor myBeanPostProcessor;
@Test
public void show(){
Assert.assertNotNull(myBeanPostProcessor);
}
}
複製代碼
3:xml中顯示配置(我的不喜歡這種方式,配置很繁瑣,有興趣的同窗自行了解學習)
4:總結,上述幾種裝配bean的方式,均可以實現一樣的功能,也能夠混合使用,使用哪一種方式徹底能夠按照開發者我的的習慣和喜愛來決定,可是做者目前使用的都是前兩種,消除配置式編程,更快樂,在目前比較流行的springboot開發中也是推薦前兩種
1:消除歧義性 在上文中咱們定義個一個IFruitService,orangeEntity 這個類實現了這個接口,咱們在測試類中是直接注入的,那麼我能夠想一想一下,在實際的開發中,可能存在一個接口對應多個實現類的狀況,spring在幫我自動注入的時候是怎麼幫咱們選擇的呢,下面咱們來實驗一下。
咱們再建立一個水果的實現類 香蕉
package com.lly.springtest1.ioc;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @ClassName BananaServiceImpl
* @Description 水果實現類-橘子
* @Author lly
* @Date 2019/1/29 10:17 AM
* @Version 1.0
**/
@Component
@Data
@Slf4j
public class BananaServiceImpl implements IFruitService {
@Override
public void showFruitInfo() {
log.info("香蕉的重量是:{}kg",100);
}
}
複製代碼
而後咱們再次啓動測試類發現
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.lly.springtest1.ioc.IFruitService' available: expected single matching bean but found 2: bananaServiceImpl,orangeServiceImpl
複製代碼
經過查看咱們發現IFruitService這個接口有2個實現類,咱們沒有指定要使用哪一個,那麼spring是不會知道咱們將要使用哪一個的。
解決方法:
spring提供2中方式來解決這個問題
第一種:使用@Qualifier註解來指定咱們要使用的具體實現類
咱們能夠看到在咱們指定了具體實現類後測試用例順利經過。
第二種:使用@Primary註解來指定哪一個實現類做爲首選實現類,咱們在香蕉類上來加上這個註解
package com.lly.springtest1.ioc;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
/**
* @ClassName BananaServiceImpl
* @Description 水果實現類-橘子
* @Author lly
* @Date 2019/1/29 10:17 AM
* @Version 1.0
**/
@Component
@Data
@Slf4j
@Primary
public class BananaServiceImpl implements IFruitService {
@Override
public void showFruitInfo() {
log.info("香蕉的重量是:{}kg",100);
}
}
複製代碼
測試類改造以下
package com.lly.springtest1.ioc;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestIoc.class)
public class TestIocTest {
@Autowired
private IFruitService iFruitService;
@Test
public void showInfo(){
iFruitService.showFruitInfo();
}
}
複製代碼
啓動查看結果
1)單例(singleton)在整個應用中,只建立一個實例;(默認狀況下,spring應用上下文中全部的bean都是單例模式)。 2)原型(prototype)每次注入或者經過spring應該上下文獲取的都是一個新的實例。 3)會話(session)在web應用中,爲每一個會話建立一個bean實例。 4)請求(request)在web應用中。爲每一個請求建立一個bean實例。
註解式指定bean做用域
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
@Scope(value = WebApplicationContext.SCOPE_SESSION,proxyMode = ScopedProxyMode.INTERFACES)
@Scope(value = WebApplicationContext.SCOPE_REQUEST,proxyMode = ScopedProxyMode.INTERFACES)
複製代碼
另外還可使用xml配置式,這裏不作詳解
工欲善其事必先利其器,spring做爲咱們使用最頻繁的框架,熟悉其主要功能和原理是頗有必有的,否則咱們一直摸着石頭過河,下面一章咱們將要介紹spring另一個重要功能AOP(面向切面編程)