Day51~(spring註解高級IOC)52

第一部分:Spring的IOC

1、設計模式-工廠模式

    工廠模式是咱們最經常使用的實例化對象模式了,它是用工廠中的方法代替new建立對象的一種設計模式。
  咱們以Mybatis的SqlSession接口爲例,它有一個實現類DefaultSqlSession,若是咱們要建立該接口的實例對象:SqlSession sqlSession=new DefaultSqlSession();
  但是,實際狀況是,一般咱們都要在建立SqlSession實例時作點初始化的工做,好比解析XML,封裝鏈接數據庫的信息等等。
    在建立對象時,若是有一些不得不作的初始化操做時,咱們首先到的是,可使用構造函數,這樣生成實例就寫成:SqlSession sqlSession=new DefaultSqlSession(傳入配置文件的路徑);
    可是,若是建立sqlSession實例時所作的初始化工做不是像賦值這樣簡單的事,多是很長一段代碼,若是也寫入構造函數中,那你的代碼很難看了(就須要Refactor重構)。
    爲何說代碼很難看,初學者可能沒有這種感受,咱們分析以下,初始化工做若是是很長一段代碼,說明要作的工做不少,將不少工做裝入一個方法中,至關於將不少雞蛋放在一個籃子裏,是很危險的,這也是有悖於Java面向對象的原則,面向對象的封裝(Encapsulation)和分派(Delegation)告訴咱們,儘可能將長的代碼分派「切割」成每段,將每段再「封裝」起來(減小段和段之間耦合聯繫性),這樣,就會將風險分散,之後若是須要修改,只要更改每段,不會再發生牽一動百的事情。
因此,Mybatis框架在使用時爲咱們提供了SqlSessionFactory工廠類,經過方法獲取到SqlSession對象。同時方法有不少重載,用於實現不一樣的需求。這個方法就是openSession(),它支持傳入Connection參數來保證鏈接的一致性;支持傳入true|false來保證事務的提交時機等等。

2、IOC和DI

一、IOC-Inversion of Control

    它的含義爲:控制反轉。它不是一個技術,而是一種思想。其做用是用於削減代碼間的耦合。它的實現思想就是利用了工廠設計模式,把建立對象代碼從具體類中剝離出去,交由工廠來完成,從而下降代碼間的依賴關係。
  耦合有以下分類:
  (1) 內容耦合。當一個模塊直接修改或操做另外一個模塊的數據時,或一個模塊不經過正常入口而轉入另外一個模塊時,這樣的耦合被稱爲內容耦合。內容耦合是最高程度的耦合,應該避免使用之。
(2) 公共耦合。兩個或兩個以上的模塊共同引用一個全局數據項,這種耦合被稱爲公共耦合。在具備大量公共耦合的結構中,肯定到底是哪一個模塊給全局變量賦了一個特定的值是十分困難的。
(3) 外部耦合 。一組模塊都訪問同一全局簡單變量而不是同一全局數據結構,並且不是經過參數表傳遞該全局變量的信息,則稱之爲外部耦合。
(4) 控制耦合 。一個模塊經過接口向另外一個模塊傳遞一個控制信號,接受信號的模塊根據信號值而進行適當的動做,這種耦合被稱爲控制耦合。
(5) 標記耦合 。若一個模塊A經過接口向兩個模塊B和C傳遞一個公共參數,那麼稱模塊B和C之間存在一個標記耦合。
(6) 數據耦合。模塊之間經過參數來傳遞數據,那麼被稱爲數據耦合。數據耦合是最低的一種耦合形式,系統中通常都存在這種類型的耦合,由於爲了完成一些有意義的功能,每每須要將某些模塊的輸出數據做爲另外一些模塊的輸入數據。
(7) 非直接耦合 。兩個模塊之間沒有直接關係,它們之間的聯繫徹底是經過主模塊的控制和調用來實現的。
爲何要解耦:
耦合是影響軟件複雜程度和設計質量的一個重要因素,在設計上咱們應採用如下原則:若是模塊間必須存在耦合,就儘可能使用數據耦合,少用控制耦合,限制公共耦合的範圍,儘可能避免使用內容耦合。

二、DI-Dependency Injection

    它的全稱是依賴注入。是spring框架核心ioc的具體實現。
