经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring Boot » 查看文章
spring boot基于Java的容器配置讲解
来源:jb51  时间:2019/4/3 16:03:10  对本文有异议

spring容器是负责实例化、配置、组装组件的容器。

容器的配置有很多,常用的是xml、Java注解和Java代码。

在spring中Ioc容器相关部分是context和beans中。其中context-support保存着许多线程的容器实现。比如AnnotationConfigApplicationContext或者ClassPathXmlApplicationContext。两者只有接收的目标不同,前者接收Java类后者接收Xml文件。但作为spring容器的不同实现殊途同归。

下面我通过spring文档中的介绍来自己过一遍容器配置,来加深印象。这里我使用的是springboot。所以有些基于web.xml的配置不会涉及到。

@Bean和@Configuration

@Configuration注解的类,表示这个类是一个配置类,类似于<beans></beans>或者.xml文件。

@Bean注解用来说明使用springIoc容器管理一个新对象的实例化、配置和初始化。类似于<bean></bean>,默认情况下,bean名称就是方法名称.

例子:

  1. @Configuration
  2. public class Conf {
  3. @Bean
  4. public HelloService helloService() {
  5. return new HelloServiceImpl();
  6. }
  7. }
  8.  

这种配置方式就类似于xml配置中的

  1. <beans>
  2. <bean id="helloService" class="com.dust.service.impl.HelloServiceImpl" />
  3. </beans>
  4.  

等价于注解配置中的

  1. @Service
  2. public class HelloServiceIMpl implements HelloService {
  3. @Override
  4. public String hello() {
  5. return "hello world";
  6. }
  7. }
  8.  

使用AnnotationConfigApplicationContext实例化Spring容器

这是在spring3.0加入的功能,除了接收@Configuration注解的类作为输入类之外还可以接受使用JSR-330元数据注解的简单类和@Component类。

当@Configuration注解的类作为输入时,@Configuration类本身会被注册为一个bean,在这个类中所有用@Bean注解的方法都会被定义为一个bean。

具体有哪些类型的bean可以方法遍历打印容器中的bean。

  1. public static void main(String[] args) {
  2. ApplicationContext context = new AnnotationConfigApplicationContext(Conf.class);
  3. HelloService helloService = context.getBean(HelloService.class);
  4. String hello = helloService.hello();
  5. System.out.println(hello);
  6. }
  7.  

该实例的步骤为:

1. 创建AnnotationConfigApplicationContext容器对象,同时将@Configuration注解的Conf.class作为参数传入。
2. 容器回根据传入的Conf类来构建bean。其中就有helloService
3. 通过bean的对象类型获取到容器中保管的对象。
4. 执行对象方法

但是AnnotationConfigApplicationContext并不仅使用@Configuration类。任何@Component或JSR-330注解的类都可以作为输入提供给构造函数。例如:

  1. public static void main(String[] args) {
  2. ApplicationContext context = new AnnotationConfigApplicationContext(HelloServiceImpl.class, A.class, B.class);
  3. HelloService helloService = context.getBean(HelloService.class);
  4. String hello = helloService.hello();
  5. System.out.println(hello);
  6. }
  7.  

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

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

也可以使用无参构造函数实例化AnnotationConfigApplicationContext,然后使用register()方法配置。当使用编程方式构建AnnotationConfigApplicationContext时,这种方法特别有用。

例子:

  1. public static void main(String[] args) {
  2. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
  3. context.register(Conf.class);
  4. context.refresh();
  5. HelloService helloService = context.getBean(HelloService.class);
  6. String hello = helloService.hello();
  7. System.out.println(hello);
  8. }

其中的refresh方法是一个初始化工作。否则注册的类并不会被生成bean。

使用scan(String …)组件扫描

组件扫描,只需要设置好对应包路径,spring容器回自动扫描包下面所有能够被容器初始化的Java类。

