1. 程式人生 > >spring-boot 深入學習

spring-boot 深入學習

--在使用spring-boot的時候踩過太多的坑,很多都是因為不懂spring真正的原理造成的,所以寫下這麼篇文章,希望這篇文章能夠幫助大家理解spring真正原理少走彎路

一:啟動原理

--每個程式都有一個主入口,也就是main方法,main裡面呼叫SpringApplication.run()啟動整個spring-boot程式
eg:一個最簡單的spring-boot啟動類
@SpringBootApplication
public class Springrun{
	public static void main(String[] args) {
		SpringApplication.run(Springrun.class, args);
	}
}
解釋:
[email protected]繼承了@Configuration、@EnableAutoConfiguration、@ComponentScan,這三個註解的特性後面會講到,預設掃描的包是以SpringApplication.run(Springrun.class, args) 引數springrun.class所在的包作為base package。
注意:如果想要啟動整個程式,這個類必須要放到根包裡面去。

run方法執行順序:
1.初始化SpringApplicationRunListeners並且開始監聽
2.載入StandardEnvironment(包括系統引數   環境變數引數 properties/yml檔案   profiles)
3.把environment set到Listeners裡面
4.預設通過AnnotationConfigApplicationContext掃描所有的註解類,建立並且註冊bean(預設都是建立的單列)
5.建立context,並且把environment set到context裡面
備註:如果需要初始化spring-boot即將完成的時候馬上去做一些事情,我們可以實現CommandLineRunner該介面
分析: SpringApplication在初始化的時候,會去建立environment,這裡預設是建立的StandardEnvironment,接著這個類會去載入sources,其uml圖如下

接著會建立spring的啟動模式,spring的啟動模式有很多:xml配置啟動、annotation啟動、檔案啟動、web啟動等等如下
spring-boot預設是選擇AnnotationConfigApplicationContext啟動上下文,其類圖如下
這個類直接間接實現了很多介面,包括beanFactory、resourceresolver、evenPublisher等等,整合所有spring需要的東西。 接著會去呼叫其父類AbstactApplicationContext.refresh()建立bean,這個方法只能呼叫一次。該方法執行的內容如下
其方法裡面所使用的beanFactory是DefaultListableBeanFactory,次類的類圖如下:

這個類解決的問題是建立bean,包括一些簡單的bean,還有一些有依賴的bean。 注意
在執行main經常程式會退出,丟擲Process finished with exit code 0,原因是程式執行main方法結束退出,出現這樣的原因是maven匯入jar出錯沒有匯入依賴的容器(jetty or tomcat),最簡單的跑起main方法並且不停止,只需要在maven引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.3.3.RELEASE</version>
</dependency>

二:配置屬性載入

spring-boot 屬性分為兩種:
1.spring-boot預設屬性(application.properties或者application.yml  系統屬性  環境變數屬性)
2.自定義載入屬性

spring-boot啟動的時候會預設掃描resources目錄下面的application.properties或者application.yml,掃瞄結束後會把值放入到Environment。
初始化bean的時候需要用到配置的值,我們可以根據這三種方式去獲取:
1).繼承EnvironmentAware獲取Environment
2).通過註解@value進行填充值
3).注入environment  通過environment獲取
具體程式碼:
@Configuration
public class MyProperty  implements EnvironmentAware {
    
    public void setEnvironment(Environment environment) {
        //方案一   繼承EnvironmentAware    獲取yml裡面的值
        RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(environment, "jdbc.");
        String url = propertyResolver.getProperty("url");
    }


    //方案二  通過註解value進行填充值
    @Value("${jdbc.url}")
    private String url;


    //方案三  注入environment  通過environment獲取
    @Autowired
    private Environment environment;
    public void init(){
        RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(environment, "jdbc.");
        String url = environment.getProperty("url");
    }
}

