评论

收藏

[Java] spring boot基于Java的容器配置讲解

编程语言 编程语言 发布于:2021-08-17 12:48 | 阅读数:290 | 评论:0

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名称就是方法名称.
例子:
@configuration
public class conf {
  
  @bean
  public helloservice helloservice() {
  return new helloserviceimpl();
  }
  
}
这种配置方式就类似于xml配置中的
<beans>
  <bean id="helloservice" class="com.dust.service.impl.helloserviceimpl" />
</beans>
等价于注解配置中的
@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实例的。

  • singleton:一个spring容器中只有一个bean的实例,此为spring的默认配置,全容器共享一个实例。
  • prototype:每次调用新建一个bean实例。
  • request:web项目中,给每一个 http request 新建一个bean实例。
  • session:web项目中,给每一个 http session 新建一个bean实例。
  • 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());
  }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持CodeAE代码之家

关注下面的标签,发现更多相似文章