spring-boot 深入學習
阿新 • • 發佈:2018-12-30
--在使用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的服務化框架