舉例說明:咱們的程序在編寫時,經過控制反轉,把對象的建立交給了spring,可是代碼中不可能出現沒有依賴的狀況。ioc解耦只是下降他們的依賴關係,但不會消除。例如:咱們的業務層仍會調用持久層的方法。
那這種業務層和持久層的依賴關係,在使用spring以後,就讓spring來維護了。
簡單的說,就是坐等框架把持久層對象傳入業務層,而不用咱們本身去獲取。

3、Spring註解驅動開發入門

一、寫在最前

spring在2.5版本引入了註解配置的支持,同時從Spring 3版本開始,Spring JavaConfig項目提供的許多特性成爲核心Spring框架的一部分。所以,可使用Java而不是XML文件來定義應用程序類外部的bean。在這裏面官方文檔爲咱們提供了四個基本註解@Configuration,@Bean,@Import,@DependsOn

ioc-1-1

二、註解驅動入門案例介紹

1.需求:
實現保存一條數據到數據庫。
2.表結構:
create table account(
id int primary key auto_increment,
name varchar(50),
money double(7,2)
);
3.要求:
使用spring框架中的JdbcTemplate和DriverManagerDataSource
使用純註解配置spring的ioc

三、案例實現

3.一、導入座標

<dependencies>
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-context</artifactId>
           <version>5.1.6.RELEASE</version>
       </dependency>
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-jdbc</artifactId>
           <version>5.1.6.RELEASE</version>
       </dependency>
       <dependency>
           <groupId>mysql</groupId>
           <artifactId>mysql-connector-java</artifactId>
           <version>5.1.45</version>
       </dependency>
   </dependencies>

3.二、編寫配置類

/**
* spring的配置類
* 用於替代xml配置
* @author 黑馬程序員
* @Company http://www.itheima.com
*/
@Configuration
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbc.properties")
public class SpringConfiguration {
}


/**
* 鏈接數據庫的配置
* @author 黑馬程序員
* @Company http://www.itheima.com
*/
public class JdbcConfig {

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


   @Bean("jdbcTemplate")
   public JdbcTemplate createJdbcTemplate(DataSource dataSource){
       return new JdbcTemplate(dataSource);
  }


   @Bean("dataSource")
   public DataSource createDataSource(){
       DriverManagerDataSource dataSource = new DriverManagerDataSource();
       dataSource.setDriverClassName(driver);
       dataSource.setUrl(url);
       dataSource.setUsername(username);
       dataSource.setPassword(password);
       return dataSource;
  }
}

3.三、編寫配置文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_day01
jdbc.username=root
jdbc.password=1234

3.四、編寫測試類

/**
* @author 黑馬程序員
* @Company http://www.itheima.com
*/
public class SpringAnnotationDrivenTest {

/**
* 測試
* @param args
*/
public static void main(String[] args) {
//1.獲取容器
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
//2.根據id獲取對象
JdbcTemplate jdbcTemplate = ac.getBean("jdbcTemplate",JdbcTemplate.class);
//3.執行操做
jdbcTemplate.update("insert into account(name,money)values(?,?)","test",12345);
}
}

4、IOC的經常使用註解分析

一、用於註解驅動的註解

1.一、@Configuration

1.1.一、源碼
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {

/**
* Explicitly specify the name of the Spring bean definition associated with the
* {@code @Configuration} class. If left unspecified (the common case), a bean
* name will be automatically generated.
* <p>The custom name applies only if the {@code @Configuration} class is picked
* up via component scanning or supplied directly to an
* {@link AnnotationConfigApplicationContext}. If the {@code @Configuration} class
* is registered as a traditional XML bean definition, the name/id of the bean
* element will take precedence.
* @return the explicit component name, if any (or empty String otherwise)
* @see org.springframework.beans.factory.support.DefaultBeanNameGenerator
*/
@AliasFor(annotation = Component.class)
String value() default "";
}
1.1.二、說明
做用:
它是在spring3.0版本以後加入的。此註解是spring支持註解驅動開發的一個標誌。代表當前類是spring的一個配置類,做用是替代spring的applicationContext.xml。但其本質就是@Component註解,被此註解修飾的類,也會被存入spring的ioc容器。
屬性:
value:
用於存入spring的Ioc容器中Bean的id。
使用場景:
在註解驅動開發時,用於編寫配置的類,一般可使用此註解。通常狀況下,咱們的配置也會分爲主從配置,@Configuration通常出如今主配置類上。例如,入門案例中的SpringConfiguration類上。值得注意的是,若是咱們在註解驅動開發時,構建ioc容器使用的是傳入字節碼的構造函數,此註解能夠省略。可是若是傳入的是一個包,此註解則不能省略。
1.1.三、示例
	在註解驅動的入門案例中,因爲沒有了applicationContext.xml,就無法在xml中配置spring建立容器要掃描的包了。那麼,咱們本身寫的一些類,經過註解配置到ioc容器中也沒法實現了。此時就可使用此註解來代替spring的配置文件。
