好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

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

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

2

3

4

5

6

7

8

9

@configuration

public class conf {

  

   @bean

   public helloservice helloservice() {

     return new helloserviceimpl();

   }

  

}

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

?

1

2

3

<beans>

   <bean id= "helloservice" class = "com.dust.service.impl.helloserviceimpl" />

</beans>

等价于注解配置中的

?

1

2

3

4

5

6

7

8

9

@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。

?

1

2

3

4

5

6

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注解的类都可以作为输入提供给构造函数。例如:

?

1

2

3

4

5

6

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时,这种方法特别有用。

例子:

?

1

2

3

4

5

6

7

8

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类。

使用注解:

?

1

2

3

4

5

6

7

8

9

10

11

@configuration

@componentscan ( "com.example.springdemo.beans" )

public class conf {

 

   @bean

   public helloservice helloservice() {

     //用这种方法创建的service相当于用@service注解标注

     return new helloserviceimpl();

   }

 

}

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

?

1

2

3

4

5

6

7

8

9

10

@configuration

public class conf2 {

 

   @bean

   public byeservice byeservice() {

     //用这种方法创建的service相当于用@service注解标注

     return new byeserviceimpl();

   }

 

}

然后是初始化容器:

?

1

2

3

4

5

6

7

8

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配置中的:

?

1

2

3

<beans>

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

</beans>

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

?

1

2

3

4

5

6

7

8

9

   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。通过查看该注解的代码可以发现一下代码段:

?

1

2

3

4

@aliasfor (

   annotation = componentscan. class ,

   attribute = "basepackages"

)

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

annotationconfigwebapplicationcontext对于web应用的支持

annotationconfigapplicationcontext在webapplicationcontext中的变体为 annotationconfigwebapplicationcontext。当配置spring contextloaderlistener servlet 监听器、spring mvc dispatcherservlet的时候,可以用此实现。

bean依赖

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

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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

@configuration

public class conf {

 

   @bean

   public helloservice helloservice(byeservice byeservice) {

     return new helloserviceimpl(byeservice);

   }

 

   @bean

   public byeservice byeservice() {

     return new byeserviceimpl();

   }

 

}

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

public class helloserviceimpl implements helloservice {

 

   private byeservice byeservice;

 

   public helloserviceimpl(byeservice byeservice) {

     this .byeservice = byeservice;

   }

  

   @override

   public string hello() {

     return "hello world" + byeservice.bye();

   }

  

}

?

1

2

3

4

5

6

7

8

9

10

11

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属性非常相似:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

   @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定义中即可禁用默认(推测)模式。

?

1

2

3

4

@bean (destroymethod= "" )

public datasource datasource() throws namingexception {

   return (datasource) jnditemplate.lookup( "myds" );

}

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

?

1

2

3

4

5

6

@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实例。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

@bean

//每次调用就创建一个新的bean

@scope ( "prototype" )

public userinfo userinfo() {

   return new userinfo();

}

 

@bean

public userservice userservice() {

   userservice userservice = new userserviceimpl();

   userservice.init(userinfo());

   return userservice;

}

测试代码:

?

1

2

3

4

5

6

7

8

9

10

11

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数组。

?

1

2

3

4

5

6

@bean (name = { "user" , "userservice" , "user" })

public userservice userservice() {

   userservice userservice = new userserviceimpl();

   userservice.init(userinfo());

   return userservice;

}

?

1

2

3

4

5

6

7

8

9

10

11

12

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添加描述:

?

1

2

3

4

5

6

7

@bean (name = { "user" , "userservice" , "user" })

@description ( "这是用户服务对象" )

public userservice userservice() {

   userservice userservice = new userserviceimpl();

   userservice.init(userinfo());

   return userservice;

}

?

1

2

3

4

5

6

7

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定义:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

@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();

   }

 

}

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

@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;

   }

 

}

?

1

2

3

4

5

6

7

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为中心

?

1

2

3

4

5

6

7

8

9

10

11

12

@configuration

public class datasourceconf {

 

   @autowired

   private datasource datasource;

 

   @bean

   public datasourceservice datasource() {

     return new datasourceerviceimpl(datasource);

   }

 

}

?

1

2

3

jdbc.url=jdbc:mysql: //39.108.119.174:3306/dust

jdbc.username=root

jdbc.password= 123456

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

<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>

?

1

2

3

4

5

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类为中心

?

1

2

3

<beans>

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

</beans>

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

@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);

   }

 

}

?

1

2

3

4

5

6

7

8

9

10

11

   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());

   }

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

查看更多关于spring boot基于Java的容器配置讲解的详细内容...

  阅读:15次