Spring中IoC初識

Spring通過一種稱作控制反轉(IoC)的技術促進了鬆耦合。當應用了IoC,一個對象依賴的其它對象會通過被動的方式傳遞進來,而不是這個對象自己創建或者查找依賴對象。你可以認爲IoC與JNDI相反——不是對象從容器中查找依賴,而是容器在對象初始化時不等對象請求就主動將依賴傳遞給它。

IoC:控制反轉,控制權的轉移,應用程序本身不負責依賴對象的創建和維護,而是由外部容器負責創建和維護。反轉即獲得依賴對象的過程被反轉了,控制被反轉後,獲得依賴對象的過程由自身管理變爲了由IoC容器主動注入。
DI:依賴注入,是IoC的一種實現方式。即是由IoC容器在運行期間,動態地將某種依賴關係注入到對象之中。

目的:創建對象並且組裝對象之間的關係。

Spring注入是指在啓動Spring容器加載bean配置的時候,就完成對變量的賦值行爲
1.設值注入
2.構造注入

設值注入:property,類中要有set方法
若property中的name屬性爲injectionDao,其在執行的時候就是調用setInjectionDao()這個方法

構造注入:constructor-arg,類中要有構造方法,且要傳入對應的參數
若constructor-arg中的name屬性爲injectDao,則類中的構造方法的參數名必須爲injectDao

//設值注入
<bean id="injectionService" class="com.lmr.spring.ioc.injection.service.InjectionServiceImpl">
    <property name="injectionDao" ref="injectionDao"></property>
</bean>

//構造注入
<bean id="injectionService" class="com.lmr.spring.ioc.injection.service.InjectionServiceImpl">
    <constructor-arg name="injectionDao" ref="injectionDao"></constructor-arg>
</bean>
public class InjectionServiceImpl implements InjectionService{

    private InjectionDao injectionDao;

    //設值注入
//  public void setInjectionDao(InjectionDao injectionDao) {
//      this.injectionDao = injectionDao;
//  }

    //構造器注入
    public InjectionServiceImpl(InjectionDao injectionDao) {
        // TODO Auto-generated constructor stub
        this.injectionDao=injectionDao;
    }

    @Override
    public void save(String arg) {
        // TODO Auto-generated method stub

        //模擬業務操作
        System.out.println("Service層接收數據: "+arg);

        injectionDao.save(arg);

    }

}

Bean的作用域 Scope
參數:
singleton 單例,指一個Bean容器中只存在一份(若對一個Bean實例化兩個對象,則前者的內容會被後者覆蓋,即兩個對象的值都相同,引用的是同一個內存塊)
prototype 每次請求(每次使用)創建新的實例,destroy方式不生效
request 每次http請求創建一個實例且僅在當前request內有效
session 同上,每次http請求創建,當前session內有效
global session 基於portlet的web中有效(portlet定義了global session),如果是在web中,同session

測試singleton和prototype作用域

public class BeanScope {

    private int id;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public void print(){
        System.out.println(this.hashCode()+" - - "+this.id);
    }

    public void say(){
        System.out.println("BeanScope say : "+this.hashCode());//通過hashCode()方法來判斷通過容器生成的對象是否爲同一個對象
    }

}
@Test
public void TestSay(){

    BeanScope beanscope1=super.getBean("beanScope");
    beanscope1.say();
    beanscope1.setId(2);
    beanscope1.print();

    BeanScope beanscope2=super.getBean("beanScope");
    beanscope2.say();
    beanscope2.setId(3);
    beanscope2.print();

    beanscope1.say();
    beanscope1.print();

}
<bean id="beanScope" class="com.lmr.spring.bean.BeanScope" scope="singleton"></bean>

結果:兩個對象的hashCode值和id值都一樣,引用的是同一內存塊