使用注解:

  1. @Configuration
  2. @ComponentScan("com.example.springdemo.beans")
  3. public class Conf {
  4.  
  5. @Bean
  6. public HelloService helloService() {
  7. //用这种方法创建的service相当于用@Service注解标注
  8. return new HelloServiceImpl();
  9. }
  10.  
  11. }
  12.  

在该路径下还有一个配置文件:

  1. @Configuration
  2. public class Conf2 {
  3.  
  4. @Bean
  5. public ByeService byeService() {
  6. //用这种方法创建的service相当于用@Service注解标注
  7. return new ByeServiceImpl();
  8. }
  9.  
  10. }
  11.  

然后是初始化容器:

  1. public static void main(String[] args) {
  2. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
  3. context.register(Conf.class);
  4. context.refresh();
  5. ByeService byeService = context.getBean(ByeService.class);
  6. String hello = byeService.bye();
  7. System.out.println(hello);
  8. }

可以看到,虽然传入的是Conf类,但是由于包扫描机制,该容器同时创建了Conf2类中的bean。

这就类似xml配置中的:

  1. <beans>
  2. <context:component-scan base-package="com.example.springdemo.beans"/>
  3. </beans>
  4.  

还可以直接调用容器的扫描方法

  1. public static void main(String[] args) {
  2. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
  3. // context.register(Conf.class);
  4. context.scan("com.example.springdemo.beans");
  5. context.refresh();
  6. ByeService byeService = context.getBean(ByeService.class);
  7. String hello = byeService.bye();
  8. System.out.println(hello);
  9. }
  10.  

springboot中的包扫描

springboot通过main方法启动,其中的注解为@SpringBootApplication。通过查看该注解的代码可以发现一下代码段:

  1. @AliasFor(
  2. annotation = ComponentScan.class,
  3. attribute = "basePackages"
  4. )
  5.  

由此可以知道@SpringBootApplication注解包括了包扫描注解,同时扫描的是该类的目录以及子目录的所有可以被spring容器初始化的类

AnnotationConfigWebApplicationContext对于web应用的支持

AnnotationConfigApplicationContext在WebApplicationContext中的变体为 AnnotationConfigWebApplicationContext。当配置Spring ContextLoaderListener servlet 监听器、Spring MVC DispatcherServlet的时候,可以用此实现。

Bean依赖

@Bean注解方法可以具有描述构建该bean所需依赖关系的任意数量的参数。依赖的必须也是Ioc容器中注册的bean。

将上面的代码修改后如下:

  1. @Configuration
  2. public class Conf {
  3.  
  4. @Bean
  5. public HelloService helloService(ByeService byeService) {
  6. return new HelloServiceImpl(byeService);
  7. }
  8.  
  9. @Bean
  10. public ByeService byeService() {
  11. return new ByeServiceImpl();
  12. }
  13.  
  14. }
  1. public class HelloServiceImpl implements HelloService {
  2.  
  3. private ByeService byeService;
  4.  
  5. public HelloServiceImpl(ByeService byeService) {
  6. this.byeService = byeService;
  7. }
  8. @Override
  9. public String hello() {
  10. return "hello world" + byeService.bye();
  11. }
  12. }
  1. public static void main(String[] args) {
  2. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
  3. context.register(Conf.class);
  4. context.refresh();
  5. HelloService helloService = context.getBean(HelloService.class);
  6. String hello = helloService.hello();
  7. System.out.println(hello);
  8. ByeService byeService = context.getBean(ByeService.class);
  9. String bye = byeService.bye();
  10. System.out.println(bye);
  11. }

输出结果:

hello worldGoodbye!

Goodbye!

这种解决原理和基于构造函数的依赖注入几乎相同。

生命周期回调

