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());
    }
复制代码
相关文章
相关标签/搜索