BeanScope say : 858242339
858242339 - - 2
BeanScope say : 858242339
858242339 - - 3
BeanScope say : 858242339
858242339 - - 3
<bean id="beanScope" class="com.lmr.spring.bean.BeanScope" scope="prototype"></bean>

結果:兩個對象的hashCode值和id值都不一樣,後者沒有影響到前者

BeanScope say : 1007653873
1007653873 - - 2
BeanScope say : 836514715
836514715 - - 3
BeanScope say : 1007653873
1007653873 - - 2

Bean的生命週期
定義

graph LR
定義-->初始化
初始化-->使用
使用-->銷燬

初始化
1.實現org.springframework.beans.factory.InitializingBean接口,覆蓋afterPropertiesSet方法
2.配置init-method
銷燬
1.實現org.springframework.beans.factory.DisposableBean接口,覆蓋destroy方法
2.配置destroy-method

<bean id="beanLifeCycle" class="com.lmr.spring.bean.BeanLifeCycle" init-method="Start" destroy-method="Stop"></bean>
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

public class BeanLifeCycle implements InitializingBean,DisposableBean{

    //配置全局默認初始化、銷燬方法
    public void defaultInit(){
        System.out.println("Bean Default Init");
    }

    public void defaultDestory(){
        System.out.println("Bean Default Destory");
    }

    //實現InitializingBean、DisposableBean接口
    @Override
    public void afterPropertiesSet() throws Exception {
        // TODO Auto-generated method stub
        System.out.println("Bean Init");
    }

    @Override
    public void destroy() throws Exception {
        // TODO Auto-generated method stub
        System.out.println("Bean Destroy");
    }

    //配置init-method、destroy-method
    public void Start(){
        System.out.println("Bean Start");
    }

    public void Stop(){
        System.out.println("Bean Stop");
    }

}

配置全局默認初始化、銷燬方法(去檢測Bean中是否有對應的方法,若有則執行,沒有也不會報錯)

<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-3.0.xsd"

    default-init-method="defaultInit" default-destroy-method="defaultDestory">

</beans>

若Bean實現了InitializingBean、DisposableBean接口或者配置了init-method、destroy-method,則全局默認初始化、銷燬方法將不起作用,不會執行。若Bean同時實現了InitializingBean、DisposableBean接口又配置了init-method、destroy-method,則按先後順序執行,先執行接口的方法,再執行配置的方法

Aware
使實現了Aware接口的bean在被初始化之後,可以獲取相應資源
ApplicationContextAware
BeanNameAware

public class BeanAware implements BeanNameAware,ApplicationContextAware{

    private String beanname;
    private ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext arg0) throws BeansException {
        // TODO Auto-generated method stub
        this.context=arg0;
        System.out.println("BeanAware setApplicationContext : "+context.getBean(beanname).hashCode());
    }

    @Override
    public void setBeanName(String name) {
        // TODO Auto-generated method stub
        this.beanname=name;
        System.out.println("BeanAware name : "+beanname);
    }

}

Bean的自動裝配(Autowiring)
參數:
No 不做任何操作
byName 根據屬性名自動裝配。此選項將檢查容器並根據名字查找與屬性完全一致的bean,並將其與屬性自動裝配;如果沒有找到,則什麼都不做
byType 如果容器中存在一個與指定屬性類型相同的bean,那麼將於該屬性自動裝配;如果存在多個該類型bean,那麼拋出異常,並指出不能使用byType方式進行自動裝配;如果沒有找到相匹配的bean,則什麼事都不發生
Constructor 與byType方式類似,不同之處在於它應用與構造器參數。如果容器中沒有找到與構造器參數類型一致的bean,那麼拋出異常

<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-3.0.xsd"
    default-autowire="byName">

    <bean id="beanAutowireService" class="com.lmr.spring.bean.BeanAutowireService"></bean>

    <bean id="beanAutowireDao" class="com.lmr.spring.bean.BeanAutowireDao"></bean>

</beans>
public class BeanAutowireService {