如果需要改造載入方式,比如先去遠端或者資料庫獲取配置項,沒有的話再讀取本地的application.properties,
需要用到PropertyPlaceholderConfigurer這個類,此類所適用的@Value("${jdbc.url}")佔位符,佔位符先去PropertyPlaceholderConfigurer資源類找,
找不到的話再去Environment環境裡面找
/**
*此類優先載入級別非常高,所以不需要擔心其它需要用到屬性值的類無法獲取屬性值
*/
@Configuration
public class ConfigConfiguration {
    @Bean
    public PropertyPlaceholderConfigurer initPropertyPlaceholder() {
        //建立佔位符類
        PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
        Properties[] properties = new Properties[3];
		//TODO add properties
        ppc.setPropertiesArray(properties);
        return ppc;
    }
}


如果需要修改Environment裡面的屬性我們可以這麼做:
/**
*spring 會自動注入applicationContext 到此方法引數
*/
@Bean
public String changeEnvironment(PropertiesCache propertiesCache, ApplicationContext applicationContext){
	//支援Environment獲取  修改容器裡面StandardServletEnvironment
   StandardEnvironment standardEnvironment =applicationContext.getBean(StandardEnvironment.class);
   standardEnvironment.getPropertySources().addLast(new PropertiesPropertySource(null, null));
	return null;
}


三:annotation總結分析

基於類的註解:
--初始裝載
@SpringBootApplication              spring-boot程式入口標誌類
@Configuration                             自動配置,類似於載入spring載入xml 裝配所有的bean事務等 所標識的類裡面可以使用@Bean 並且啟動的時候會初始化bean
@EnableAutoConfiguration       Spring-Boot 根據應用所宣告的依賴來對Spring框架進行自動配置
@ComponentScan                     規定掃描包的範圍
@PropertySources                  property掃描載入
--業務區分
@Component 定義該bean為一個普通元件
@Repository 定義該bean是一個倉儲,用於資料庫、mq、redis以及其它一些遠端訪問的資源
@Service  定義該bean是一個業務邏輯
@Controller  定義該bean是一個控制頁面訪問層
--載入條件
@Order  配置載入順序
@ConditionalOnClass  該註解的引數對應的類必須存在,否則不解析該註解修飾的配置類;
@ConditionalOnMissingBean  該註解表示,如果存在它修飾的類的bean,則不需要再建立這個bean;可以給該註解傳入引數例如@ConditionOnMissingBean(name = "example"),這個表示如果name為“example”的bean存在,這該註解修飾的程式碼塊不執行。
@AutoConfigureAfter 在摸個自動裝載類之後裝載


基於屬性的註解:
@Value 載入配置屬性的值
@Autowired 自動注入bean
@Qualifier 當存在多個bean注入時,需要通過name進行過濾
@Resource 獲取當前jvm的resource 也類似依賴注入
@Inject 欄位注入bean


基於方法的註解:
@Bean  釋出一個返回object型別的bean,類似配置xml釋出一個bean
@PostConstruct 指定當類載入完成的時候就會執行該方法

四:mybatis整合

public class MybatisConfiguration {


    private static Log logger = LogFactory.getLog(MybatisConfiguration.class);


	//建立DataSource  
    @Bean
    public DataSource dataSource(Environment env) {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setPassword(env.getProperty("jdbc.password"));
        druidDataSource.setUsername(env.getProperty("jdbc.username"));
        druidDataSource.setUrl(env.getProperty("jdbc.url"));
        druidDataSource.setDriverClassName(env.getProperty("jdbc.driver"));
        druidDataSource.setMaxActive(Integer.parseInt(env.getProperty("jdbc.poolMaximumActiveConnections")));
        return druidDataSource;
    }


	//建立SqlSessionFactory  DataSource spring會自動匯入到引數
    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) {
        try {
            SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
            sessionFactory.setDataSource(dataSource);
			//set  map.xml的路徑
            sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
                    .getResources("classpath:mapping/*.xml"));
            return sessionFactory.getObject();
        } catch (Exception e) {
            logger.error("not install sessionFactory", e);
            throw new RuntimeException("not install sessionFactory");
        }
    }


	//建立事務  DataSource spring會自動匯入到引數
    @Bean
    public DataSourceTransactionManager transaction(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

結記:spring-boot是較好的微服務實現容器,減少開發除錯時間,並且非常好的對微服務支援,而微服務又需要利用微服務框架實現,微服務框架可以檢視本人另外一片文章詳細說明基於grpc的服務化框架