【SpringBoot】@Configration與@Bean的使用

oollXianluo發表於2020-11-07

一.前言

Spring Boot 推薦使用 java 配置完全代替 XML 配置,java 配置是通過 @Configration 和 @Bean 註解實現的。

  • @Configration:作用在上,宣告當前類是一個配置類,相當於 Spring 中的一個 XML配置檔案,可理解為用Spring的xml配置檔案裡的<beans>標籤。
  • @Bean:作用在方法上,宣告當前方法的返回值是一個 Bean相當於Spring的XML配置檔案中的<bean>標籤

二.使用Java配置方式基礎Shiro框架

這些xml配置和java程式碼大概瞄一下就行了,重點是要了解用xml配置檔案Java配置方式區別

例如: 在spring專案中我們整合第三方的框架如shiro會在spring.xml配置檔案中進行配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans" xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/util
    http://www.springframework.org/schema/util/spring-util.xsd">

    <!-- 提供shiro的相關配置,簡單的說,就是把shiro.ini裡的內容搬到這個xml檔案裡面來了,只是寫法不同-->

    <!--首先宣告自定義URL匹配過濾器 --> <!--TODO:新增-->
    <bean id="urlPathMatchingFilter" class="com.oyjp.filter.URLPathMatchingFilter"/>

    <!-- 配置shiro的過濾器工廠類,id- shiroFilter要和我們在web.xml中配置的過濾器一致 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!-- 呼叫我們配置的許可權管理器 -->
        <property name="securityManager" ref="securityManager"/>
        <!-- 配置我們的登入請求地址 -->
        <property name="loginUrl" value="/login"/>
        <!-- 如果您請求的資源不再您的許可權範圍,則跳轉到/403請求地址 -->
        <property name="unauthorizedUrl" value="/unauthorized"/>
        <!-- 退出 -->
        <property name="filters">
            <util:map>
                <entry key="logout" value-ref="logoutFilter"/>
                <entry key="url" value-ref="urlPathMatchingFilter" /> <!--TODO:新增-->
            </util:map>
        </property>
        <!-- 許可權配置 -->
        <property name="filterChainDefinitions">
            <value>
                <!-- anon表示此地址不需要任何許可權即可訪問 -->
                /login=anon
                /index=anon
                /static/**=anon
                <!-- 只對業務功能進行許可權管理,許可權配置本身不需要沒有做許可權要求,這樣做是為了不讓初學者混淆:大概的意思就是所有 /config的請求都不進行許可權校驗 -->
                /config/**=anon
                /doLogout=logout
                /404=anon
                /500=anon
                /favicon.ico=anon
                /unauthorized=anon
                <!--所有的請求(除去配置的靜態資源請求或請求地址為anon的請求)都要通過登入驗證,如果未登入則跳到/login -->
                <!--/** = authc-->
                <!--所有的請求(除去配置的靜態資源請求或請求地址為anon的請求)都要通過過濾器url TODO:新增 -->
                /** = url
            </value>
        </property>
    </bean>

    <!-- 退出過濾器 -->
    <bean id="logoutFilter" class="org.apache.shiro.web.filter.authc.LogoutFilter">
        <property name="redirectUrl" value="/index"/>
    </bean>

    <!-- 會話ID生成器 -->
    <bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>
    <!-- 會話Cookie模板 關閉瀏覽器立即失效 -->
    <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
        <constructor-arg value="sid"/>
        <property name="httpOnly" value="true"/>
        <property name="maxAge" value="-1"/>
    </bean>
    <!-- 會話DAO -->
    <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
        <property name="sessionIdGenerator" ref="sessionIdGenerator"/>
    </bean>
    <!-- 會話驗證排程器,每30分鐘執行一次驗證 ,設定會話超時及儲存 -->
    <bean name="sessionValidationScheduler"
          class="org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler">
        <property name="interval" value="1800000"/>
        <property name="sessionManager" ref="sessionManager"/>
    </bean>
    <!-- 會話管理器 -->
    <bean id="sessionManager"  class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        <!-- 全域性會話超時時間(單位毫秒),預設30分鐘 -->
        <property name="globalSessionTimeout" value="1800000"/>
        <property name="deleteInvalidSessions" value="true"/>
        <property name="sessionValidationSchedulerEnabled" value="true"/>
        <property name="sessionValidationScheduler" ref="sessionValidationScheduler"/>
        <property name="sessionDAO" ref="sessionDAO"/>
        <property name="sessionIdCookieEnabled" value="true"/>
        <property name="sessionIdCookie" ref="sessionIdCookie"/>
        <property name="sessionIdUrlRewritingEnabled" value="false" /> <!--去掉URL中的JSESSIONID-->
    </bean>

    <!-- 安全管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="databaseRealm"/>
        <property name="sessionManager" ref="sessionManager"/>
    </bean>

    <!-- 相當於呼叫SecurityUtils.setSecurityManager(securityManager) -->
    <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
        <property name="arguments" ref="securityManager"/>
    </bean>

    <!-- 密碼匹配器 -->
    <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
        <property name="hashAlgorithmName" value="md5"/> <!--加密演算法-->
        <property name="hashIterations" value="2"/> <!--加密次數-->
        <property name="storedCredentialsHexEncoded" value="true"/>
    </bean>

    <!-- DatabaseRealm 真正做登入驗證和授權的地方-->
    <bean id="databaseRealm" class="com.oyjp.realm.DatabaseRealm">
        <property name="credentialsMatcher" ref="credentialsMatcher"/>
    </bean>

    <!-- 保證實現了Shiro內部lifecycle函式的bean執行 -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
</beans>

使用@Configration+@Bean可以將原本在xml中的配置,轉移到Java程式碼中進行配置,被
@Configration修飾的類稱為"配置類"

  • 使用 @Configration 註解將該類ShiroConfiguration 宣告為一個配置類。
  • 在方法上新增@Bean註解則會往Spring 容器預設新增一個名為方法名首字母小寫的Bean,該 Bean 即為方法的返回值。
@Configuration
public class ShiroConfiguration {
    /**
     * ShiroFilterFactoryBean 處理攔截資原始檔問題。
     * 注意:單獨一個ShiroFilterFactoryBean配置是或報錯的,因為在
     * 初始化ShiroFilterFactoryBean的時候需要注入:SecurityManager
     * <p>
     * Filter Chain定義說明
     * 1、一個URL可以配置多個Filter,使用逗號分隔
     * 2、當設定多個過濾器時,全部驗證通過,才視為通過
     * 3、部分過濾器可指定引數,如perms,roles
     */
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        System.out.println("ShiroConfiguration.shirFilter()");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        // 必須設定 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 如果不設定預設會自動尋找Web工程根目錄下的"/login.jsp"頁面
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登入成功後要跳轉的連結
        shiroFilterFactoryBean.setSuccessUrl("/index");
        //未授權介面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");

        //攔截器.
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        //自定義攔截器
        Map<String, Filter> customisedFilter = new HashMap<>();
        customisedFilter.put("url", getUrlPathMatchingFilter());
        //配置對映關係
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/index", "anon");
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/config/**", "anon");
        filterChainDefinitionMap.put("/doLogout", "logout");
        filterChainDefinitionMap.put("/**", "url");
        shiroFilterFactoryBean.setFilters(customisedFilter);
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;
    }
    /**
     * 用於例項化自定義URL匹配過濾器
     * URLPathMatchingFilter 並沒有用@Bean管理起來。 原因是Shiro的bug, 這個也是過濾器,ShiroFilterFactoryBean 也是過濾器,當他們都出現的時候,預設的什麼anno,authc,logout過濾器就失效了。所以不能把他宣告為@Bean。
     */
    public UrlPathMatchingFilter getUrlPathMatchingFilter() {
        return new UrlPathMatchingFilter();
    }
    /**
     * 例項化安全管理器並設定DatabaseRealm(真正做登入驗證和授權的地方)
     */
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //設定realm.
        securityManager.setRealm(getDatabaseRealm());
        return securityManager;
    }
    @Bean
    public DatabaseRealm getDatabaseRealm() {
        DatabaseRealm myShiroRealm = new DatabaseRealm();
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myShiroRealm;
    }
    /**
     * 例項化憑證匹配器
     * (由於我們的密碼校驗交給Shiro的SimpleAuthenticationInfo進行處理了所以我們需要修改下doGetAuthenticationInfo中的程式碼; )
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        //雜湊演算法:這裡使用MD5演算法;
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        //雜湊的次數,比如雜湊兩次,相當於 md5(md5(""));
        hashedCredentialsMatcher.setHashIterations(2);
        return hashedCredentialsMatcher;
    }
    /**
     * 開啟shiro aop註解支援.
     * 使用代理方式;所以需要開啟程式碼支援;
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
    /**
     * 例項化LifecycleBeanPostProcessor  保證實現了Shiro內部lifecycle函式的bean執行
     */
    @Bean
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
} 