/**
* spring的配置類
* 用於替代xml配置
* @author 黑馬程序員
* @Company http://www.itheima.com
*/
@Configuration
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbc.properties")
@ComponentScan("com.itheima")
public class SpringConfiguration {
}

1.二、@ComponentScan

1.2.一、源碼
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
/**
*/
@AliasFor("basePackages")
String[] value() default {};

/**
*/
@AliasFor("value")
String[] basePackages() default {};

/**
*/
Class<?>[] basePackageClasses() default {};

/**
*/
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

/**
*/
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;

/**
*/
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;

/**
*/
String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;

/**
*/
boolean useDefaultFilters() default true;

/**
*/
Filter[] includeFilters() default {};

/**
*/
Filter[] excludeFilters() default {};

/**
*/
boolean lazyInit() default false;


/**
* Declares the type filter to be used as an {@linkplain ComponentScan#includeFilters
* include filter} or {@linkplain ComponentScan#excludeFilters exclude filter}.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface Filter {
/**
*/
FilterType type() default FilterType.ANNOTATION;

/**
*/
@AliasFor("classes")
Class<?>[] value() default {};

/**
*/
@AliasFor("value")
Class<?>[] classes() default {};

/**
*/
String[] pattern() default {};
}
}
1.2.二、說明
做用:
用於指定建立容器時要掃描的包。該註解在指定掃描的位置時,能夠指定包名,也能夠指定掃描的類。同時支持定義掃描規則,例如包含哪些或者排除哪些。同時,它還支持自定義Bean的命名規則
屬性:
value:
用於指定要掃描的包。當指定了包的名稱以後,spring會掃描指定的包及其子包下的全部類。
basePackages:
它和value做用是同樣的。
basePackageClasses:
指定具體要掃描的類的字節碼。
nameGenrator:
指定掃描bean對象存入容器時的命名規則。詳情請參考第五章第4小節的BeanNameGenerator及其實現類。
scopeResolver:
用於處理並轉換檢測到的Bean的做用範圍。
soperdProxy:
用於指定bean生成時的代理方式。默認是Default,則不使用代理。詳情請參考第五章第5小節ScopedProxyMode枚舉。
resourcePattern:
用於指定符合組件檢測條件的類文件,默認是包掃描下的 **/*.class
useDefaultFilters:
是否對帶有@Component @Repository @Service @Controller註解的類開啓檢測,默認是開啓的。
includeFilters:
自定義組件掃描的過濾規則,用以掃描組件。
FilterType有5種類型:
ANNOTATION, 註解類型 默認
ASSIGNABLE_TYPE,指定固定類
ASPECTJ, ASPECTJ類型
REGEX,正則表達式
CUSTOM,自定義類型
詳細用法請參考第五章第6小節自定義組件掃描過濾規則
excludeFilters:
自定義組件掃描的排除規則。
lazyInit:
組件掃描時是否採用懶加載 ,默認不開啓。
使用場景:
在註解驅動開發時,咱們本身編寫的類都使用註解的方式進行配置,要想讓spring添加到ioc容器中,就須要使用此註解來實現組件的掃描。
細節:
在spring4.3版本以後還加入了一個@ComponentScans的註解,該註解就是支持配置多個@ComponentScan。
1.2.三、示例
	在入門案例中,若是咱們加入了dao或者記錄日誌的工具類,這些使用了@Component或其衍生註解配置的bean,要想讓他們進入ioc容器,就少不了使用@ComponentScan

package com.itheima.dao.impl;
import com.itheima.dao.AccountDao;
/**
* @author 黑馬程序員
* @Company http://www.itheima.com
*/
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao{
//持久層開發(此處沒有考慮mybatis代理dao方式或者其餘持久層技術,由於不但願和其餘框架技術關聯)
}

