一、使用 maven 建立一個 java 項目,依賴以下:java
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.12.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency>
二、建立測試 pojo:linux
package com.springanno.pojo; public class User { public User() { } public User(String name, Integer age) { this.name = name; this.age = age; } private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
@Configuration 標註的類就是一個 Spring 配置類,配置類的做用就至關於以前咱們使用的 Spring 配置文件。web
一、建立配置類,經過 @Configuration 註解標註一個類爲配置類:spring
package com.springanno.config; import org.springframework.context.annotation.Configuration; /** * 配置類 */ @Configuration public class MainConfig { }
二、經過讀取註解配置類來建立 IOC 容器:windows
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
@ComponentScan 註解的做用至關於在 Spring 配置文件中配置 <context:component-scan> 標籤。數組
一、建立測試 Service 類:session
package com.springanno.service; import org.springframework.stereotype.Service; @Service public class UserService { }
二、使用 @ComponentScan 註解:app
package com.springanno.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * 配置類 */ @Configuration @ComponentScan("com.springanno") public class MainConfig { }
三、測試:maven
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); UserService bean = applicationContext.getBean(UserService.class); System.out.println(bean); /* com.springanno.service.UserService@77e9807f */
@ComponentScan 相關屬性:ide
excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})}, // 不掃描指定註解標註的類
includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Service.class})}, // 僅掃描指定註解標註的類 useDefaultFilters = false
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Service.class}), // 過濾指定註解標註的類 @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {UserService.class}), // 過濾給定類型的類 @ComponentScan.Filter(type = FilterType.ASPECTJ,pattern = {"com.springanno.service.*Service"}), // 經過 ASPECTJ 表達式過濾指定類 @ComponentScan.Filter(type = FilterType.REGEX, pattern ={".*.*Service"}), // 經過正則過濾指定類
還可自定義過濾規則,先自定義一個 TypeFilter 類:
package com.springanno.config; import org.springframework.core.io.Resource; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.ClassMetadata; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.filter.TypeFilter; import java.io.IOException; public class MyTypeFilter implements TypeFilter { /** * @param metadataReader 正在掃描的類的信息 * @param metadataReaderFactory 可獲取其它類信息 * @return 若是返回 true ,說明當前掃描的類匹配成功 */ public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { // 獲取當前類註解信息 AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); // 獲取當前正在掃描的類的類信息 ClassMetadata classMetadata = metadataReader.getClassMetadata(); // 獲取當前類資源 Resource resource = metadataReader.getResource(); String className = classMetadata.getClassName(); System.out.println("--->"+className); return true; } }
使用以下:
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class}) // 自定義規則過濾
@Bean 註解的做用至關於在 Spring 配置文件中配置 <bean> 標籤。
package com.springanno.config; import com.springanno.pojo.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MainConfig { /** * 默認狀況下方法名爲 bean 的 id * 返回值爲加入到 IOC 容器的實例 * 可經過 @Bean 註解的 value 屬性指定 bean 的 id * * <bean id="user1" class="com.springanno.pojo.User"> * <property name="name" value="張三"/> * <property name="age" value="20"/> * </bean> */ @Bean(value = "user1") public User user(){ return new User("張三", 20); } }
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); Object user1 = applicationContext.getBean("user1"); System.out.println(user1); /* User{name='張三', age=20} */
還能夠經過 FactoryBean 方式註冊:
package com.springanno.config; import com.springanno.pojo.User; import org.springframework.beans.factory.FactoryBean; public class UserFactoryBean implements FactoryBean<User> { /** * 返回的對象將會添加到容器 */ public User getObject() throws Exception { return new User("ice",22); } /** * 對象類型 */ public Class<?> getObjectType() { return User.class; } /** * 是否單例 */ public boolean isSingleton() { return true; } }
package com.springanno.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MainConfig { @Bean public UserFactoryBean userFactoryBean(){ return new UserFactoryBean(); } }
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { System.out.println(beanDefinitionName); } Object user = applicationContext.getBean("userFactoryBean"); System.out.println(user); /* mainConfig userFactoryBean User{name='ice', age=22} */ // 能夠看到,咱們獲取的是 userFactoryBean,但實際上返回的是 userFactoryBean 對應實例的 getObject 方法的返回值 // 若是咱們的確要獲取 userFactoryBean 對應的實例,可經過 &id 這種方式獲取: UserFactoryBean userFactoryBean = (UserFactoryBean) applicationContext.getBean("&userFactoryBean"); System.out.println(userFactoryBean); /* com.springanno.config.UserFactoryBean@4461c7e3 */
@Scope 註解做用至關於在 <bean> 標籤上的 scope 屬性。
package com.springanno.config; import com.springanno.pojo.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; @Configuration public class MainConfig { /** * @Scope * value 屬性有以下可選值: * singleton(默認): 單例。IOC 容器啓動時就會調用方法建立對象放到容器,以後每次使用都是直接從容器中取。 * prototype : 多例。只有要使用該對象時纔會調用該方法建立對象。 * request (web 環境): 一次請求。 * session (web 環境): 一次會話。 */ @Scope("prototype") @Bean public User user(){ return new User("張三", 20); } }
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); Object user1 = applicationContext.getBean("user"); Object user2 = applicationContext.getBean("user"); System.out.println(user1==user2); /* false */
@Lazy 註解做用至關於在 <bean> 標籤上配置屬性 lazy-init="true" 。
package com.springanno.config; import com.springanno.pojo.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; @Configuration public class MainConfig { /** * 懶加載:針對單實例 bean,控制容器啓動時不建立對象,第一次獲取該 bean 時才建立對象。 */ @Lazy @Bean public User user(){ System.out.println("建立了"); return new User("張三", 20); } }
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); /* [無任何輸出] */
按照指定的條件進行判斷,知足條件纔在容器中註冊 bean。
有以下示例,若是當前操做系統爲 Windows 時,咱們註冊一個 id 爲 windows 的 bean,當前系統爲 Linux 時,註冊一個 id 爲 linux 的 bean。
一、建立 Condition 類:
package com.springanno.condition; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; /** * 判斷是不是 Windows 系統 */ public class WindowsCondition implements Condition { public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { Environment environment = conditionContext.getEnvironment(); String osName = environment.getProperty("os.name").toLowerCase(); return osName.contains("windows"); } }
package com.springanno.condition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; /** * 判斷是不是 Linux 系統 */ public class LinuxCondition implements Condition { /** * @param conditionContext 判斷條件能使用的上下文 * @param annotatedTypeMetadata 註解信息 */ public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { // 獲取到容器使用的 BeanFactory ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory(); // 獲取類加載器 ClassLoader classLoader = conditionContext.getClassLoader(); // 獲取當前環境信息 Environment environment = conditionContext.getEnvironment(); // 獲取 bean 定義的註冊類 BeanDefinitionRegistry registry = conditionContext.getRegistry(); String osName = environment.getProperty("os.name").toLowerCase(); return osName.contains("linux"); } }
二、使用:
package com.springanno.config; import com.springanno.condition.LinuxCondition; import com.springanno.condition.WindowsCondition; import com.springanno.pojo.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @Configuration public class MainConfig { @Conditional({WindowsCondition.class}) @Bean public User windows() { return new User("windows", 20); } @Conditional({LinuxCondition.class}) @Bean public User linux() { return new User("linux", 3); } }
三、測試:
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); String[] beanNames = applicationContext.getBeanDefinitionNames(); for (String beanName : beanNames) { System.out.println(beanName); } /* mainConfig windows */ // 當前是在 windows 下,因此能夠看到只註冊了 windows bean
@Import 提供了下面幾種註冊 bean 到容器的方式。
能夠直接將指定 bean 實例註冊到容器:
@Configuration /** * @Import 能夠直接將註冊指定 bean 到容器中,id 爲 bean 的類全路徑名 */ @Import({User.class}) public class MainConfig { }
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { System.out.println(beanDefinitionName); } /* mainConfig com.springanno.pojo.User */
經過 @ImportSelector 返回 bean 的全路徑數組批量註冊:
package com.springanno.config; import com.springanno.pojo.User; import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; import org.springframework.util.StringUtils; import java.util.ArrayList; import java.util.List; public class MyImportSelector implements ImportSelector { public String[] selectImports(AnnotationMetadata annotationMetadata) { List<String> classNameList = new ArrayList<String>(); classNameList.add(User.class.getName()); return StringUtils.toStringArray(classNameList); } }
package com.springanno.config; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @Configuration /** * MyImportSelector.selectImports() 方法返回的類的全路徑列表, * @Import 將會把這些全路徑對應的類都註冊到容器,id 爲類的全路徑名 */ @Import({MyImportSelector.class}) public class MainConfig { }
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { System.out.println(beanDefinitionName); } /* mainConfig com.springanno.pojo.User */
經過 @ImportBeanDefinitionRegistrar 手動定義 bean 的信息完成 bean 的註冊:
package com.springanno.config; import com.springanno.pojo.User; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.type.AnnotationMetadata; public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { /** * * @param annotationMetadata 當前類註解信息 * @param beanDefinitionRegistry BeanDefinition 註冊類 * */ public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) { // 判斷 IOC 容器中是否已經註冊了 user boolean isContainsUser = beanDefinitionRegistry.containsBeanDefinition("user"); // 能夠經過 beanDefinitionRegistry.registerBeanDefinition() 方法註冊全部須要添加到容器中的 bean if(!isContainsUser){ RootBeanDefinition beanDefinition = new RootBeanDefinition(User.class.getName()); // id=user beanDefinitionRegistry.registerBeanDefinition("user", beanDefinition); } } }
package com.springanno.config; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @Configuration /** * @Import 會執行 MyImportBeanDefinitionRegistrar.registerBeanDefinitions(),在該方法中完成 bean 的註冊 */ @Import({MyImportBeanDefinitionRegistrar.class}) public class MainConfig { }
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { System.out.println(beanDefinitionName); } /* mainConfig user */
這裏說的生命週期指的是註冊進 IoC 容器的 bean 的生命週期,可經過下面幾種方式來監聽 bean 的生命週期變化。
經過 @Bean 註解的 initMethod 屬性來指定 bean 的初始化方法, destroyMethod 屬性來指定 bean 的銷燬方法。
package com.springanno.pojo; public class User { public User() { } public User(String name, Integer age) { System.out.println("User 構造方法"); this.name = name; this.age = age; } private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public void init(){ System.out.println("User 初始化方法"); } public void destroy(){ System.out.println("User 銷燬方法"); } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
package com.springanno.config; import com.springanno.pojo.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MainConfig { /* 經過 @Bean 註解的 initMethod 屬性來指定 bean 的初始化方法,destroyMethod 屬性來指定 bean 的銷燬方法 構造方法: 單實例 bean 在容器啓動的時候執行 多實例 bean 在每次獲取 bean 的時候執行 初始化方法: 構造方法執行後執行 銷燬方法: 單實例 bean 在容器關閉時執行 多實例時,容器不會管理這個 bean ,因此不會調用銷燬方法 */ @Bean(initMethod = "init", destroyMethod = "destroy") public User user(){ return new User("tom", 12); } }
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); ((AnnotationConfigApplicationContext) applicationContext).close(); /* User 構造方法 User 初始化方法 User 銷燬方法 */
經過將 @PostConstruct 和 @PreDestroy 註解標註在 bean 的方法上讓其成爲初始化方法和銷燬方法。
package com.springanno.pojo; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; public class User { public User() { } public User(String name, Integer age) { System.out.println("User 構造方法"); this.name = name; this.age = age; } private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @PostConstruct public void init() throws Exception { System.out.println("User init"); } @PreDestroy public void destroy() throws Exception { System.out.println("User destroy"); } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
package com.springanno.config; import com.springanno.pojo.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MainConfig { @Bean public User user(){ return new User("tom", 12); } }
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); ((AnnotationConfigApplicationContext) applicationContext).close(); /* User 構造方法 User init User destroy */
經過讓 bean 實現 InitializingBean 來定義初始化邏輯,實現 DisposableBean 來定義銷燬邏輯。
package com.springanno.pojo; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; public class User implements InitializingBean, DisposableBean { public User() { } public User(String name, Integer age) { System.out.println("User 構造方法"); this.name = name; this.age = age; } private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public void afterPropertiesSet() throws Exception { System.out.println("User afterPropertiesSet"); } public void destroy() throws Exception { System.out.println("User destroy"); } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
package com.springanno.config; import com.springanno.pojo.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MainConfig { @Bean public User user(){ return new User("tom", 12); } }
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); ((AnnotationConfigApplicationContext) applicationContext).close(); /* User 構造方法 User afterPropertiesSet User destroy */
定義一個類實現 BeanPostProcessor 接口,將其註冊到容器中後它即可以監聽容器中全部 bean 的初始化前和初始化後操做。
package com.springanno.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.stereotype.Component; @Component public class MyBeanPostProcessor implements BeanPostProcessor { /** * 初始化操做執行以前執行 */ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println(beanName + "---->postProcessBeforeInitialization"); return bean; } /** * 初始化操做執行以後執行 */ public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println(beanName + "---->postProcessAfterInitialization"); return bean; } }
package com.springanno.pojo; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; public class User { public User() { } public User(String name, Integer age) { System.out.println("User 構造方法"); this.name = name; this.age = age; } private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @PostConstruct public void init() throws Exception { System.out.println("User init"); } @PreDestroy public void destroy() throws Exception { System.out.println("User destroy"); } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); ((AnnotationConfigApplicationContext) applicationContext).close(); /* User 構造方法 user---->postProcessBeforeInitialization User init user---->postProcessAfterInitialization User destroy */
Spring 底層給 bean 賦值、@Autowried 注入、生命週期註解等功能都是經過 BeanPostProcessor 完成的。
ScheduledAnnotationBeanPostProcessor (org.springframework.scheduling.annotation)
AdvisorAdapterRegistrationManager (org.springframework.aop.framework.adapter)
BeanPostProcessorChecker in PostProcessorRegistrationDelegate (org.springframework.context.support)
ImportAwareBeanPostProcessor in ConfigurationClassPostProcessor (org.springframework.context.annotation)
LoadTimeWeaverAwareProcessor (org.springframework.context.weaving)
AbstractAdvisingBeanPostProcessor (org.springframework.aop.framework)
AbstractBeanFactoryAwareAdvisingPostProcessor (org.springframework.aop.framework.autoproxy)
DestructionAwareBeanPostProcessor (org.springframework.beans.factory.config)
InitDestroyAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation)
ApplicationListenerDetector in PostProcessorRegistrationDelegate (org.springframework.context.support)
ApplicationContextAwareProcessor (org.springframework.context.support)
MergedBeanDefinitionPostProcessor (org.springframework.beans.factory.support)
InitDestroyAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation)
RequiredAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation)
AutowiredAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation)
ApplicationListenerDetector in PostProcessorRegistrationDelegate (org.springframework.context.support)
BeanValidationPostProcessor (org.springframework.validation.beanvalidation)
InstantiationAwareBeanPostProcessor (org.springframework.beans.factory.config)
SmartInstantiationAwareBeanPostProcessor (org.springframework.beans.factory.config)
CommonAnnotationBeanPostProcessor (org.springframework.context.annotation)
使用 @Value 能夠爲屬性賦基本數值,也能夠經過語法 @Value("#{SpEL}") 經過 SpEL 表達式賦值,還能夠經過 ${屬性名} 取出配置文件(環境變量)中的值,而 @PropertySource 註解就能夠幫咱們讀取屬性文件中的屬性到環境變量。
user.testKey=testValue
package com.springanno.pojo; import org.springframework.beans.factory.annotation.Value; public class User { public User() { } public User(String name, Integer age) { this.name = name; this.age = age; } @Value("張三") private String name; @Value("#{19+3}") private Integer age; @Value("${user.testKey}") private String testKey; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + ", testKey='" + testKey + '\'' + '}'; } }
package com.springanno.config; import com.springanno.pojo.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; @Configuration @PropertySource(value = {"classpath:user.properties"}) // 使用該註解將指定屬性文件加載到環境變量 public class MainConfig { @Bean public User user() { return new User(); } }
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); User bean = applicationContext.getBean(User.class); System.out.println(bean); /* User{name='張三', age=22, testKey='testValue'} */
經過使用 @Profile 註解,Spring 能夠實現根據當前環境動態的激活和切換一些列組件的功能。
package com.springanno.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; @Configuration public class MainConfig { /* 指定環境了的組件只有在指定環境下運行時纔會註冊到容器中,不指定環境的組件任何環境都能註冊到容器中,默認爲 default 環境 這裏模擬三個環境下的數據源 */ @Profile("dev") @Bean("dataSource") public String devStr(){ return "devDataSource"; } @Profile("test") @Bean("dataSource") public String testStr(){ return "testDataSource"; } @Profile("prod") @Bean("dataSource") public String prodStr(){ return "prodDataSource"; } }
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); Object dataSource = applicationContext.getBean("dataSource"); System.out.println(dataSource); // 經過 VM-options 來指定運行環境 // -Dspring.profiles.active=dev 時,輸出 devDataSource // -Dspring.profiles.active=test 時,輸出 testDataSource // -Dspring.profiles.active=prod 時,輸出 prodDataSource
String profileStr = "test"; AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); // 指定運行環境 applicationContext.getEnvironment().setActiveProfiles(profileStr); applicationContext.register(MainConfig.class); applicationContext.refresh(); Object dataSource = applicationContext.getBean("dataSource"); System.out.println(dataSource); /* 編碼指定運行環境 profileStr="test" 時,輸出 testDataSource profileStr="prod" 時,輸出 prodDataSource profileStr="dev" 時,輸出 devDataSource */
Spring 利用 DI(依賴注入),完成對 IoC 容器中各個組件的依賴賦值。
@Autowired 將會自動從 IoC 容器中查找與標註屬性同類型的 bean 注入, 若是容器中有多個相同類型的 bean,將會注入 id 與 屬性名相同的那個 bean。
package com.springanno.dao; import org.springframework.stereotype.Repository; @Repository public class UserDao { }
package com.springanno.service; import com.springanno.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { /** * 默認狀況下,若是容器中沒有該屬性類型對應的 bean,那麼將會拋出異常 * 可經過指定 @Autowired 的屬性 required = false 讓這次的自動裝配沒必要須,此時容器中若是不存在該類型 bean 就不會拋出異常 */ @Autowired(required = false) private UserDao userDao; public void printDao() { System.out.println(userDao); } }
package com.springanno.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.springanno") public class MainConfig { }
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); UserService userService = applicationContext.getBean(UserService.class); System.out.println(userService); userService.printDao(); /* com.springanno.service.UserService@60dcc9fe com.springanno.dao.UserDao@222114ba */
@Autowired 不只能夠用在屬性上,還能夠用在方法上:
package com.springanno.service; import com.springanno.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { private UserDao userDao; /* 將會從容器中找到與參數類型相同的 bean 傳入來執行該方法 */ @Autowired public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void printDao() { System.out.println(userDao); } }
也能夠標註在構造器上:
package com.springanno.service; import com.springanno.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { private UserDao userDao; /* 將會從容器中找到與參數類型相同的 bean 傳入來執行該構造器來建立 UserService 實例 */ @Autowired public UserService(UserDao userDao) { this.userDao = userDao; } public void printDao() { System.out.println(userDao); } }
不只如此,還能夠標註在參數上:
package com.springanno.service; import com.springanno.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { private UserDao userDao; /* 將會從容器中找到與參數類型相同的 bean 傳入來執行該構造器來建立 UserService 實例 */ public UserService(@Autowired UserDao userDao) { this.userDao = userDao; } public void printDao() { System.out.println(userDao); } }
若是當前組件只有一個有參構造器, @Autowired 能夠省略不寫:
package com.springanno.service; import com.springanno.dao.UserDao; import org.springframework.stereotype.Service; @Service public class UserService { private UserDao userDao; /* 當前組件只有一個有參構造器 將會從容器中找到與參數類型相同的 bean 傳入來自動執行該構造器來建立 UserService 實例 */ public UserService (UserDao userDao) { this.userDao = userDao; } public void printDao() { System.out.println(userDao); } }
經過 @Bean 標註的方法註冊 bean 時,該方法的參數也會默認從容器中獲取:
package com.springanno.dao; import org.springframework.stereotype.Repository; @Repository public class UserDao { public UserDao(){} }
package com.springanno.service; import com.springanno.dao.UserDao; import org.springframework.stereotype.Service; @Service public class UserService { private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void printDao() { System.out.println(userDao); } }
package com.springanno.config; import com.springanno.dao.UserDao; import com.springanno.service.UserService; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.springanno") public class MainConfig { @Bean("userService2") public UserService userService(UserDao userDao){ UserService userService = new UserService(); userService.setUserDao(userDao); return userService; } }
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); UserService userService = (UserService) applicationContext.getBean("userService2"); UserDao userDao = applicationContext.getBean(UserDao.class); userService.printDao(); System.out.println(userDao); /* com.springanno.dao.UserDao@21507a04 com.springanno.dao.UserDao@21507a04 */
能夠經過 @Qualifier 與 @Autowired 搭配使用來指定這次要裝配 bean 的 id,以下:
package com.springanno.dao; import org.springframework.stereotype.Repository; @Repository public class UserDao { public UserDao(){} }
package com.springanno.service; import com.springanno.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; @Service public class UserService { @Qualifier("userDao") // 指定裝配 id 爲 userDao 的 bean @Autowired private UserDao userDao; public void printDao(){ System.out.println(userDao); } }
package com.springanno.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.springanno") public class MainConfig { }
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); UserService userService = applicationContext.getBean(UserService.class); System.out.println(userService); userService.printDao(); /* com.springanno.service.UserService@60dcc9fe com.springanno.dao.UserDao@222114ba */
@Primary 也是用來對自動裝配進行控制的,他用來指定當容器中存在多個類型相同的 bean 時,自動裝配優先裝配哪一個 bean,和 @Bean 一塊兒使用。固然,它不能和 @Qualifier 同時使用。
package com.springanno.dao; import org.springframework.stereotype.Repository; @Repository public class UserDao { public UserDao(){} }
package com.springanno.service; import com.springanno.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { @Autowired private UserDao userDao; public void printDao(){ System.out.println(userDao); } }
package com.springanno.config; import com.springanno.dao.UserDao; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; @Configuration @ComponentScan("com.springanno") public class MainConfig { @Primary // id 爲 userDao2 的 bean 優先裝配 @Bean public UserDao userDao2() { return new UserDao(); } }
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); UserService userService = applicationContext.getBean(UserService.class); System.out.println(userService); userService.printDao(); UserDao userDao = (UserDao) applicationContext.getBean("userDao2"); System.out.println(userDao); /* com.springanno.service.UserService@3b2da18f com.springanno.dao.UserDao@5906ebcb com.springanno.dao.UserDao@5906ebcb */
@Resource 註解(JSR250 中定義)至關於 @Autowired 和 @Qualifier 註解一塊兒使用,既能完成自動裝配,也能指定要裝配 bean 的 id,不支持 @Primary 註解。
package com.springanno.dao; import org.springframework.stereotype.Repository; @Repository public class UserDao { public UserDao(){} }
package com.springanno.service; import com.springanno.dao.UserDao; import org.springframework.stereotype.Service; import javax.annotation.Resource; @Service public class UserService { @Resource(name = "userDao2") private UserDao userDao; public void printDao(){ System.out.println(userDao); } }
package com.springanno.config; import com.springanno.dao.UserDao; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.springanno") public class MainConfig { @Bean public UserDao userDao2() { return new UserDao(); } }
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); UserService userService = applicationContext.getBean(UserService.class); System.out.println(userService); userService.printDao(); UserDao userDao = (UserDao) applicationContext.getBean("userDao2"); System.out.println(userDao); /* com.springanno.service.UserService@2d9d4f9d com.springanno.dao.UserDao@4034c28c com.springanno.dao.UserDao@4034c28c */
@Inject 註解(JSR330 中定義)使用與 @Autowired 註解一致,只是沒有屬性。
package com.springanno.dao; import org.springframework.stereotype.Repository; @Repository public class UserDao { public UserDao(){} }
package com.springanno.service; import com.springanno.dao.UserDao; import org.springframework.stereotype.Service; import javax.inject.Inject; @Service public class UserService { /* 與 Autowired 相同,需導包 <dependency> <groupId>javax.inject</groupId> <artifactId>com.springsource.javax.inject</artifactId> <version>1.0.0</version> </dependency> */ @Inject private UserDao userDao; public void printDao() { System.out.println(userDao); } }
package com.springanno.config; import com.springanno.dao.UserDao; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; @Configuration @ComponentScan("com.springanno") public class MainConfig { @Primary @Bean public UserDao userDao2() { return new UserDao(); } }
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); UserService userService = applicationContext.getBean(UserService.class); System.out.println(userService); userService.printDao(); UserDao userDao = (UserDao) applicationContext.getBean("userDao2"); System.out.println(userDao); /* com.springanno.service.UserService@2aa5fe93 com.springanno.dao.UserDao@5c1a8622 com.springanno.dao.UserDao@5c1a8622 */
@Autowired 是 Spring 定義的,而 @Resource 和 @Inject 是Java 規範中定義的。
若是咱們自定義的組件想要使用 Spring 容器底層的一些組件,例如 ApplicationContext、BeanFactory 等,可讓自定義組件實現相應的 Aware 接口,Spring 容器啓動時會經過接口的回調給咱們注入相應的組件。
package com.springanno.service; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanNameAware; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.EnvironmentAware; import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; @Service("testUserServer") public class UserService implements ApplicationContextAware, BeanNameAware, EnvironmentAware { public void setBeanName(String beanName) { System.out.println(beanName); } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { System.out.println(applicationContext.getBean("testUserServer")); } public void setEnvironment(Environment environment) { System.out.println(environment); } }
package com.springanno.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.springanno") public class MainConfig { }
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); /* testUserServer StandardEnvironment {activeProfiles=[], defaultProfiles=[default], propertySources=[MapPropertySource {name='systemProperties'}, SystemEnvironmentPropertySource {name='systemEnvironment'}]} com.springanno.service.UserService@3f56875e */
下面是 Spring 提供的繼承了 Aware 接口的類:
ApplicationEventPublisherAware (org.springframework.context)
NotificationPublisherAware (org.springframework.jmx.export.notification)
MessageSourceAware (org.springframework.context)
BeanFactoryAware (org.springframework.beans.factory)
EnvironmentAware (org.springframework.context)
EmbeddedValueResolverAware (org.springframework.context)
ResourceLoaderAware (org.springframework.context)
ImportAware (org.springframework.context.annotation)
LoadTimeWeaverAware (org.springframework.context.weaving)
BeanNameAware (org.springframework.beans.factory)
BeanClassLoaderAware (org.springframework.beans.factory)
ApplicationContextAware (org.springframework.context)
一、建立被代理類:
package com.springanno.service; public class CalculateService { public Integer div(int i,int j){ return i/j; } }
二、編寫切面類:
package com.springanno.aspects; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.*; import java.util.Arrays; @Aspect public class LogAspect { // 若是要在別的類引用這個切入點表達式,能夠使用 com.springanno.aspects.LogAspect.pc() @Pointcut(value = "execution(* com.springanno.service.CalculateService.div(..))") public void pc() { } @Before("pc()") public void logStart(JoinPoint joinPoint) { Object[] args = joinPoint.getArgs(); String methodName = joinPoint.getSignature().getName(); System.out.printf("%s 運行了,參數爲 %s \n", methodName, Arrays.toString(args)); } @After("pc()") public void logEnd(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); System.out.println(methodName + " 執行完畢"); } @AfterReturning(value = "pc()", returning = "result") public void logReturn(JoinPoint joinPoint, Object result) { // 注意,若是使用 JoinPoint 參數,該參數只能在第一個位置 String methodName = joinPoint.getSignature().getName(); System.out.printf("%s 運行了,結果爲 %s \n", methodName, result); } @AfterThrowing(value = "pc()",throwing = "ex") public void logException(JoinPoint joinPoint,Exception ex){ String methodName = joinPoint.getSignature().getName(); System.out.printf("%s 出現異常,異常信息爲 %s \n", methodName, ex); } }
三、將被代理類與切面類註冊到容器,使用 @EnableAspectJAutoProxy 註解開啓切面自動代理功能:
package com.springanno.config; import com.springanno.aspects.LogAspect; import com.springanno.service.CalculateService; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @EnableAspectJAutoProxy // 開啓切面自動代理功能 @Configuration public class MainConfig { @Bean public CalculateService calculateService(){ return new CalculateService(); } @Bean public LogAspect logAspect(){ return new LogAspect(); } }
四、測試:
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); CalculateService calculateService = applicationContext.getBean(CalculateService.class); calculateService.div(4, 1); /* div 運行了,參數爲 [4, 1] div 執行完畢 div 運行了,結果爲 4 */
updating....