Java编程配置思路详解
Java編程配置思路詳解
SpringBoot雖然提供了很多優秀的starter幫助我們快速開發,可實際生產環境的特殊性,我們依然需要對默認整合配置做自定義操作,提高程序的可控性,雖然你配的不一定比官方提供的starter好。上周因為工作和裝修的事情,導致博客沒有正常更新,害怕停更會讓人懶惰起來,擠了一點時間寫了一篇內容比較簡單的文章。后面閑談一下我是如何從裝修小白到入門的經歷。
技術:Configuration,ComponentScan,PropertySource,EnableTransactionManagement,Bean,Value
說明:文中只貼出了配置代碼,完整的測試代碼在github上。
源碼:https://github.com/ITDragonBl...
文章目錄結構:
一、Java編程配置
在Spring4.x之前,應用的基本配置中一般使用xml配置的方式,而在業務邏輯中建議使用注解的方式。可在Spring4.x以后,官方便開始推薦使用Java的編程配置來代替xml配置,這又是為什么?這兩種配置又有什么優缺點呢?
Java編程配置和xml配置
xml配置優點:對于我們這些老一輩的程序員來說(┬_┬),xml非常親切,使用簡單,容易擴展,修改應用配置參數不需要重新編譯。
xml配置缺點:配置文件的讀取和解析需要耗時,xml配置文件內容太多會顯得很臃腫,不方便管理。
Java編程配置優點:相對于xml配置而言,其結構更清晰,可讀性更高,同時也節省了解析xml耗時。
Java編程配置缺點:修改應用配置參數需要重新編譯。其實并不是一個大的問題,實際生成環境中,應用配置完成后一般都不會也不敢去隨意修改。
兩者各有千秋,考慮到Spring4.x和SpringBoot都在推薦使用Java編程配置的方式,那我們也應該順應時代潮流,你可以不用,但你應該要懂!
Java編程配置步驟
第一步:創建配置類,在類名上添加注解Configuration,告知Spring這是一個配置類,其作用類似xml文件
第二步:加載外部配置文件,在類名上添加注解PropertySource,指定properties文件的讀取路徑
第三步:獲取應用配置屬性值,在屬性變量上添加注解Value,通過${}表達式獲取配置文件中參數
第四步:依賴注入,在方法上添加Bean注解,也可以用FactoryBean
第一步和第四步的語法,文章第二部分會詳細介紹。第二步和第三步的語法,文章第三部分會詳細介紹。
import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import com.mchange.v2.c3p0.ComboPooledDataSource;/*** Spring 配置類* 配置數據源,事務管理,bean,自動掃描包* @author itdragon*/ @Configuration // 聲明該類為配置類 @PropertySource({"classpath:propertySource.properties"}) // 引入外部文件 @ComponentScan("com.itdragon") // 配置自動掃描包的路徑 @EnableTransactionManagement // 開啟基于注解的事務管理功能 public class ApplicationContextConfig {@Value("${DATA_USER}")private String DATA_USER;@Value("${DATA_PAWD}")private String DATA_PAWD;@Value("${DATA_DRIVER}")private String DATA_DRIVER;@Value("${DATA_JDBC_URL}")private String DATA_JDBC_URL;@Bean // 數據源public DataSource dataSource() throws Exception{ComboPooledDataSource dataSource = new ComboPooledDataSource();dataSource.setUser(DATA_USER);dataSource.setPassword(DATA_PAWD);dataSource.setDriverClass(DATA_DRIVER);dataSource.setJdbcUrl(DATA_JDBC_URL);return dataSource;}@Bean // jdbc模板public JdbcTemplate jdbcTemplate() throws Exception{JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());return jdbcTemplate;}@Bean // 事務管理public PlatformTransactionManager transactionManager() throws Exception{return new DataSourceTransactionManager(dataSource());}}事務類
import java.util.List; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.itdragon.dao.ITDragonDao;@Service public class ITDragonServer {@Autowired(required=false)private ITDragonDao itdragonDao;public List<Map<String,Object>> findAll() {return itdragonDao.findAll();}@Transactionalpublic void updateNameById(String name, Long id) {itdragonDao.updateUserNameById(name, id);System.out.println(0/0); // 事務異常} }完整代碼,請異步github
二、組件注入
Bean注解類似xml文件中的<bean>標簽,其中被Bean注解修飾的方法名對應<bean>標簽中的id,也可以通過Bean注解的value屬性設置id的值。在SpringBoot底層代碼中被大量使用。
若希望容器啟動后創建對象,并在使用后從容器中直接獲取,則什么都不需要做,因為Spring默認是單實例,即容器啟動后創建對象,并保存到容器中,使用時再從容器中獲取。
若希望容器啟動后不創建對象,而是在使用時再創建,繼而保存到容器中,下次使用再從容器中獲取,可以通過懶加載的方式實現,即使用Lazy注解修飾。
若希望容器啟動后不創建對象,而是在每次使用時創建,則采用多實例的方式,即使用Scope注解,參數的值為prototype,即@Scope("prototype") 。
若希望容器啟動后根據條件選擇需要注入的Bean,可以使用注解Conditional判斷,SpringBoot的底層打量使用了該注解。
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Controller; import com.itdragon.entity.ITDragonEntity; import com.itdragon.server.ITDragonServer;/*** 知識點二:配置自動掃描包路徑* 一、注解ComponentScan的value值設置自動掃描包的路徑* 二、注解ComponentScan的excludeFilters值設置掃描排除的規則* 1)、通過注解@Filter設置排除的類型,type=ANNOTATION表示按照注解包含排除。classes是具體的注解,如Controller,Server,Repository* 三、注解ComponentScan的includeFilters值設置掃描加入的規則* 1)、通過設置useDefaultFilters=false關閉Spring默認掃描全部的功能,使includeFilters生效* * 知識點三:@Filter常用的攔截類型* 一、FilterType.ANNOTATION:按照注解* 二、FilterType.ASSIGNABLE_TYPE:按照給定的類型,包括其子類和實現類* 三、FilterType.CUSTOM:使用自定義規則* * 第一個ComponentScan注解表示在指定包下不掃描通過Controller注解修飾的類和ITDragonServer類及其子類和實現類* 第二個ComponentScan注解表示在指定包下只掃描通過Controller注解修飾的類* 第三個ComponentScan注解表示在指定包下根據自定義攔截規則,不掃描滿足規則的類*/ @Configuration @ComponentScan(value="com.itdragon",excludeFilters={@Filter(type=FilterType.ANNOTATION,classes={Controller.class}),@Filter(type=FilterType.ASSIGNABLE_TYPE,classes={ITDragonServer.class})}) //@ComponentScan(value="com.itdragon",includeFilters={@Filter(type=FilterType.ANNOTATION,classes={Controller.class})},useDefaultFilters=false) //@ComponentScan(value="com.itdragon",excludeFilters={@Filter(type=FilterType.CUSTOM,classes={ITDragonCustomTypeFilter.class})}) public class ITDragonConfig {/*** 知識點一:配置bean* 一、注解Bean的value值表示bean的id* 二、注解Bean的value值未設置,則方法名表示bean的id*/@Bean(value="itdragonBean")public ITDragonEntity itdragonEntity() { //方法名很重要,類似xml的id名,也可以通過@bean的value值重定義return new ITDragonEntity("itdragon", "configuration-password", 25);}/*** 知識點四:Scope屬性* @Scope,調整作用域,默認單實例* singleton:單實例:ioc容器啟動后創建對象放到ioc容器中,需要是從容器中獲取。* prototype:多實例:ioc容器啟動后每次獲取對象時都要創建對象。* request:同一次請求創建一個實例* session:同一個session創建一個實例* * 知識點五:懶加載* 針對單實例而言,在容器啟動后不創建對象,在第一次使用Bean時創建對象。可以理解為單實例的一種補充。* */ // @Scope("prototype")@Lazy@Beanpublic ITDragonEntity scopeTopicBean() {System.out.println("^^^^^^^^^^^^^^^^^^^^^Create Bean");return new ITDragonEntity("scopeBean", "singleton-prototype-request-session", 25);}/*** 知識點六:Conditional條件判斷* 滿足條件才會注冊bean,可以修飾在類上,管理整個類下的組件注入。*/@Bean@Conditional({ITDragonCustomCondition.class})public ITDragonEntity conditionalBean() {return new ITDragonEntity("conditionalBean", "Conditional-Condition-CustomCondition", 25);}/*** 知識點七:FactoryBean工廠Bean* FactoryBean默認通過調用getObject創建的對象,通過調用isSingleton設置單實例和多實例。*/@Beanpublic ITDragonFactoryBean itdragonFactoryBean() {return new ITDragonFactoryBean();} }自定義的條件判斷組件注入類
import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata;/*** 自定義的條件判斷組件注入* @author itdragon**/ public class ITDragonCustomCondition implements Condition{/*** 判斷注冊的bean中是否含有指定的bean*/public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {// 獲取bean的注冊類BeanDefinitionRegistry registry = context.getRegistry();return registry.containsBeanDefinition("scopeTopicBean"); // 有則加載conditionalBean}}自定義Bean的工廠類
import org.springframework.beans.factory.FactoryBean; import com.itdragon.entity.ITDragonEntity; /*** 自定義Bean的工廠類* @author itdragon**/ public class ITDragonFactoryBean implements FactoryBean<ITDragonEntity>{public ITDragonEntity getObject() throws Exception {System.out.println("^^^^^^^^^^^^^^^^^^^^^FactoryBean Create Bean");return new ITDragonEntity(); // 創建對象并返回到容器中}public Class<?> getObjectType() {return ITDragonEntity.class;}public boolean isSingleton() {return false; // 設置多實例,true則為單例}}三、屬性賦值
屬性賦值步驟:
第一步:通過注解PropertySource引入外部文件。可以引入多個,若擔心文件不存在,可以通過參數ignoreResourceNotFound設置忽略
第二步:通過注解Value從外部文件中獲取值,一般采用${}格式,還支持#{} SpEL表達式,也可以直接傳字符串。如果想接收一些復雜的值,比如集合,可以考慮使用注解ConfigurationProperties,后續會詳細介紹兩者的優缺點。
import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import com.itdragon.entity.ITDragonEntity;/*** 知識點一: 引入外部文件,并從文件中獲取值* @PropertySource 引入外部文件,支持多個,如果文件不存在會報錯,可以通過設置參數ignoreResourceNotFound=true忽略* @Value 從外部文件中獲取值,支持spel表達式,#{},${},string* @author itdragon*/ @Configuration @PropertySource(value={"classpath:propertySource.properties","classpth:xxx.properties"},ignoreResourceNotFound=true) public class ITDragonConfigValue {@Value("${ACCOUNT}") // 從配置文件獲取數據private String ACCOUNT;@Value("${PASSWORD}")private String PASSWORD;@Value("${AGE}")private Integer AGE;@Value("ITDragon") // 普通賦值private String str;@Value("#{(1+2-3)/4*5}") // 算術運算private String operator;@Value("#{1>2 || 2 <= 3}") // 關系運算private Boolean comparison;@Value("#{systemProperties['java.version']}") // 系統配置:os.nameprivate String systemProperties;@Value("#{T(java.lang.Math).abs(-18)}") // 表達式private String mapExpression;@Bean("valueBean")public ITDragonEntity itdragonEntity() {System.out.println("^^^^^^^^^^^^^^^^^^^^ str : " + str);System.out.println("^^^^^^^^^^^^^^^^^^^^ operator : " + operator);System.out.println("^^^^^^^^^^^^^^^^^^^^ comparison : " + comparison);System.out.println("^^^^^^^^^^^^^^^^^^^^ systemProperties : " + systemProperties);System.out.println("^^^^^^^^^^^^^^^^^^^^ mapExpression : " + mapExpression);return new ITDragonEntity(ACCOUNT, PASSWORD, AGE);}}四、閑談學習
這里并不是介紹如何學習一門技術,而是論養成一個學習習慣的重要性。大一時期,因為擔心找不到工作而報了一個線上培訓機構。經常被洗腦,其中一句話而我印象深刻 ---- "讓優秀成為一種習慣"。聽得我熱血沸騰。花了五六千大洋報名,后來才發現網上有免費的。。。。個人不建議參加培訓。
這錢花的還算值得,"讓優秀成為一種習慣",這句話對我的影響很大,從大學到工作,每當遇到陌生的知識,并沒有選擇逃避它。而是抽時間從網上找資料,去學習,整理,實踐直到弄懂它。可我萬萬沒有想到,這種學習的精神竟然用到了裝修上。。。。可能學習已經是我的一個習慣了吧!
開始,我是一個裝修小白,不知道什么是全包、半包、清包;不知道什么是硬裝、軟裝;也不知道裝修的流程;不知道水電線、櫥柜、潔具的品牌選擇,不知道掛機、柜機、風管機、中央空調的優缺點;不知道封陽臺的利弊;更不知道一個裝修效果圖要七八千。面對這些未知的領域,我寸步難行。我清楚的知道:如果你不懂,你就是砧板上的魚肉,任人宰割。
現在,我通過在百度,知乎,兔巴兔等平臺上找答案,并把內容用Markdown的格式整理!我都被自己嚇到了。不僅如此,我還在搶設計師的飯碗,自己動手設計效果圖。在制作效果圖的過程中,發現了很多不合理的設想。慶幸問自己設計了一套效果圖,不然又有很多無用功,耗時,耗力,耗錢。爸媽和女票就是客戶,而我就一直處于改!改!改!的階段。體驗了一把前端工程師的辛酸。
我是誰?我在哪?我在做什么?
我是一名程序員,我在學習的道路上,我在做能提高自己的事情!
總結
以上是生活随笔為你收集整理的Java编程配置思路详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 简单队列的实现
- 下一篇: Python Web 框架:Django