@Bean注解支持任意的初始化和销毁回调方法,这与Spring XML 中bean元素上的init方法和destroy-method属性非常相似:

  1. @Bean(initMethod = "init")
  2. public HelloService helloService(ByeService byeService) {
  3. return new HelloServiceImpl(byeService);
  4. }
  5.  
  6. @Bean(destroyMethod = "destroy")
  7. public ByeService byeService() {
  8. return new ByeServiceImpl();
  9. }
  10.  
  11. public interface ByeService {
  12.  
  13. String bye();
  14.  
  15. void destroy();
  16.  
  17. }
  18.  
  19. public interface HelloService {
  20.  
  21. String hello();
  22.  
  23. void init();
  24. }
  25.  
  26. public static void main(String[] args) {
  27. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
  28. context.register(Conf.class);
  29. context.refresh();
  30. context.close();
  31. }
  32.  

输出如下:

init helloService!!

destroy byeService!

默认情况下,Ioc容器关闭后所有bean都会被销毁,但是如果要引入一个生命周期在应用程序之外进行管理的组件,例如:DataSource。那么只需要将@Bean(destroyMethod =””)添加到你的bean定义中即可禁用默认(推测)模式。

  1. @Bean(destroyMethod="")
  2. public DataSource dataSource() throws NamingException {
  3. return (DataSource) jndiTemplate.lookup("MyDS");
  4. }
  5.  

当然,初始化的时候也可以先执行对应方法,而不用交给Ioc容器

  1. @Bean
  2. public HelloService helloService(ByeService byeService) {
  3. HelloService helloService = new HelloServiceImpl(byeService);
  4. helloService.init();
  5. return helloService;
  6. }
  7.  

@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实例。
  1. @Bean
  2. //每次调用就创建一个新的bean
  3. @Scope("prototype")
  4. public UserInfo userInfo() {
  5. return new UserInfo();
  6. }
  7.  
  8. @Bean
  9. public UserService userService() {
  10. UserService userService = new UserServiceImpl();
  11. userService.init(userInfo());
  12. return userService;
  13. }

测试代码:

  1. public static void main(String[] args) {
  2. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
  3. context.register(Conf.class);
  4. context.refresh();
  5. UserService userService = context.getBean(UserService.class);
  6. UserService userService2 = context.getBean(UserService.class);
  7. UserInfo userInfo = context.getBean(UserInfo.class);
  8. UserInfo userInfo2 = context.getBean(UserInfo.class);
  9. System.out.println(userService == userService2);
  10. System.out.println(userInfo == userInfo2);
  11. }

输出:

true

false

自定义Bean命名

通常,bean的名称是bean的方法名,但是可以通过name属性重命名。有时一个单一的bean需要给出多个名称,称为bean别名。为了实现这个目标,@Bean注解的name属性接受一个String数组。

  1. @Bean(name = {"user", "userService", "User"})
  2. public UserService userService() {
  3. UserService userService = new UserServiceImpl();
  4. userService.init(userInfo());
  5. return userService;
  6. }
  1. public static void main(String[] args) {
  2. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
  3. context.register(Conf.class);
  4. context.refresh();
  5. Object user = context.getBean("user");
  6. Object userService = context.getBean("userService");
  7. Object User = context.getBean("User");
  8.  
  9. System.out.println(user == userService);
  10. System.out.println(user == User);
  11. System.out.println(userService == User);
  12. }
  13.  

输出:

true

true

true

Bean描述

有时候需要提供一个详细的bean描述文本是非常有用的。当对bean暴露(可能通过JMX)进行监控使,特别有用。可以使用@Description注解对Bean添加描述:

  1. @Bean(name = {"user", "userService", "User"})
  2. @Description("这是用户服务对象")
  3. public UserService userService() {
  4. UserService userService = new UserServiceImpl();
  5. userService.init(userInfo());
  6. return userService;
  7. }
  1. public static void main(String[] args) {
  2. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
  3. context.register(Conf.class);
  4. context.refresh();
  5. String description = context.getBeanDefinition("user").getDescription();
  6. System.out.println(description);
  7. }
  8.  

输出:

这是用户服务对象

基于Java组合配置

使用@Import注解