/**
* spring的配置類
* 用於替代xml配置
* @author 黑馬程序員
* @Company http://www.itheima.com
*/
@Configuration
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbc.properties")
@ComponentScan("com.itheima")
public class SpringConfiguration {
}

1.三、@Bean

1.3.一、源碼
package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
/**
*/
@AliasFor("name")
String[] value() default {};

/**
*/
@AliasFor("value")
String[] name() default {};

/**
*/
@Deprecated
Autowire autowire() default Autowire.NO;

/**
*/
boolean autowireCandidate() default true;

/**
*/
String initMethod() default "";

/**
*/
String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
}
1.3.二、說明
做用:
它寫在方法上,表示把當前方法的返回值存入spring的ioc容器。
同時還能夠出如今註解上,做爲元註解來使用。
屬性:
name:
用於指定存入spring容器中bean的標識。支持指定多個標識。當不指定該屬性時,默認值是當前方法的名稱。
value:
此屬性是在4.3.3版本以後加入的。它和name屬性的做用是同樣的。
autowireCandidate:
用於指定是否支持自動按類型注入到其餘bean中。隻影響@Autowired註解的使用。不影響@Resource註解注入。默認值爲true,意爲容許使用自動按類型注入。
initMethod:
用於指定初始化方法。
destroyMethod:
用於指定銷燬方法。
使用場景:
一般狀況下,在基於註解的配置中,咱們用於把一個類存入spring的ioc容器中,首先考慮的是使用@Component以及他的衍生註解。可是若是遇到要存入容器的Bean對象不是咱們寫的類,此時沒法在類上添加@Component註解,這時就須要此註解了。
示例:
例如,在咱們配置JdbcTemplate使用Spring內置數據源DriverManagerDataSource時,數據源類是spring-jdbc這個jar包中類,此時咱們沒法編輯,在上面加註解,此時就可使用@Bean註解配置。
1.3.三、示例代碼
@Bean("jdbcTemplate")
public JdbcTemplate createJdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}

1.四、@Import

1.4.一、源碼
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
*/
Class<?>[] value();
}
1.4.二、說明
做用:
該註解是寫在類上的,一般都是和註解驅動的配置類一塊兒使用的。其做用是引入其餘的配置類。使用了此註解以後,可使咱們的註解驅動開發和早期xml配置同樣,分別配置不一樣的內容,使配置更加清晰。同時指定了此註解以後,被引入的類上能夠再也不使用@Configuration,@Component等註解。
屬性:
value:
用於指定其餘配置類的字節碼。它支持指定多個配置類。
關於ImportSelector和ImportBeanDefinitionRegistrar請參考第五章第7小節@Import註解的高級分析。
使用場景:
當咱們在使用註解驅動開發時,因爲配置項過多,若是都寫在一個類裏面,配置結構和內容將雜亂不堪,此時使用此註解能夠把配置項進行分門別類進行配置。
1.4.三、示例
	在入門案例中,咱們使用了SpringConfiguration作爲主配置類,而鏈接數據庫相關的配置被分配到了JdbcConfig配置類中,此時使用在SpringConfiguration類上使用@Import註解把JdbcConfig導入進來就能夠了。
/**
* spring的配置類
* 用於替代xml配置
* @author 黑馬程序員
* @Company http://www.itheima.com
*/
@Configuration
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbc.properties")
@ComponentScan("com.itheima")
public class SpringConfiguration {
}

/**
* 鏈接數據庫的配置
* @author 黑馬程序員
* @Company http://www.itheima.com
*/
public class JdbcConfig {

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


@Bean("jdbcTemplate")
public JdbcTemplate createJdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}


@Bean("dataSource")
public DataSource createDataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}

1.五、@PropertySource

