Spring原理java
配置類mysql
package com.great.config; import com.great.service.BookService; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.ComponentScans; import com.great.bean.Person; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Repository; //配置類==配置文件 @Configuration //告訴Spring這是一個配置類 //@ComponentScans( // value = { // @ComponentScan(value="com.great",includeFilters = { // //按照註解 //// @Filter(type=FilterType.ANNOTATION,classes={Controller.class}), // @Filter(type=FilterType.ANNOTATION,classes={Repository.class}), //// //按照給定的類型 //// @Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}), // //使用自定義規則。由於上面的value="com.great"全部該包下的全部的類都會進入這個自定義的規則裏面進行匹配。 // @Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class}) // },useDefaultFilters = false) // } //) //@ComponentScan value:指定要掃描的包 //excludeFilters = Filter[] :指定掃描的時候按照什麼規則排除那些組件 //includeFilters = Filter[] :指定掃描的時候只須要包含哪些組件 //FilterType.ANNOTATION:按照註解 //FilterType.ASSIGNABLE_TYPE:按照給定的類型; //FilterType.ASPECTJ:使用ASPECTJ表達式 (這個基本不用) //FilterType.REGEX:使用正則指定 //FilterType.CUSTOM:使用自定義規則 //@ComponentScan(value="com.great") public class MainConfig { //給容器中註冊一個Bean;類型爲返回值的類型,id默認是用方法名做爲id,這裏咱們指定的id是person @Bean("person") public Person person01(){ return new Person("lisi", 20); } }
測試一下linux
package com.great; import com.great.config.MainConfig; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import com.great.bean.Person; public class MainTest { public static void main(String[] args) { //AnnotationConfigApplicationContext註解式的ApplicationContext ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); Person bean = applicationContext.getBean(Person.class); System.out.println(bean); //獲取ioc容器裏面的Person類型的全部bean的名字 String[] namesForType = applicationContext.getBeanNamesForType(Person.class); for (String name : namesForType) { System.out.println(name); } } }
運行結果spring
Person [name=張三, age=18, nickName=${person.nickName}] person
分割線
---
---sql
咱們在看MyTypeFilter.class編程
package com.great.config; import java.io.IOException; 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; public class MyTypeFilter implements TypeFilter { /** * metadataReader:讀取到的當前正在掃描的類的信息 * metadataReaderFactory:能夠獲取到其餘任何類信息的 */ @Override 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); //若是className包含er就匹配成功,也就掃描進IOC容器裏面了。 if(className.contains("er")){ return true; } return false; } }
懶加載只針對單實例bean
windows
在配置類中進行配置,以下圖app
package com.great.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 { /** * ConditionContext:判斷條件能使用的上下文(環境) * AnnotatedTypeMetadata:註釋信息 */ @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { // 是否爲linux系統 //一、能獲取到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"); //能夠判斷容器中的bean註冊狀況,也能夠給容器中註冊bean。 boolean definition = registry.containsBeanDefinition("person"); if(property.contains("linux")){ return true; } return false; } }
package com.great.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 { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment environment = context.getEnvironment(); String property = environment.getProperty("os.name"); if(property.contains("Windows")){ return true; } return false; } }
測試一下
運行結果
注意註解Conditional也能夠放在類上面的ide
該註解是給容器中註冊組件
post
能夠直接用import導
@Configuration //@Import(Color.class) //@Import導入組件,id默認是組件的全類名 @Import({Color.class,MyImportSelector.class}) public class MainConfig2 {
package com.great.condition; import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; //自定義邏輯返回須要導入的組件 public class MyImportSelector implements ImportSelector { //返回值,就是到導入到容器中的組件全類名 //AnnotationMetadata:當前標註@Import註解的類的全部註解信息 @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { //importingClassMetadata //方法不要返回null值,要否則報空指針異常 //這樣就把實體類Blue,Yellow掃描進容器裏面了。 return new String[]{"com.great.bean.Blue","com.great.bean.Black"}; } }
這樣就把Bule和Black加載到容器裏面了
package com.great.condition; 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; import com.great.bean.RainBow; public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { /** * AnnotationMetadata:當前類的註解信息 * BeanDefinitionRegistry:BeanDefinition註冊類; * 把全部須要添加到容器中的bean;調用 * BeanDefinitionRegistry.registerBeanDefinition手工註冊進來 */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //判斷是否有Black和Blue的bean boolean definition = registry.containsBeanDefinition("com.great.bean.Black"); boolean definition2 = registry.containsBeanDefinition("com.great.bean.Blue"); if(definition && definition2){ //指定Bean定義信息;(Bean的類型,Bean。。。) RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class); //註冊一個Bean,指定bean名 registry.registerBeanDefinition("rainBow", beanDefinition); } } }
運行結果就是把RainBow註冊進容器裏了,bean的名字是rainBow
如上圖所示有四種方式,上面咱們介紹完了import的方式,下面咱們看FactoryBean的方式
在配置類裏面加下面的代碼
package com.great.bean; import org.springframework.beans.factory.FactoryBean; //建立一個Spring定義的FactoryBean public class ColorFactoryBean implements FactoryBean<Color> { //返回一個Color對象,這個對象會添加到容器中 @Override public Color getObject() throws Exception { System.out.println("ColorFactoryBean...getObject..."); return new Color(); } @Override public Class<?> getObjectType() { return Color.class; } //是單例? //true:這個bean是單實例,在容器中保存一份 //false:多實例,每次獲取都會建立一個新的bean; @Override public boolean isSingleton() { return false; } }
測試一下
咱們先看第一種@Bean的方式
@Configuration public class MainConfigOfLifeCycle { // @Scope("prototype") //多實例:容器不會管理這個bean;容器不會調用銷燬方法。 @Bean(initMethod="init",destroyMethod="detory") public Car car(){ return new Car(); } }
package com.great.bean; import org.springframework.stereotype.Component; @Component public class Car { public Car(){ System.out.println("car constructor..."); } public void init(){ System.out.println("car ... init..."); } public void detory(){ System.out.println("car ... detory..."); } }
測試
package com.great.test; import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import com.great.config.MainConfigOfLifeCycle; public class IOCTest_LifeCycle { @Test public void test01(){ //一、建立ioc容器 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class); System.out.println("容器建立完成..."); // applicationContext.getBean("car"); //關閉容器 applicationContext.close(); } }
構造(對象建立):
單實例:在容器啓動的時候建立對象
多實例:在每次獲取的時候建立對象
第二種方式
第三種方式
加兩個註解就能夠了
第四種方式
這裏使用的BBP的原理也就是BeanPostProcessor bean的後置處理器
容器裏面的全部的bean都是要走這個咱們自定義的MyBeanPostProcessor
仍是上面咱們自定義的
斷點進來
這裏咱們看一下這個ApplicationContextAwareProcessor
這樣咱們自定義一個類來試一下
運行結果
1 . 標註在方法參數上面 @Bean+方法參數
2.放在構造器上面和放在方法上面
總結:
新建一個red類
package com.great.bean; 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.EmbeddedValueResolverAware; import org.springframework.stereotype.Component; import org.springframework.util.StringValueResolver; @Component public class Red implements ApplicationContextAware,BeanNameAware,EmbeddedValueResolverAware { private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { System.out.println("傳入的ioc:"+applicationContext); this.applicationContext = applicationContext; } @Override public void setBeanName(String name) { System.out.println("當前bean的名字:"+name); } @Override public void setEmbeddedValueResolver(StringValueResolver resolver) { //解析字符串 String resolveStringValue = resolver.resolveStringValue("你好 ${os.name} 我是 #{20*18}"); System.out.println("解析的字符串:"+resolveStringValue); } }
這樣就完成自定義組件想要使用Spring容器底層的一些組件
咱們來看一下原理
以ApplicationContextAware接口爲列例子
咱們在Red類裏面的setApplicationContext上面打斷點
咱們debug進去
能夠看出來是ApplicationContextAwareProcessor和BeanPostProcessor在起做用
原理總結:
咱們的bean在初始化的時候,利用後置處理器,判斷這個bean是否實現某個aware接口,而後調用相應的方法把組件傳過來
package com.great.config; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.EmbeddedValueResolverAware; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.context.annotation.PropertySource; import org.springframework.util.StringValueResolver; import com.great.bean.Yellow; import com.mchange.v2.c3p0.ComboPooledDataSource; /** * Profile: * Spring爲咱們提供的能夠根據當前環境,動態的激活和切換一系列組件的功能; * * 開發環境、測試環境、生產環境; * 數據源:(/A)(/B)(/C); * * * @Profile:指定組件在哪一個環境的狀況下才能被註冊到容器中,不指定,任何環境下都能註冊這個組件 * * 1)、加了環境標識的bean,只有這個環境被激活的時候才能註冊到容器中。默認是default環境 * 2)、寫在配置類上,只有是指定的環境的時候,整個配置類裏面的全部配置才能開始生效 * 3)、沒有標註環境標識的bean在,任何環境下都是加載的; */ @PropertySource("classpath:/dbconfig.properties") @Configuration public class MainConfigOfProfile implements EmbeddedValueResolverAware{ @Value("${db.user}") private String user; //實現EmbeddedValueResolverAware接口把咱們的值解析器引進來 //這也是一種方式用於解析值 private StringValueResolver valueResolver; private String driverClass; @Bean public Yellow yellow(){ return new Yellow(); } //默認是default // @Profile("default") @Profile("test") @Bean("testDataSource") //@Value註解也能夠直接寫在參數上面 public DataSource dataSourceTest(@Value("${db.password}")String pwd) throws Exception{ ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setUser(user); dataSource.setPassword(pwd); dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test"); dataSource.setDriverClass(driverClass); return dataSource; } @Profile("dev") @Bean("devDataSource") public DataSource dataSourceDev(@Value("${db.password}")String pwd) throws Exception{ ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setUser(user); dataSource.setPassword(pwd); dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/ssm_crud"); dataSource.setDriverClass(driverClass); return dataSource; } @Profile("prod") @Bean("prodDataSource") public DataSource dataSourceProd(@Value("${db.password}")String pwd) throws Exception{ ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setUser(user); dataSource.setPassword(pwd); dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/scw_0515"); dataSource.setDriverClass(driverClass); return dataSource; } @Override public void setEmbeddedValueResolver(StringValueResolver resolver) { this.valueResolver = resolver; driverClass = valueResolver.resolveStringValue("${db.driverClass}"); } }
package com.great.test; import javax.sql.DataSource; import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import com.great.bean.Yellow; import com.great.config.MainConfigOfProfile; public class IOCTest_Profile { //一、使用命令行動態參數: 在虛擬機參數位置加載 -Dspring.profiles.active=test //二、代碼的方式激活某種環境; @Test public void test01(){ AnnotationConfigApplicationContext applicationContext = //這裏用無參構造,目的是後面設置值 new AnnotationConfigApplicationContext(); //一、建立一個applicationContext //二、設置須要激活的環境 applicationContext.getEnvironment().setActiveProfiles("dev"); //三、註冊主配置類 applicationContext.register(MainConfigOfProfile.class); //四、啓動刷新容器 applicationContext.refresh(); String[] namesForType = applicationContext.getBeanNamesForType(DataSource.class); for (String string : namesForType) { System.out.println(string); } Yellow bean = applicationContext.getBean(Yellow.class); System.out.println(bean); applicationContext.close(); } }
【動態代理】
指在程序運行期間動態的將某段代碼切入到指定方法指定位置進行運行的編程方式;
建一個目標類
而後是切面類
而後是配置類
測試一下
沒有異常的時候運行結果以下
div運行。。。@Before:參數列表是:{[1, 1]} MathCalculator...div... div結束。。。@After div正常返回。。。@AfterReturning:運行結果:{1}
有異常的時候運行結果以下
div運行。。。@Before:參數列表是:{[1, 0]} MathCalculator...div... div結束。。。@After div異常。。。異常信息:{java.lang.ArithmeticException: / by zero}
//給配置類中加 @EnableAspectJAutoProxy 【開啓基於註解的aop模式】 @EnableAspectJAutoProxy
一切從這個註解提及。
要明白enable開頭的看給容器中註冊了什麼組件,這個組件何時工做,這個組件的功能是什麼?
上面流程能夠看出註解@EnableAspectJAutoProxy注入了AnnotationAwareAspectJAutoProxyCreator.class
AnnotationAwareAspectJAutoProxyCreator.class是一個後置處理器,這個能夠從他的父類看的出來
如今咱們重點研究AnnotationAwareAspectJAutoProxyCreator.