spring boot基於Java的容器配置

spring容器是負責實例化、配置、組裝組件的容器。java

容器的配置有不少,經常使用的是xml、Java註解和Java代碼。mysql

在spring中Ioc容器相關部分是context和beans中。其中context-support保存着許多線程的容器實現。好比AnnotationConfigApplicationContext或者ClassPathXmlApplicationContext。二者只有接收的目標不一樣,前者接收Java類後者接收Xml文件。但做爲spring容器的不一樣實現異曲同工。git

下面我經過spring文檔中的介紹來本身過一遍容器配置,來加深印象。這裏我使用的是springboot。因此有些基於web.xml的配置不會涉及到。web

歡迎捉蟲spring

@Bean和@Configuration

@Configuration註解的類,表示這個類是一個配置類,相似於<beans></beans>或者.xml文件。sql

@Bean註解用來講明使用springIoc容器管理一個新對象的實例化、配置和初始化。相似於<bean></bean>,默認狀況下,bean名稱就是方法名稱.編程

例子:數組

@Configuration
public class Conf {
    
    @Bean
    public HelloService helloService() {
        return new HelloServiceImpl();
    }
    
}
複製代碼

這種配置方式就相似於xml配置中的springboot

<beans>
    <bean id="helloService" class="com.dust.service.impl.HelloServiceImpl" />
</beans>
複製代碼

等價於註解配置中的bash

@Service
public class HelloServiceIMpl implements HelloService {
    
    @Override
    public String hello() {
        return "hello world";
    }
    
}
複製代碼

使用AnnotationConfigApplicationContext實例化Spring容器

這是在spring3.0加入的功能,除了接收@Configuration註解的類做爲輸入類以外還能夠接受使用JSR-330元數據註解的簡單類和@Component類。

當@Configuration註解的類做爲輸入時,@Configuration類自己會被註冊爲一個bean,在這個類中全部用@Bean註解的方法都會被定義爲一個bean。

具體有哪些類型的bean能夠方法遍歷打印容器中的bean。

public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(Conf.class);
        HelloService helloService = context.getBean(HelloService.class);
        String hello = helloService.hello();
        System.out.println(hello);
    }
複製代碼

該實例的步驟爲:

1. 建立AnnotationConfigApplicationContext容器對象,同時將@Configuration註解的Conf.class做爲參數傳入。
2. 容器回根據傳入的Conf類來構建bean。其中就有helloService
3. 經過bean的對象類型獲取到容器中保管的對象。
4. 執行對象方法
複製代碼

可是AnnotationConfigApplicationContext並不只使用@Configuration類。任何@Component或JSR-330註解的類均可以做爲輸入提供給構造函數。例如:

public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(HelloServiceImpl.class, A.class, B.class);
        HelloService helloService = context.getBean(HelloService.class);
        String hello = helloService.hello();
        System.out.println(hello);
    }
複製代碼

上面假設MyServiceImpl、A和B都用了Spring的依賴注入的註解,例如@Autowired。

使用register(Class<?>…)的方式構建容器

也可使用無參構造函數實例化AnnotationConfigApplicationContext,而後使用register()方法配置。當使用編程方式構建AnnotationConfigApplicationContext時,這種方法特別有用。

例子:

public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(Conf.class);
        context.refresh();
        HelloService helloService = context.getBean(HelloService.class);
        String hello = helloService.hello();
        System.out.println(hello);
    }
複製代碼

其中的refresh方法是一個初始化工做。不然註冊的類並不會被生成bean。

使用scan(String …)組件掃描

組件掃描,只須要設置好對應包路徑,spring容器回自動掃描包下面全部可以被容器初始化的Java類。

使用註解:

@Configuration
@ComponentScan("com.example.springdemo.beans")
public class Conf {

    @Bean
    public HelloService helloService() {
        //用這種方法建立的service至關於用@Service註解標註
        return new HelloServiceImpl();
    }

}
複製代碼

在該路徑下還有一個配置文件:

@Configuration
public class Conf2 {

    @Bean
    public ByeService byeService() {
        //用這種方法建立的service至關於用@Service註解標註
        return new ByeServiceImpl();
    }

}
複製代碼

而後是初始化容器:

public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(Conf.class);
        context.refresh();
        ByeService byeService = context.getBean(ByeService.class);
        String hello = byeService.bye();
        System.out.println(hello);
    }

複製代碼

能夠看到,雖然傳入的是Conf類,可是因爲包掃描機制,該容器同時建立了Conf2類中的bean。

這就相似xml配置中的:

<beans>
    <context:component-scan base-package="com.example.springdemo.beans"/>
</beans>
複製代碼