    private BeanAutowireDao beanAutowireDao;

    public void setBeanAutowireDao(BeanAutowireDao beanAutowireDao) {
        System.out.println("setBeanAutowireDao");
        this.beanAutowireDao = beanAutowireDao;
    }

//  public BeanAutowireService(BeanAutowireDao beanAutowireDao) {
//      System.out.println("Constructor");
//      this.beanAutowireDao = beanAutowireDao;
//  }

    public void say(String word){

        System.out.println("BeanAutowireService : "+word);
        beanAutowireDao.say(word);

    }

}
@Test
public void testAutowire(){
    BeanAutowireService beanAutowireService=super.getBean("beanAutowireService");
    beanAutowireService.say("hello,Autowire");
}

結果:

setBeanAutowireDao
BeanAutowireService : hello,Autowire
BeanAutowireDao : hello,Autowire

Bean的資源文件的統一接口 Resources
ResourceLoader:資源加載器,所有的ApplicationContext都實現了該接口,可以直接獲取資源
classpath:xx/xx/xx.xx 從classpath中加載
file:/xx/xx/xx.xx
http://xx/xx/xx.xx
(none):/xx/xx/xx.xx 依賴於ApplicationContext

public class BeanResource implements ApplicationContextAware{

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext arg0) throws BeansException {
        // TODO Auto-generated method stub
        this.applicationContext=arg0;
    }

    public void resource() throws IOException{
        Resource resource=applicationContext.getResource("classpath:config.xml");
//      Resource resource=applicationContext.getResource("file:D:\\coding\\codeworksace\\Spring\\src\\main\\resources\\config.xml");
//      Resource resource=applicationContext.getResource("url:http://docs.spring.io/spring-boot/docs/2.0.0.BUILD-SNAPSHOT/reference/htmlsingle/");
//      Resource resource=applicationContext.getResource("config.xml");


        System.out.println(resource.getFilename());
        System.out.println(resource.contentLength());
        FileInputStream input=new FileInputStream(resource.getFile());
        byte[] bytes=new byte[1024];
        while(input.read(bytes)!=-1){
            System.out.println(new String(bytes));
        }
        input.close();
    }

}

結果:

config.xml
306
<?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-3.0.xsd">

</beans>

註解
@Component是一個通用註解,可用於任何bean
@Repository,@Service,@Controller是更有針對性的註解
@Repository通常用於註解DAO類,即持久層
@Service通常用於註解Service類,即服務層
@Controller通常用於註解Controller類,即控制層(MVC)
引入以上註解時,默認的Bean的id爲類名首字母變小寫,BeanAnnotation->beanannotation;也可加入參數,參數的值即爲Bean的id,@Component(“bean”)

@Scope作用域
默認爲singleton
@Scope(“prototype”)

掃描器
過濾器
通過此掃描器,spring會掃描包下面的註解,通過各個註解的規則,來進行bean的自動裝配

<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-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.lmr.spring.beanannotation"></context:component-scan>

</beans>

Bean的自動裝配
@Required註解使用於bean屬性的setter方法
該註解僅僅表示受影響的bean屬性必須在配置時被填充,通過在bean定義或通過自動裝配一個明確的屬性值

@Autowired註解可用於“傳統”的setter方法,也可用於構造器或成員變量
用於成員變量上,就不需要setter方法了
默認情況下,如果因找不到合適的bean將會導致autowiring失敗拋出異常,可以通過設置required=false來避免。每個類中只能有一個構造器被標記爲required=true
@Autowired的自動注入策略是byType,按類型自動裝配

@Qualifier註解
當按類型自動裝配可能多個bean實例的情況,使用@Qualifier可以縮小範圍,或者指定唯一,也可以用於指定單獨的構造器參數或方法參數
其value值爲所選bean的id,也可在xml中配置bean的qualifier值

<bean class="com.lmr.spring.beanannotation.injection.dao.InjectionDaoImplOne">
    <qualifier value="daoOne"></qualifier>
