代碼地址:https://github.com/showkawa/spring-annotation/tree/master/src/main/java/com/brianjava
1.使用@Condition多條件註冊bean對象
2.@Import註解快速注入第三方bean對象
3.@EnableXXXX 開啓原理
4.基於ImportBeanDefinitionRegistrar註冊bean
5.基於FactoryBean註冊bean對象mysql
conditional字面意思條件句,亦即知足某些條件將該類註冊到IOC容器的意思git
@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(); }
能夠看到 Conditional註解的value是Condition的子類或實現類github
我這裏寫自定義的BriancCondition類來實現Condition接口,能夠看到Condition就一個返回boolean值的mathes()方法spring
public class BrianCondition implements Condition { /* * context:判斷條件能使用的上下文(環境) * metadata: 註釋信息 * */ public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { System.out.println("---male:" + context.getRegistry().containsBeanDefinition("person"));
//我這裏判斷ioc容器中是否有person實例,有返回true,不然返回false if(context.getRegistry().containsBeanDefinition("person")) return true; return false; } }
如今在配置類中加上,註冊Person類到容器中,而後用@Conditional控制是否將person01和person02註冊到容器中sql
@Configuration //告訴spring這是一個配置類 /* * @ComponentScan * value:只當於掃描的的包 * excludeFilters = 指定掃描的時候按照什麼規則排除哪些組件 * includeFilters = 指定掃描的時候只須要包含哪些組件 * Filter.ANNOTATION:按照註解 * Filter.ASSIGNABLE_TYPE: 按照給定的類型 * */ @ComponentScans(value = { @ComponentScan(value = "com.brian",includeFilters = { // @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class}), // @ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE,classes = {BookService.class}), @ComponentScan.Filter(type = FilterType.CUSTOM,classes = {BrianTypeFilter.class}) },useDefaultFilters = false) }) //@Import({Brian.class,Alan.class,BrianSelector.class}) public class MainConfig { @Bean("person") //給容器中註冊一個Bean;類型爲返回值的類型;id默認是方法名做爲id public Person person(){ return new Person("Alan",18); } /* * @Conditional() 按照條件註冊 * * */ @Conditional({BrianCondition.class}) @Bean("person01") public Person person01() { return new Person("Brian",17); } @Conditional({BrianCondition.class}) @Bean("person02") public Person person02() { return new Person("wenTao",19); } /* * *給容器中註冊組件 * 1,包掃描+ 組件標註註解(@Controller/@Service/@Repository/@Component)[本身寫的方法] * 2, @Bean [導入的第三方包裏面的組件] * 3,@Import [快速的給容器導入一個組件] * 1.@Import(要導入的組件class) * 2.ImportSelector:返回須要導入的組件的全類名數組 * 3.ImportBeanDefinitionRegistrar: 手動註冊bean到容器 * 4. 使用Spring提供的FactoryBean * */ @Bean public BrianBeanFactory brianBeanFactory() { return new BrianBeanFactory(); } }
加上測試類編程
public class MainTest { public static void main(String[] args) { ApplicationContext acac = new AnnotationConfigApplicationContext(MainConfig.class); /* ApplicationContext acac = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);*/ System.out.println("ioc容器建立成功"); // Alan alan1 = acac.getBean(Alan.class); // Alan alan2 = acac.getBean(Alan.class); //System.out.println("比較兩個Alan實例: " + (alan1 == alan2)); Person person1 = (Person) acac.getBean("person01"); System.out.println("---main---test---person1---: " + person1.toString()); Person person2 = (Person) acac.getBean("person02"); System.out.println("---main---test---person2---: " + person2.toString()); //關閉ioc容器 ((AnnotationConfigApplicationContext) acac).close(); } }
你會發現控制檯能夠獲取到對象person01和person02的信息數組
我這邊再作一個matches發返回false的測試,亦即修改BrianCondition類的matches返回值爲false,能夠下面的測試結果:NoSuchBeanDefinitionException: No bean named 'person01' available。所i以根據上面咱們測試的接口能夠知道@Conditional註解的使用也是簡單的app
經過@Import能夠快速的導入依賴的bean對象,好比咱們在配置類上導入其餘類@Import({Brian.class,Alan.class})less
configure配置類
@Configuration //告訴spring這是一個配置類 /* * @ComponentScan * value:只當於掃描的的包 * excludeFilters = 指定掃描的時候按照什麼規則排除哪些組件 * includeFilters = 指定掃描的時候只須要包含哪些組件 * Filter.ANNOTATION:按照註解 * Filter.ASSIGNABLE_TYPE: 按照給定的類型 * */ @ComponentScans(value = { @ComponentScan(value = "com.brian",includeFilters = { // @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class}), // @ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE,classes = {BookService.class}), @ComponentScan.Filter(type = FilterType.CUSTOM,classes = {BrianTypeFilter.class}) },useDefaultFilters = false) }) @Import({Brian.class,Alan.class}) //@Import({BrianSelector.class}) public class MainConfig { @Bean("person") //給容器中註冊一個Bean;類型爲返回值的類型;id默認是方法名做爲id public Person person(){ return new Person("Alan",18); } /* * @Conditional() 按照條件註冊 * * */ @Conditional({BrianCondition.class}) @Bean("person01") public Person person01() { return new Person("Brian",17); } @Conditional({BrianCondition.class}) @Bean("person02") public Person person02() { return new Person("wenTao",19); } /* * *給容器中註冊組件 * 1,包掃描+ 組件標註註解(@Controller/@Service/@Repository/@Component)[本身寫的方法] * 2, @Bean [導入的第三方包裏面的組件] * 3,@Import [快速的給容器導入一個組件] * 1.@Import(要導入的組件class) * 2.ImportSelector:返回須要導入的組件的全類名數組 * 3.ImportBeanDefinitionRegistrar: 手動註冊bean到容器 * 4. 使用Spring提供的FactoryBean * */ @Bean public BrianBeanFactory brianBeanFactory() { return new BrianBeanFactory(); } }
測試類
public class MainTest {
public static void main(String[] args) {
ApplicationContext acac =
new AnnotationConfigApplicationContext(MainConfig.class);
/* ApplicationContext acac =
new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);*/
System.out.println("ioc容器建立成功");
Alan alan1 = acac.getBean(Alan.class);
System.out.println("--ALAN--:" + alan1);
// Alan alan2 = acac.getBean(Alan.class);
//System.out.println("比較兩個Alan實例: " + (alan1 == alan2));
// Person person1 = (Person) acac.getBean("person01");
// System.out.println("---main---test---person1---: " + person1.toString());
// Person person2 = (Person) acac.getBean("person02");
// System.out.println("---main---test---person2---: " + person2.toString());
// MathCalculator mathCalculator = (MathCalculator) acac.getBean("mathCalculator");
// System.out.println("----get--mathCalculator---: " + mathCalculator);
//關閉ioc容器
((AnnotationConfigApplicationContext) acac).close();
}
}
@Import上面的使用方式屬於靜態的導入依賴,固然Import註解還有一種動態導入第三組件的方式是和ImportSelector結合使用
好比我在這裏MainConfig配置類上經過Import註解導入BrianSelector類.
@Configuration //告訴spring這是一個配置類 /* * @ComponentScan * value:只當於掃描的的包 * excludeFilters = 指定掃描的時候按照什麼規則排除哪些組件 * includeFilters = 指定掃描的時候只須要包含哪些組件 * Filter.ANNOTATION:按照註解 * Filter.ASSIGNABLE_TYPE: 按照給定的類型 * */ @ComponentScans(value = { @ComponentScan(value = "com.brian",includeFilters = { // @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class}), // @ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE,classes = {BookService.class}), @ComponentScan.Filter(type = FilterType.CUSTOM,classes = {BrianTypeFilter.class}) },useDefaultFilters = false) }) //@Import({Brian.class,Alan.class}) @Import({BrianSelector.class}) public class MainConfig { @Bean("person") //給容器中註冊一個Bean;類型爲返回值的類型;id默認是方法名做爲id public Person person(){ return new Person("Alan",18); } /* * @Conditional() 按照條件註冊 * * */ @Conditional({BrianCondition.class}) @Bean("person01") public Person person01() { return new Person("Brian",17); } @Conditional({BrianCondition.class}) @Bean("person02") public Person person02() { return new Person("wenTao",19); } /* * *給容器中註冊組件 * 1,包掃描+ 組件標註註解(@Controller/@Service/@Repository/@Component)[本身寫的方法] * 2, @Bean [導入的第三方包裏面的組件] * 3,@Import [快速的給容器導入一個組件] * 1.@Import(要導入的組件class) * 2.ImportSelector:返回須要導入的組件的全類名數組 * 3.ImportBeanDefinitionRegistrar: 手動註冊bean到容器 * 4. 使用Spring提供的FactoryBean * */ @Bean public BrianBeanFactory brianBeanFactory() { return new BrianBeanFactory(); } }
BrianSelector類,該類實現了ImportSelector接口,經過實現selectImports方法,返回須要動態導入到IOC容器的其餘的配置類的全量類名
/* * 自定義返回須要導入的組件 * */ public class BrianSelector implements ImportSelector { /** * * @param importingClassMetadata 當前被標記有@Import註解的全部註解信息 * @return */ public String[] selectImports(AnnotationMetadata importingClassMetadata) { System.out.println("----ImportSelector----:"+importingClassMetadata.getClassName()); //return new String[]{}; return new String[]{MainConfigOfAOP.class.getName()}; } }
MainConfigOfAOP配置類有注入MathCalculator對象
@Configuration ublic class MainConfigOfAOP { @Bean public MathCalculator mathCalculator() { return new MathCalculator(); } }
測試類,主要測試在IOC容器中獲取MathCalculator類的信息
public class MainTest { public static void main(String[] args) { ApplicationContext acac = new AnnotationConfigApplicationContext(MainConfig.class); /* ApplicationContext acac = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);*/ System.out.println("ioc容器建立成功"); // Alan alan1 = acac.getBean(Alan.class); // System.out.println("--ALAN--:" + alan1); // Alan alan2 = acac.getBean(Alan.class); //System.out.println("比較兩個Alan實例: " + (alan1 == alan2)); // Person person1 = (Person) acac.getBean("person01"); // System.out.println("---main---test---person1---: " + person1.toString()); // Person person2 = (Person) acac.getBean("person02"); // System.out.println("---main---test---person2---: " + person2.toString()); MathCalculator mathCalculator = (MathCalculator) acac.getBean("mathCalculator"); System.out.println("----get--mathCalculator---: " + mathCalculator); //關閉ioc容器 ((AnnotationConfigApplicationContext) acac).close(); } }
這裏簡單的擴展下@Import註解和@Bean註解異同點
1.都是導入的外部的Jar包
2.@Import的bean id是當前完整路徑地址註冊到IOC容器,@Bean的bean id是以方法名註冊到IOC容器,相比來講@Import注入類更加簡單
enable字面意思啓動,亦即開關的概念,有@EnableXXXX註解的地方,基本會看到@Import這個註解,通常他們都是結合起來使用的
好比看到我代碼裏面的MainConfigOfAutowired這個配置類,上有加上@EnableTransactionManagement這個註解,亦即打開事務管理
/** * 自動裝配 * Spring利用依賴注入(DI),完成對IOC容器中各個組件的依賴關係賦值 *1).@Autowired,自動注入: * 1.默認優先按照類型去容器中找對應的組件:applicationContext.getBean(BookDao.class); * 2.若是找到多個相同類型的組件,再將屬性方法的名稱做爲組件的id去容器中查找 * applicationContext.getBean("bookDao"); * 3.@Qualifier("bookDao"):使用@Qualifier指定須要裝配的組件id,而不是使用屬性名 * 4.自動裝配默認必定要將屬性賦值好,沒有就會報錯 * 使用@Autoeired(required=false),沒有默認值也不會報錯 * 5.@Primary, 讓Spring進行自動裝配的時候,默認使用首先的Bean * * 2).Spring還支持使用@Resource(JSR250)和@Inject(JSR330) [java規範的註解] * 3).@Autowired :構造器,參數,方法,屬性, * */ @EnableAspectJAutoProxy //開啓AOP代理自動配置 @EnableTransactionManagement //基於註解的事務管理 //@ComponentScan(value = {"com.brian.bean","com.write.annotation"}) @ComponentScan(value = {"com.write.annotation.transaction"}) @Configuration public class MainConfigOfAutowired { @Bean public DataSource dataSource() throws PropertyVetoException { ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setJdbcUrl("jdbc:mysql://remotemysql.com:3306/khgvUiO4eh");
咱們再看看@EnableTransactionManagement的代碼,經過Import快速導入TransactionManagementConfigurationSelector
@Retention(RetentionPolicy.RUNTIME) @Documented @Import(TransactionManagementConfigurationSelector.class) public @interface EnableTransactionManagement { /** * Indicate whether subclass-based (CGLIB) proxies are to be created ({@code true}) as * opposed to standard Java interface-based proxies ({@code false}). The default is * {@code false}. <strong>Applicable only if {@link #mode()} is set to * {@link AdviceMode#PROXY}</strong>. * <p>Note that setting this attribute to {@code true} will affect <em>all</em> * Spring-managed beans requiring proxying, not just those marked with * {@code @Transactional}. For example, other beans marked with Spring's * {@code @Async} annotation will be upgraded to subclass proxying at the same * time. This approach has no negative impact in practice unless one is explicitly * expecting one type of proxy vs another, e.g. in tests. */ boolean proxyTargetClass() default false; /** * Indicate how transactional advice should be applied. * <p><b>The default is {@link AdviceMode#PROXY}.</b> * Please note that proxy mode allows for interception of calls through the proxy * only. Local calls within the same class cannot get intercepted that way; an * {@link Transactional} annotation on such a method within a local call will be * ignored since Spring's interceptor does not even kick in for such a runtime * scenario. For a more advanced mode of interception, consider switching this to * {@link AdviceMode#ASPECTJ}. */ AdviceMode mode() default AdviceMode.PROXY; /** * Indicate the ordering of the execution of the transaction advisor * when multiple advices are applied at a specific joinpoint. * <p>The default is {@link Ordered#LOWEST_PRECEDENCE}. */ int order() default Ordered.LOWEST_PRECEDENCE; }
咱們再點進去看看TransactionManagementConfigurationSelector這個類,會發現selectImports會根據條件,選擇不一樣的配置類,因此這就是爲何說ImportSelector能夠動態加載其餘配置類了
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> { /** * Returns {@link ProxyTransactionManagementConfiguration} or * {@code AspectJ(Jta)TransactionManagementConfiguration} for {@code PROXY} * and {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()}, * respectively. */ @Override protected String[] selectImports(AdviceMode adviceMode) { switch (adviceMode) { case PROXY: return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()}; case ASPECTJ: return new String[] {determineTransactionAspectClass()}; default: return null; } } private String determineTransactionAspectClass() { return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ? TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME : TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME); } }
再回到MainConfigOfAOP這個配置類
/** * AOP: [動態代理] * 指在程序運行時期間將某段代碼切入到指定方法指定位置執行的編程方式 * * 1.將業務邏輯類和切面類注入到容器中(加上@Aspect註解表示切面類 ) * 2.在切面類上的每一個通知方法註解上註解,定義好切點 * 3.開啓基於註解的AOP模式: @EnableAspectAutoProxy * * * AOP 原理: * @EnableAspectJAutoProxy * @Import(AspectJAutoProxyRegistrar.class) 給容器中導入AspectJAutoProxyRegistrar類 * 利用AspectJAutoProxyRegistrar自定義向容器中註冊bean * AnnotationAwareAspectJAutoProxyCreator * ->AspectJAwareAdvisorAutoProxyCreator * ->AbstractAdvisorAutoProxyCreator * ->AbstractAutoProxyCreator * implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware * 後置處理器(在bean初始化完成先後執行) ,自動裝配BeanFactory * * * */ @Configuration @EnableAspectJAutoProxy public class MainConfigOfAOP { @Bean public MathCalculator mathCalculator() { return new MathCalculator(); } @Bean public LogAspects logAspects() { return new LogAspects(); } }
上面經過@EnableAspectJAutoProxy開啓基於註解的AOP模式,咱們點進去看看,又是熟悉的@Import註解
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy { /** * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed * to standard Java interface-based proxies. The default is {@code false}. */ boolean proxyTargetClass() default false; /** * Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal} * for retrieval via the {@link org.springframework.aop.framework.AopContext} class. * Off by default, i.e. no guarantees that {@code AopContext} access will work. * @since 4.3.1 */ boolean exposeProxy() default false; }
咱們再點進去看看AspectJAutoProxyRegistrar這個類
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar { /** * Register, escalate, and configure the AspectJ auto proxy creator based on the value * of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing * {@code @Configuration} class. */ @Override public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class); if (enableAspectJAutoProxy != null) { if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } if (enableAspectJAutoProxy.getBoolean("exposeProxy")) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); } } } }
你會發現該類經過實現ImportBeanDefinitionRegistrar接口的registerBeanDefinitions的方法,最終經過AopConfigUtil工具類來註冊到容器中
再次回到個人MainConfig這個配置類
@Configuration //告訴spring這是一個配置類 /* * @ComponentScan * value:只當於掃描的的包 * excludeFilters = 指定掃描的時候按照什麼規則排除哪些組件 * includeFilters = 指定掃描的時候只須要包含哪些組件 * Filter.ANNOTATION:按照註解 * Filter.ASSIGNABLE_TYPE: 按照給定的類型 * */ @ComponentScans(value = { @ComponentScan(value = "com.brian",includeFilters = { // @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class}), // @ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE,classes = {BookService.class}), @ComponentScan.Filter(type = FilterType.CUSTOM,classes = {BrianTypeFilter.class}) },useDefaultFilters = false) }) //@Import({Brian.class,Alan.class}) @Import({BrianSelector.class}) public class MainConfig { @Bean("person") //給容器中註冊一個Bean;類型爲返回值的類型;id默認是方法名做爲id public Person person(){ return new Person("Alan",18); } /* * @Conditional() 按照條件註冊 * * */ @Conditional({BrianCondition.class}) @Bean("person01") public Person person01() { return new Person("Brian",17); } @Conditional({BrianCondition.class}) @Bean("person02") public Person person02() { return new Person("wenTao",19); } /* * *給容器中註冊組件 * 1,包掃描+ 組件標註註解(@Controller/@Service/@Repository/@Component)[本身寫的方法] * 2, @Bean [導入的第三方包裏面的組件] * 3,@Import [快速的給容器導入一個組件] * 1.@Import(要導入的組件class) * 2.ImportSelector:返回須要導入的組件的全類名數組 * 3.ImportBeanDefinitionRegistrar: 手動註冊bean到容器 * 4. 使用Spring提供的FactoryBean * */ @Bean public BrianBeanFactory brianBeanFactory() { return new BrianBeanFactory(); } }
經過@Bean註解注入BrianBeanFactory,咱們點進去看看
public class BrianBeanFactory implements FactoryBean<WenTao> { //獲取對象 public WenTao getObject() throws Exception { return new WenTao(); } //獲取對象的類型 public Class<?> getObjectType() { return WenTao.class; } //獲取對象是單例模式仍是原型模式 public boolean isSingleton() { return true; } }
BrianBeanFactoryt經過實現了BeanFactory接口的getObject()獲取到bean對象
上測試類
public class MainTest { public static void main(String[] args) { ApplicationContext acac = new AnnotationConfigApplicationContext(MainConfig.class); /* ApplicationContext acac = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);*/ System.out.println("ioc容器建立成功"); // Alan alan1 = acac.getBean(Alan.class); // System.out.println("--ALAN--:" + alan1); // Alan alan2 = acac.getBean(Alan.class); //System.out.println("比較兩個Alan實例: " + (alan1 == alan2)); // Person person1 = (Person) acac.getBean("person01"); // System.out.println("---main---test---person1---: " + person1.toString()); // Person person2 = (Person) acac.getBean("person02"); // System.out.println("---main---test---person2---: " + person2.toString()); // MathCalculator mathCalculator = (MathCalculator) acac.getBean("mathCalculator"); // System.out.println("----get--mathCalculator---: " + mathCalculator); BrianBeanFactory beanFactory = acac.getBean(BrianBeanFactory.class); WenTao wentao = null; try { wentao = beanFactory.getObject(); } catch (Exception e) { e.printStackTrace(); } System.out.println("----get--WenTao---: " + wentao); //關閉ioc容器 ((AnnotationConfigApplicationContext) acac).close(); } }
這裏拓展一點FactoryBean和 BeanFactory的區別,FactoryBean是建立bean對象,BeanFactory是獲取bean對象。