還能夠直接調用容器的掃描方法

public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// context.register(Conf.class);
        context.scan("com.example.springdemo.beans");
        context.refresh();
        ByeService byeService = context.getBean(ByeService.class);
        String hello = byeService.bye();
        System.out.println(hello);
    }
複製代碼

springboot中的包掃描

springboot經過main方法啓動,其中的註解爲@SpringBootApplication。經過查看該註解的代碼能夠發現一下代碼段:

@AliasFor(
    annotation = ComponentScan.class,
    attribute = "basePackages"
)
複製代碼

由此能夠知道@SpringBootApplication註解包括了包掃描註解,同時掃描的是該類的目錄以及子目錄的全部能夠被spring容器初始化的類

AnnotationConfigWebApplicationContext對於web應用的支持

AnnotationConfigApplicationContext在WebApplicationContext中的變體爲 AnnotationConfigWebApplicationContext。當配置Spring ContextLoaderListener servlet 監聽器、Spring MVC DispatcherServlet的時候,能夠用此實現。

Bean依賴

@Bean註解方法能夠具備描述構建該bean所需依賴關係的任意數量的參數。依賴的必須也是Ioc容器中註冊的bean。

將上面的代碼修改後以下:

@Configuration
public class Conf {

    @Bean
    public HelloService helloService(ByeService byeService) {
        return new HelloServiceImpl(byeService);
    }

    @Bean
    public ByeService byeService() {
        return new ByeServiceImpl();
    }

}
複製代碼
public class HelloServiceImpl implements HelloService {

    private ByeService byeService;

    public HelloServiceImpl(ByeService byeService) {
        this.byeService = byeService;
    }
    
    @Override
    public String hello() {
        return "hello world" + byeService.bye();
    }
    
}
複製代碼
public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(Conf.class);
        context.refresh();
        HelloService helloService = context.getBean(HelloService.class);
        String hello = helloService.hello();
        System.out.println(hello);
        ByeService byeService = context.getBean(ByeService.class);
        String bye = byeService.bye();
        System.out.println(bye);
    }
複製代碼

輸出結果:

hello worldGoodbye!

Goodbye!

這種解決原理和基於構造函數的依賴注入幾乎相同。

生命週期回調

@Bean註解支持任意的初始化和銷燬回調方法,這與Spring XML 中bean元素上的init方法和destroy-method屬性很是類似:

@Bean(initMethod = "init")
    public HelloService helloService(ByeService byeService) {
        return new HelloServiceImpl(byeService);
    }

    @Bean(destroyMethod = "destroy")
    public ByeService byeService() {
        return new ByeServiceImpl();
    }
複製代碼
public interface ByeService {

    String bye();

    void destroy();

}
複製代碼
public interface HelloService {

    String hello();

    void init();
}
複製代碼
public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(Conf.class);
        context.refresh();
        context.close();
    }
複製代碼

輸出以下:

init helloService!!

destroy byeService!

默認狀況下,Ioc容器關閉後全部bean都會被銷燬,可是若是要引入一個生命週期在應用程序以外進行管理的組件,例如:DataSource。那麼只須要將@Bean(destroyMethod =」」)添加到你的bean定義中便可禁用默認(推測)模式。

@Bean(destroyMethod="")
public DataSource dataSource() throws NamingException {
    return (DataSource) jndiTemplate.lookup("MyDS");
}
複製代碼

固然,初始化的時候也能夠先執行對應方法,而不用交給Ioc容器

@Bean
    public HelloService helloService(ByeService byeService) {
        HelloService helloService = new HelloServiceImpl(byeService);
        helloService.init();
        return helloService;
    }
複製代碼

@Scope和scope 代理

Scope描述的是Spring容器如何新建Bean實例的。

  1. Singleton:一個Spring容器中只有一個Bean的實例,此爲Spring的默認配置,全容器共享一個實例。
  2. Prototype:每次調用新建一個Bean實例。
  3. Request:Web項目中,給每個 http request 新建一個Bean實例。
  4. Session:Web項目中,給每個 http session 新建一個Bean實例。
  5. GlobalSession:這個只在portal應用中有用,給每個 global http session 新建一個Bean實例。
@Bean
    //每次調用就建立一個新的bean
    @Scope("prototype")
    public UserInfo userInfo() {
        return new UserInfo();
    }

    @Bean
    public UserService userService() {
        UserService userService = new UserServiceImpl();
        userService.init(userInfo());
        return userService;
    }
複製代碼

測試代碼:

public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(Conf.class);
        context.refresh();
        UserService userService = context.getBean(UserService.class);
        UserService userService2 = context.getBean(UserService.class);
        UserInfo userInfo = context.getBean(UserInfo.class);
        UserInfo userInfo2 = context.getBean(UserInfo.class);
        System.out.println(userService == userService2);
        System.out.println(userInfo == userInfo2);
    }
複製代碼

輸出:

true

false

自定義Bean命名

一般,bean的名稱是bean的方法名,可是能夠經過name屬性重命名。有時一個單一的bean須要給出多個名稱,稱爲bean別名。爲了實現這個目標,@Bean註解的name屬性接受一個String數組。

@Bean(name = {"user", "userService", "User"})
    public UserService userService() {
        UserService userService = new UserServiceImpl();
        userService.init(userInfo());
        return userService;
    }
複製代碼
public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(Conf.class);
        context.refresh();
        Object user = context.getBean("user");
        Object userService = context.getBean("userService");
        Object User = context.getBean("User");

        System.out.println(user == userService);
        System.out.println(user == User);
        System.out.println(userService == User);
    }
複製代碼

輸出:

true

true

true

Bean描述

有時候須要提供一個詳細的bean描述文本是很是有用的。當對bean暴露(可能經過JMX)進行監控使,特別有用。可使用@Description註解對Bean添加描述:

@Bean(name = {"user", "userService", "User"})
    @Description("這是用戶服務對象")
    public UserService userService() {
        UserService userService = new UserServiceImpl();
        userService.init(userInfo());
        return userService;
    }
複製代碼
public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(Conf.class);
        context.refresh();
        String description = context.getBeanDefinition("user").getDescription();
        System.out.println(description);
    }
複製代碼

輸出:

這是用戶服務對象

基於Java組合配置

使用@Import註解

和Spring XML文件中使用元素來幫助模塊化配置相似,@Import註解容許從另外一個配置類加載@Bean定義:

@Configuration
@Import(UserConf.class)
public class Conf {

    @Bean(initMethod = "init")
    public HelloService helloService(ByeService byeService) {
        //用這種方法建立的service至關於用@Service註解標註
        return new HelloServiceImpl(byeService);
    }

    @Bean(destroyMethod = "destroy")
    public ByeService byeService() {
        return new ByeServiceImpl();
    }

}
複製代碼
@Configuration
public class UserConf {

    @Bean
    //每次調用就建立一個新的bean
    @Scope("prototype")
    public UserInfo userInfo() {
        return new UserInfo();
    }

    @Bean(name = {"user", "userService", "User"})
    @Description("這是用戶服務對象")
    public UserService userService() {
        UserService userService = new UserServiceImpl();
        userService.init(userInfo());
        return userService;
    }

}
複製代碼
public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(Conf.class);
        context.refresh();
        String description = context.getBeanDefinition("user").getDescription();
        System.out.println(description);
    }
複製代碼

這種方法簡化了容器實例化,由於只須要處理一個類,而不是須要開發人員在構建期間記住大量的@Configuration註解類。

Java and XML 混合配置

Java配置並不能100%替代xml配置,所以Ioc容器支持二者混合配置。不過這裏有個區別就是以xml爲中心仍是以Java配置爲中心。

以XML爲中心

@Configuration
public class DataSourceConf {

    @Autowired
    private DataSource dataSource;

    @Bean
    public DataSourceService dataSource() {
        return new DataSourceerviceImpl(dataSource);
    }

}
複製代碼
jdbc.url=jdbc:mysql://39.108.119.174:3306/dust
jdbc.username=root
jdbc.password=123456
複製代碼
<beans>

    <context:annotation-config/>

    <context:property-placeholder location="classpath:jdbc.properties"/>

    <bean class="com.example.springdemo.beans.DataSourceConf"/>

    <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

</beans>
複製代碼
public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/datasource.xml");
        DataSourceService dataSourceService = context.getBean(DataSourceService.class);
        System.out.println(dataSourceService.toString());
    }
複製代碼

以Java類爲中心

<beans>
    <context:property-placeholder location="classpath:jdbc.properties"/>
</beans>
複製代碼
@Configuration
@ImportResource("classpath:spring/datasource.xml")
public class DataSourceConf {

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSourceService dataSource() {
        return new DataSourceerviceImpl(url, username, password);
    }

}
複製代碼
public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.scan("com.example.springdemo.beans");
        context.refresh();
        DataSourceService dataSourceService = context.getBean(DataSourceService.class);
        System.out.println(dataSourceService.toString());

// ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/datasource.xml");
// DataSourceService dataSourceService = context.getBean(DataSourceService.class);
// System.out.println(dataSourceService.toString());
    }
複製代碼
相關文章
相關標籤/搜索