</bean>
@Autowired()
@Qualifier("injectionDaoImplOne")
private InjectionDao injectionDao;

//設值注入
@Autowired()
public void setInjectionDao(@Qualifier("injectionDaoImplOne")InjectionDao injectionDao) {
    this.injectionDao = injectionDao;
}

@Autowired 和 @Qualifier 結合使用時,自動注入的策略就從 byType 轉變成 byName

@Autowired適用於fields,constructors,multi-argument methods這些允許在參數級別使用@Qualifier註解縮小範圍的情況
@Resource使用於成員變量,只有一個參數的setter方法,所以在目標是構造器或一個多參數方法時,最好的方式時使用qualifiers

容器註解
@Bean 標識一個用於配置和初始化一個由SpringIoC容器管理的新對象的方法,類似於XML配置文件中的
屬性: name、init-method、destroy-method
@Configuration 配置
通常與@bean公用的註解是@configuration而不是@component。
在方法頭加上@bean註解,然後方法返回一個bean實例,完成向springIOC容器中註冊一個bean實例。
@Scope 默認是單例的
屬性:value 作用域(singleton,prototype,session,request,global session)
proxyMode 代理方式,代理可以採取延遲解析策略(ScopedProxyMode.NO,ScopedProxyMode.INTERFACES,ScopedProxyMode.TARGET_CLASS)

@Configuration
public class AppConfig { //這裏使用@Configuration註解,標識這個類相當於一個註冊文件
    @Bean
    public MyService myService() { 
        return new MyServiceImpl();
    }
}
<beans>
    <bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>

加載外部資源文件
一種是Xml獲取,一種是Java註解方式獲取

<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-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config></context:annotation-config>
    <context:property-placeholder location="classpath:/jdbc.properties" />

    <bean class="org.springframework.jdbc.datasource.DiverManagerDataSource">
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

</beans>

@ImportResource
參數:value爲聲明properties的xml文件的路徑
使用@Value(“${}”)給類中屬性賦值

@Configuration
@ImportResource(value = { "classpath:/resourceconfig.xml" })
public class StoreConfig {

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    @Value("${jdbc.url}")
    private String url;

    @Bean(name="diverManager")
    public DiverManager getDiverManager(){
        return new DiverManager(username, password, url);
    }

}
//resourceconfig.xml
<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-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config></context:annotation-config>
    <context:property-placeholder location="classpath:/jdbc.properties" />

</beans>

JSR註解

使用JSR-250的註解
@Resource
默認的名稱是從屬性名或者setter方法得出
可通過name屬性來將其解析爲一個bean的名稱,這是由ApplicationContext中的CommonAnnotationBeanPostProcessor發現並處理的
@PostConstruct 初始化方法
@PreDestroy 銷燬方法
需要先註冊CommonAnnotationBeanPostProcessor類;效果與xml配置的init-method ,destroy-method一致。

使用JSR-330標準註解(依賴注入註解)
掃描方式與Spring註解一致,需要依賴javax.inject包

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

@Inject 等效於@Autowired, 可以使用於類,屬性,方法,構造器
@Named 如果想使用特定名稱進行依賴注入,使用@Named
@Named與@Component是等效的,通常與@Inject一起使用,跟@Qualifier類似

@Service
public class JsrService {

//  @Resource
//  @Autowired
    @Inject
    private JsrDao jsrDao;

//  @Resource
    public void setJsrDao(@Named("jsrDao")JsrDao jsrDao) {
        this.jsrDao = jsrDao;
    }

    public void save(String word){
        System.out.println("JsrService : "+word);
        jsrDao.save(word);
    }

    @PostConstruct
    public void init(){
        System.out.println("JsrService init !");
    }

    @PreDestroy
    public void destory(){
        System.out.println("JsrService destory !");
    }

}
發佈了60 篇原創文章 · 獲贊 27 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章