三.@Configuration

3.1.@Configuration作用

  • @Configuration底層是含有@Component,所以@Configuration 具有和 @Component的作用。
  • @Configuration 用於定義配置類,可理解為Spring的xml配置檔案裡面的<beans>標籤。
  • @Configration 標註的類不能是final 型別
  • @Configration 標註類中可以宣告一個或多個 @Bean方法
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    String value() default "";
}

@Configuration標註在類上,相當於把該類作為spring的xml配置檔案中的<beans>

作用為配置spring容器(應用上下文)

@Configuration
public class TestConfiguration {
    public TestConfiguration() {
        System.out.println("TestConfiguration容器啟動初始化。。。");
    }
}

相當於

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc"  
    xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:util="http://www.springframework.org/schema/util" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd
        http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd" default-lazy-init="false">

</beans>

3.2.@Configuration使用

@Configration標註的類中可以宣告多個@Bean方法,並且 Bean 與 Bean 之間是可以有依賴關係的
如果一個 bean 的依賴其他 bean,可以注入方式有:

  1. 直接呼叫對配置類中依賴Bean的方法
  2. 形參上使用@Qualifier("beanName")通過Bean的名字注入例項
  3. 形參上使用@Autowired通過Bean的型別注入例項

相關Bean

@Data
public class TestBean {
    private String username;
    private String url;
    private String password;
}
@Data
public class TestBean2{
	//省略類結構,和TestBean一樣
}
@Data
public class TestBean3{
	//省略類結構,和TestBean一樣
}
@Data
public class TestBean4{
	//省略類結構,和TestBean一樣
}
@Configuration
public class TestConfiguration {
@Bean
    public TestBean testBean() {
        TestBean testBean = new TestBean();
        testBean.setUsername("admin");
        testBean.setPassword("123456");
        testBean.setUrl("www.testBean.com");
        return testBean;
    }
    /**
     * 使用@Qualifier通過Bean的名字注入例項
     */
    @Bean
    public TestBean2 testBean2(@Qualifier("testBean") TestBean testBean) {
        TestBean2 testBean2 = new TestBean2();
        testBean2.setUsername(testBean.getUsername());
        testBean2.setPassword(testBean.getPassword());
        testBean2.setUrl("www.TestBean2.com");
        return testBean2;
    }
    /**
     * 使用@Autowired通過Bean的型別注入例項
     */
    @Bean
    public TestBean3 testBean3(@Autowired TestBean2 testBean2) {
        TestBean3 testBean3 = new TestBean3();
        testBean3.setUsername(testBean2.getUsername());
        testBean3.setPassword(testBean2.getPassword());
        testBean3.setUrl("www.TestBean3.com");
        return testBean3;
    }
    /**
     * 呼叫Bean的方法獲取依賴Bean
     */
    @Bean
    public TestBean4 testBean4() {
        TestBean testBean = testBean();//獲取TestBean例項

        TestBean4 testBean4 = new TestBean4();
        testBean4.setUsername(testBean.getUsername());
        testBean4.setPassword(testBean.getPassword());
        testBean4.setUrl("www.testBean4.com");
        return testBean4;
    }
}