1.5.一、源碼
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(PropertySources.class)
public @interface PropertySource {

/**
*/
String name() default "";

/**
*/
String[] value();

/**
*/
boolean ignoreResourceNotFound() default false;

/**
*/
String encoding() default "";

/**
*/
Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;

}
1.5.二、說明
做用:
用於指定讀取資源文件的位置。注意,它不只支持properties,也支持xml文件,而且經過YAML解析器,配合自定義PropertySourceFactory實現解析yml配置文件(詳情請參考第五章第8小節自定義PropertySourceFactory實現YAML文件解析)。
屬性:
name:
指定資源的名稱。若是沒有指定,將根據基礎資源描述生成。
value:
指定資源的位置。能夠是類路徑,也能夠是文件路徑。
ignoreResourceNotFound:
指定是否忽略資源文件有沒有,默認是false,也就是說當資源文件不存在時spring啓動將會報錯。
encoding:
指定解析資源文件使用的字符集。當有中文的時候,須要指定中文的字符集。
factory:
指定讀取對應資源文件的工廠類,默認的是PropertySourceFactory。
使用場景:
咱們實際開發中,使用註解驅動後,xml配置文件就沒有了,此時一些配置若是直接寫在類中,會形成和java源碼的緊密耦合,修改起來不方法。此時一些配置可使用properties或者yml來配置就變得很靈活方便。
1.5.三、示例
在入門案例中,咱們鏈接數據庫的信息若是直接寫到JdbcConfig類中,當須要修改時,就面臨修改源碼的問題,此時使用@PropertySource和SpringEL表達式,就能夠把配置放到properties文件中了。
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_day01
jdbc.username=root
jdbc.password=1234
/**
* 鏈接數據庫的配置
* @author 黑馬程序員
* @Company http://www.itheima.com
*/
public class JdbcConfig {

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

/**
* spring的配置類
* 用於替代xml配置
* @author 黑馬程序員
* @Company http://www.itheima.com
*/
@Configuration
@Import(JdbcConfig.class)
@PropertySource(value = "classpath:jdbc.properties")
@ComponentScan("com.itheima")
public class SpringConfiguration {
}

二、註解驅動開發之注入時機和設定注入條件的註解

2.一、@DependsOn

2.1.一、源碼
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DependsOn {
String[] value() default {};
}
2.1.二、說明
做用:
用於指定某個類的建立依賴的bean對象先建立。spring中沒有特定bean的加載順序,使用此註解則可指定bean的加載順序。(在基於註解配置中,是按照類中方法的書寫順序決定的)
屬性:
value:
用於指定bean的惟一標識。被指定的bean會在當前bean建立以前加載。
使用場景:
在觀察者模式中,分爲事件,事件源和監聽器。通常狀況下,咱們的監聽器負責監聽事件源,當事件源觸發了事件以後,監聽器就要捕獲,而且作出相應的處理。以此爲前提,咱們確定但願監聽器的建立時間在事件源以前,此時就可使用此註解。
2.1.三、示例
/**
* @author 黑馬程序員
* @Company http://www.itheima.com
*/
@Component
public class CustomerListener {

public CustomerListener(){
System.out.println("監聽器建立了。。。");
}
}

/**
* @author 黑馬程序員
* @Company http://www.itheima.com
*/
@Component
@DependsOn("customerListener")
public class Customer {

public Customer(){
System.out.println("事件源建立了。。。");
}
}

ioc-dependson

2.二、@Lazy

2.2.一、源碼
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Lazy {
boolean value() default true;
}
2.2.二、說明
做用:
用於指定單例bean對象的建立時機。在沒有使用此註解時,單例bean的生命週期與容器相同。可是當使用了此註解以後,單例對象的建立時機變成了第一次使用時建立。注意:這不是延遲加載思想(由於不是每次使用時都建立,只是第一次建立的時機改變了)。
屬性:
value:
指定是否採用延遲加載。默認值爲true,表示開啓。
使用場景:
在實際開發中,當咱們的Bean是單例對象時,並非每一個都須要一開始都加載到ioc容器之中,有些對象能夠在真正使用的時候再加載,當有此需求時,便可使用此註解。值得注意的是,此註解只對單例bean對象起做用,當指定了@Scope註解的prototype取值後,此註解不起做用。
2.2.三、示例
/**
* @author 黑馬程序員
* @Company http://www.itheima.com
*/
@Component
@Lazy
//@Lazy(value = false)
//@Scope("prototype")
public class LazyBean {

public LazyBean(){
System.out.println("LazyBean對象建立了");
}
}

/**
* @author 黑馬程序員
* @Company http://www.itheima.com
*/
public class SpringAnnotationDrivenTest {

/**
* 測試
* @param args
*/
public static void main(String[] args) {
//1.獲取容器
ApplicationContext ac = new AnnotationConfigApplicationContext("config");
//2.根據id獲取對象
LazyBean lazyBean = (LazyBean)ac.getBean("lazyBean");
System.out.println(lazyBean);
}

}

ioc-lazy

ioc-lazy2

2.三、@Conditional

2.3.一、源碼
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {

/**
* All {@link Condition Conditions} that must {@linkplain Condition#matches match}
* in order for the component to be registered.
*/
Class<? extends Condition>[] value();
}
2.3.二、說明
做用:
它的做用是根據條件選擇注入的bean對象。
屬性:
value:
用於提供一個Condition接口的實現類,實現類中須要編寫具體代碼實現注入的條件。
使用場景:
當咱們在開發時,可能會使用多平臺來測試,例如咱們的測試數據庫分別部署到了linux和windows兩個操做系統上面,如今根據咱們的工程運行環境選擇鏈接的數據庫。此時就可使用此註解。同時基於此註解引出的@Profile註解,就是根據不一樣的環境,加載不一樣的配置信息,詳情請參考第五章第9小節@Profile的使用。
2.3.三、示例
/**
* 鏈接數據庫的配置
* @author 黑馬程序員
* @Company http://www.itheima.com
*/
public class JdbcConfig {

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




/**
* linux系統注入的數據源
* @param lDriver
* @param lUrl
* @param lUsername
* @param lPassword
* @return
*/
@Bean("dataSource")
@Conditional(LinuxCondition.class)
public DataSource createLinuxDataSource(@Value("${linux.driver}") String lDriver,
@Value("${linux.url}")String lUrl,
@Value("${linux.username}")String lUsername,
@Value("${linux.password}")String lPassword){
DriverManagerDataSource dataSource = new DriverManagerDataSource(lUrl,lUsername,lPassword);
dataSource.setDriverClassName(lDriver);
System.out.println(lUrl);
return dataSource;
}

/**
* windows系統注入的數據源
* @return
*/
@Bean("dataSource")
@Conditional(WindowsCondition.class)
public DataSource createWindowsDataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
System.out.println(url);
return dataSource;
}
}

