Java編程配置思路詳解

Java編程配置思路詳解

SpringBoot雖然提供了不少優秀的starter幫助咱們快速開發,可實際生產環境的特殊性,咱們依然須要對默認整合配置作自定義操做,提升程序的可控性,雖然你配的不必定比官方提供的starter好。上週由於工做和裝修的事情,致使博客沒有正常更新,懼怕停更會讓人懶惰起來,擠了一點時間寫了一篇內容比較簡單的文章。後面閒談一下我是如何從裝修小白到入門的經歷。前端

技術:Configuration,ComponentScan,PropertySource,EnableTransactionManagement,Bean,Value
說明:文中只貼出了配置代碼,完整的測試代碼在github上。
源碼:https://github.com/ITDragonBlog/daydayup/tree/master/Spring/itdragon-spring-anno
文章目錄結構:java

文章目錄結構

1、Java編程配置

在Spring4.x以前,應用的基本配置中通常使用xml配置的方式,而在業務邏輯中建議使用註解的方式。可在Spring4.x之後,官方便開始推薦使用Java的編程配置來代替xml配置,這又是爲何?這兩種配置又有什麼優缺點呢?git

Java編程配置和xml配置

xml配置優勢:對於咱們這些老一輩的程序員來講(┬_┬),xml很是親切,使用簡單,容易擴展,修改應用配置參數不須要從新編譯。程序員

xml配置缺點:配置文件的讀取和解析須要耗時,xml配置文件內容太多會顯得很臃腫,不方便管理。github

Java編程配置優勢:相對於xml配置而言,其結構更清晰,可讀性更高,同時也節省了解析xml耗時。spring

Java編程配置缺點:修改應用配置參數須要從新編譯。其實並非一個大的問題,實際生成環境中,應用配置完成後通常都不會也不敢去隨意修改。sql

二者各有千秋,考慮到Spring4.x和SpringBoot都在推薦使用Java編程配置的方式,那咱們也應該順應時代潮流,你能夠不用,但你應該要懂!編程

Java編程配置步驟

第一步:建立配置類,在類名上添加註解Configuration,告知Spring這是一個配置類,其做用相似xml文件session

第二步:加載外部配置文件,在類名上添加註解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();
    }
    
    @Transactional
    public void updateNameById(String name, Long id) {
        itdragonDao.updateUserNameById(name, id);
        System.out.println(0/0); // 事務異常
    }
}

完整代碼,請異步github

2、組件注入

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;

/**
 * 知識點二:配置自動掃描包路徑
 * 1、註解ComponentScan的value值設置自動掃描包的路徑
 * 2、註解ComponentScan的excludeFilters值設置掃描排除的規則
 *  1)、經過註解@Filter設置排除的類型,type=ANNOTATION表示按照註解包含排除。classes是具體的註解,如Controller,Server,Repository
 * 3、註解ComponentScan的includeFilters值設置掃描加入的規則
 *  1)、經過設置useDefaultFilters=false關閉Spring默認掃描所有的功能,使includeFilters生效
 *  
 * 知識點三:@Filter經常使用的攔截類型
 * 1、FilterType.ANNOTATION:按照註解
 * 2、FilterType.ASSIGNABLE_TYPE:按照給定的類型,包括其子類和實現類
 * 3、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
     * 1、註解Bean的value值表示bean的id
     * 2、註解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
    @Bean
    public 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設置單實例和多實例。
     */
    @Bean
    public 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則爲單例
    }

}

3、屬性賦值

屬性賦值步驟:

第一步:經過註解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.name
    private 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);
    }

}

4、閒談學習

這裏並非介紹如何學習一門技術,而是論養成一個學習習慣的重要性。大一時期,由於擔憂找不到工做而報了一個線上培訓機構。常常被洗腦,其中一句話而我印象深入 ---- "讓優秀成爲一種習慣"。聽得我熱血沸騰。花了五六千大洋報名,後來才發現網上有免費的。。。。我的不建議參加培訓

這錢花的還算值得,"讓優秀成爲一種習慣",這句話對個人影響很大,從大學到工做,每當遇到陌生的知識,並無選擇逃避它。而是抽時間從網上找資料,去學習,整理,實踐直到弄懂它。可我萬萬沒有想到,這種學習的精神居然用到了裝修上。。。。可能學習已是個人一個習慣了吧

開始,我是一個裝修小白,不知道什麼是全包、半包、清包;不知道什麼是硬裝、軟裝;也不知道裝修的流程;不知道水電線、櫥櫃、潔具的品牌選擇,不知道掛機、櫃機、風管機、中央空調的優缺點;不知道封陽臺的利弊;更不知道一個裝修效果圖要七八千。面對這些未知的領域,我步履維艱。我清楚的知道:若是你不懂,你就是砧板上的魚肉,任人宰割。

如今,我經過在百度,知乎,兔巴兔等平臺上找答案,並把內容用Markdown的格式整理!我都被本身嚇到了。不只如此,我還在搶設計師的飯碗,本身動手設計效果圖。在製做效果圖的過程當中,發現了不少不合理的設想。好比以前打算在主臥的左上角放一個電腦桌,牆上打幾個小格子。可在實際的效果圖中,和牀的位置有了衝突,從新改了方案。否則又是一個無用功,不只耗時,耗力,還耗錢。爸媽和女票就是客戶,而我就一直處於改!改!改!的階段。體驗了一把前端工程師的辛酸。(往左邊移一點,往右邊移一點,移多了,換個顏色試試,o(≧口≦)o)

我是誰?我在哪?我在作什麼?

我是一名程序員,我在學習的道路上,我在作能提升本身的事情!

相關文章
相關標籤/搜索