四.@Bean

4.1.@Bean作用

@Bean標註在方法上(返回某個例項的方法),等價於spring的xml配置檔案中的<bean>

作用為註冊bean物件

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
    @AliasFor("name")
    String[] value() default {};

    @AliasFor("value")
    String[] name() default {};

    /** @deprecated */
    @Deprecated
    Autowire autowire() default Autowire.NO;

    boolean autowireCandidate() default true;

    String initMethod() default "";

    String destroyMethod() default "(inferred)";
}
  • name: 指定bean的名字,並且可以接受一個陣列,配置多個name,預設採用的是 "方法名" + "首字母小寫"的命名方式
  • initMethod: 初始化Bean時執行的方法名
  • destoryMethod: 銷燬Bean時執行的方法名

或者使用通過@PostConstruct 和 @PreDestroy 方法 實現初始化和銷燬bean之前進行的操作 。

配置類

@Configuration
public class TestConfiguration {
    public TestConfiguration() {
        System.out.println("TestConfiguration容器啟動初始化。。。");
    }

    // @Bean註解註冊bean,同時可以指定初始化和銷燬方法
    @Bean(initMethod = "init",destroyMethod = "destroy")
    @Scope("prototype")//每次從Spring容器獲取都會新建一個TestBean 物件
    public TestBean testBean() {
        TestBean testBean = new TestBean();
        testBean.setUsername("admin");
        testBean.setPassword("123456");
        testBean.setUrl("www.baidu.com");
        return testBean;
    }
}