/**
* @author 黑馬程序員
* @Company http://www.itheima.com
*/
public class LinuxCondition implements Condition {

@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//獲取ioc使用的beanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//獲取類加載器
ClassLoader classLoader = context.getClassLoader();
//獲取當前環境信息
Environment environment = context.getEnvironment();
//獲取bean定義的註冊類
BeanDefinitionRegistry registry = context.getRegistry();

//得到當前系統名
String property = environment.getProperty("os.name");
//包含Windows則說明是windows系統,返回true
if (property.contains("Linux")){
return true;
}
return false;
}
}


/**
* @author 黑馬程序員
* @Company http://www.itheima.com
*/
public class WindowsCondition implements Condition {

@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//獲取ioc使用的beanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//獲取類加載器
ClassLoader classLoader = context.getClassLoader();
//獲取當前環境信息
Environment environment = context.getEnvironment();

/**
* 獲取全部系統環境變量
*/
if(environment instanceof StandardEnvironment){
StandardEnvironment standardEnvironment = (StandardEnvironment)environment;
Map<String,Object> map = standardEnvironment.getSystemProperties();
for(Map.Entry<String,Object> me : map.entrySet()){
System.out.println(me.getKey()+","+me.getValue());
}
}

//獲取bean定義的註冊類
BeanDefinitionRegistry registry = context.getRegistry();

//得到當前系統名
String property = environment.getProperty("os.name");
//包含Windows則說明是windows系統,返回true
if (property.contains("Windows")){
return true;
}
return false;
}
}
linux.driver=com.mysql.jdbc.Driver
linux.url=jdbc:mysql://localhost:3306/ssm
linux.username=root
linux.password=1234
-------------------------------------------------------------------------------------
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_day01
jdbc.username=root
jdbc.password=1234

### java

相關文章
相關標籤/搜索