1、@EnableXXX註解驅動原理
從Spring 3.x開始中有許多@EnableXXX的註解,例如@EnableWebMvc,@EnableAsync,@EnableCaching等待註解,這些註解的意義在於根據需要完成自動裝配所需的bean。自動裝配好比汽車的自動擋一樣,它的實現大致分爲兩種方式,一種是通過自定義註解,另一種是實現相應的接口。
1.1 基於接口實現
一種是通過實現ImportSelector接口,另一種是實現ImportBeanDefinitionRegistrar接口。下面我們先看第一種實現方式。
package com.william.demo3;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
/**
* @Author: WilliamDream
* @Description:
* @Date: 2019/9/13 14:24
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MilkImportSelector.class)
public @interface EnableMilk {
Milk.Type type();
String name();
}
接口以及實現類
package com.william.demo3;
/**
* @Author: WilliamDream
* @Description:
* @Date: 2019/9/15 16:26
*/
public interface Milk {
void processMilk();
void getMilk();
enum Type{
MENGNIU,
YILI
}
}
package com.william.demo3;
import org.springframework.stereotype.Component;
/**
* @Author: WilliamDream
* @Description:
* @Date: 2019/9/15 21:45
*/
@Component
public class MengniuMilk implements Milk {
@Override
public void processMilk() {
System.out.println("加工蒙牛牛奶");
}
@Override
public void getMilk() {
System.out.println("獲取蒙牛牛奶");
}
}
package com.william.demo3;
import org.springframework.stereotype.Component;
/**
* @Author: WilliamDream
* @Description:
* @Date: 2019/9/15 21:44
*/
@Component
public class YiliMilk implements Milk {
@Override
public void processMilk() {
System.out.println("加工伊利牛奶");
}
@Override
public void getMilk() {
System.out.println("獲取伊利牛奶");
}
}
實現ImportSelector接口
package com.william.demo3;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import java.util.Map;
/**
* @Author: WilliamDream
* @Description:
* @Date: 2019/9/15 21:49
*/
public class MilkImportSelector implements ImportSelector{
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
//獲取EnableServer中所有的屬性方法
Map<String,Object> annotationAttributes = annotationMetadata.getAnnotationAttributes(EnableMilk.class.getName());
Milk.Type type = (Milk.Type) annotationAttributes.get("type");
String [] importClassNames = new String[0];
switch (type){
case MENGNIU:
importClassNames = new String[]{MengniuMilk.class.getName()};
break;
case YILI:
importClassNames = new String[]{YiliMilk.class.getName()};
break;
}
return importClassNames;
}
}
測試類
package com.william.demo3;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
/**
* @Author: WilliamDream
* @Description:
* @Date: 2019/9/15 22:11
*/
@Configuration
@EnableMilk(type = Milk.Type.MENGNIU,name = "蒙牛")
public class EnableDemoMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(EnableDemoMain.class);
context.refresh();
Milk milk = context.getBean(Milk.class);
milk.processMilk();
milk.getMilk();
}
}
代碼示例:github傳送門
第二種實現ImportBeanDefinitionRegistrar接口,方式差不多:
package com.william.demo4;
import com.william.demo3.MilkImportSelector;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.StringUtils;
/**
* @Author: WilliamDream
* @Description:
* @Date: 2019/9/15 25:19
*/
public class MilkImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar{
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
ImportSelector importSelector = new MilkImportSelector();
// String[] slecatedClassNames = importSelector.selectImports(annotationMetadata);
RootBeanDefinition beanDefinition=new RootBeanDefinition(importSelector.getClass());
String beanName= StringUtils.uncapitalize(importSelector.getClass().getSimpleName());
beanDefinitionRegistry.registerBeanDefinition(beanName,beanDefinition);
}
}
註解名改爲@EnableNewMilk
package com.william.demo4;
import com.william.demo3.Milk;
import com.william.demo3.MilkImportSelector;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
/**
* @Author: WilliamDream
* @Description:
* @Date: 2019/9/13 14:24
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MilkImportBeanDefinitionRegistrar.class)
public @interface EnableNewMilk {
Milk.Type type();
String name();
}
測試類
package com.william.demo4;
import com.william.demo3.Milk;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
/**
* @Author: WilliamDream
* @Description:
* @Date: 2019/9/15 22:25
*/
@Configuration
@EnableNewMilk(type = Milk.Type.YILI,name = "伊利")
public class EnableRegisterDemoMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(EnableRegisterDemoMain.class);
context.refresh();
Milk milk = context.getBean(Milk.class);
milk.processMilk();
milk.getMilk();
}
}
代碼示例:github傳送門
1.2 基於註解方式實現
@Configuration
public class DataSourceConfiguration {
@Bean
public DataSource dataSource(){
return new DataSource();
}
}
public class DataSource {
private String driverClassName;
private String url;
private String username;
private String password;
//geter&seter略
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DataSourceConfiguration.class)
public @interface EnableDataSource {
}
//測試類
@EnableDataSource
public class EnableDatasourceMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
//註冊當前引導類(被@Configuration註解標註)到Spring上下文中
context.register(EnableDatasourceMain.class);
context.refresh();
//獲取名爲dataSource的bean對象
Object object = context.getBean("dataSource");
System.out.println(object);
context.close();
}
}
測試結果爲:可以在控制檯打印出DataSource對象實例。
相關代碼示例:github傳送門
1.3 @Enable模塊驅動原理
1.3.1 裝在@Configuration Class
Spring3.0開始引入了@Configuration註解,此時還未引入@ComponentScan註解。@Configuration配合@Import註解使用,但還不能替換xml中<context:component-scan />自動掃描配置。
CongfigurationClassPostProcessor無論是在XML配置驅動還是在註解驅動的使用場景下,均通過AnnotationConfigUtils#registerAnnotationConfigProcessors方法的執行得到裝載。