和Spring XML文件中使用元素来帮助模块化配置类似,@Import注解允许从另一个配置类加载@Bean定义:

  1. @Configuration
  2. @Import(UserConf.class)
  3. public class Conf {
  4.  
  5. @Bean(initMethod = "init")
  6. public HelloService helloService(ByeService byeService) {
  7. //用这种方法创建的service相当于用@Service注解标注
  8. return new HelloServiceImpl(byeService);
  9. }
  10.  
  11. @Bean(destroyMethod = "destroy")
  12. public ByeService byeService() {
  13. return new ByeServiceImpl();
  14. }
  15.  
  16. }
  17.  
  1. @Configuration
  2. public class UserConf {
  3.  
  4. @Bean
  5. //每次调用就创建一个新的bean
  6. @Scope("prototype")
  7. public UserInfo userInfo() {
  8. return new UserInfo();
  9. }
  10.  
  11. @Bean(name = {"user", "userService", "User"})
  12. @Description("这是用户服务对象")
  13. public UserService userService() {
  14. UserService userService = new UserServiceImpl();
  15. userService.init(userInfo());
  16. return userService;
  17. }
  18.  
  19. }
  20.  
  1. public static void main(String[] args) {
  2. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
  3. context.register(Conf.class);
  4. context.refresh();
  5. String description = context.getBeanDefinition("user").getDescription();
  6. System.out.println(description);
  7. }

这种方法简化了容器实例化,因为只需要处理一个类,而不是需要开发人员在构建期间记住大量的@Configuration注解类。

Java and XML 混合配置

Java配置并不能100%替代xml配置,因此Ioc容器支持两者混合配置。不过这里有个区别就是以xml为中心还是以Java配置为中心。

以XML为中心

  1. @Configuration
  2. public class DataSourceConf {
  3.  
  4. @Autowired
  5. private DataSource dataSource;
  6.  
  7. @Bean
  8. public DataSourceService dataSource() {
  9. return new DataSourceerviceImpl(dataSource);
  10. }
  11.  
  12. }
  1. jdbc.url=jdbc:mysql://39.108.119.174:3306/dust
  2. jdbc.username=root
  3. jdbc.password=123456
  4.  
  1. <beans>
  2.  
  3. <context:annotation-config/>
  4.  
  5. <context:property-placeholder location="classpath:jdbc.properties"/>
  6.  
  7. <bean class="com.example.springdemo.beans.DataSourceConf"/>
  8.  
  9. <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  10. <property name="url" value="${jdbc.url}"/>
  11. <property name="username" value="${jdbc.username}"/>
  12. <property name="password" value="${jdbc.password}"/>
  13. </bean>
  14.  
  15. </beans>
  16.  
  1. public static void main(String[] args) {
  2. ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/datasource.xml");
  3. DataSourceService dataSourceService = context.getBean(DataSourceService.class);
  4. System.out.println(dataSourceService.toString());
  5. }
  6.  

以Java类为中心

  1. <beans>
  2. <context:property-placeholder location="classpath:jdbc.properties"/>
  3. </beans>
  1. @Configuration
  2. @ImportResource("classpath:spring/datasource.xml")
  3. public class DataSourceConf {
  4.  
  5. @Value("${jdbc.url}")
  6. private String url;
  7.  
  8. @Value("${jdbc.username}")
  9. private String username;
  10.  
  11. @Value("${jdbc.password}")
  12. private String password;
  13.  
  14. @Bean
  15. public DataSourceService dataSource() {
  16. return new DataSourceerviceImpl(url, username, password);
  17. }
  18.  
  19. }
  1. public static void main(String[] args) {
  2. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
  3. context.scan("com.example.springdemo.beans");
  4. context.refresh();
  5. DataSourceService dataSourceService = context.getBean(DataSourceService.class);
  6. System.out.println(dataSourceService.toString());
  7.  
  8. // ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/datasource.xml");
  9. // DataSourceService dataSourceService = context.getBean(DataSourceService.class);
  10. // System.out.println(dataSourceService.toString());
  11. }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持w3xue。

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号