要被註冊到Spring容器的Bean

@Data
public class TestBean {
    private String username;
    private String url;
    private String password;
    //Bean初始化方法
    public void init() {
        System.out.println("TestBean 初始化。。。");
    }
    //Bean銷燬方法
    public void destroy() {
        System.out.println("TestBean 銷燬。。。");
    }
}

上述操作相當於例項化TestBean ,並交給Spring容器管理

注:

  1. @Bean標註的方法,如果沒有指定bean的名稱,預設採用的是 “方法名” + "首字母小寫"的命名方式
  2. @Bean註解預設作用域為單例singleton作用域,可通過@Scope("prototype")設定為原型作用域;
  3. @Bean的作用是註冊bean物件,我們也可以使用@Component、@Controller、@Service、@Repository等註解註冊bean(在需要註冊的類上加註解),然後配置@ComponentScan註解進行自動掃描。

4.2.指定@Bean別名

Bean 名稱
預設情況下 Bean 名稱就是方法名,比如下面 Bean 名稱便是 myBean

@Bean
public MyBean myBean() {
    return new MyBean();
}

@Bean 註解支援設定別名。

@Bean("myBean")
public MyBean myBean() {
    return new MyBean();
}

@Bean 註解支援設定多個別名。

@Bean({"myBean1","myBean2"})
public MyBean myBean() {
    return new MyBean();
}

4.3.@Bean 與其他註解一起使用

@Bean 註解常與 @Scope、@Lazy,@DependsOn 和 @Primary註解一起使用:

  • @Profile :可以在不同環境載入不同的Bean。如: 開發環境和生產環境載入不同的資料來源Bean
  • @Scope :將 Bean 的作用域從單例改變為指定的作用域

    @Scope("prototype ") :每次獲取 Bean 的時候會有一個新的例項

  • @Lazy :只有在預設單例作用域的情況下才有實際效果
  • @DependsOn :在當前 Bean 建立之前需要先建立其他 Bean,可以控制Bean的載入順序,
  • @Primayr: 當一種型別的Bean,可能會有幾種不同的實現類,可以使用@Primary,讓Sping容器預設注入某一個例項

4.3.@Bean初始化和銷燬的回撥

通過 @Bean 註解的initMethoddestrodMethod數學可以Bean 在初始化和銷燬時會呼叫哪個方法

public class MyBean {
    public void init() {
        System.out.println("MyBean開始初始化...");
    }
    public void destroy() {
        System.out.println("MyBean銷燬...");
    }
}
@Bean(initMethod="init", destroyMethod="destroy")
public MyBean myBean() {
    return new MyBean();
}

Spring Boot不是Spring的加強版,所以@Configuration和@Bean同樣可以用在普通的spring專案中。

相關文章