文章目录
从
Spring 3.0
开始,
Spring
提供的许多功能都是使用的
JavaConfig
配置的。所以我们可以使用
Java
而不是
XML
文件来定义应用程序类外部的
bean
。
@Bean和@Configuration 的基础使用
Spring
新Java
配置主要组成部分就是@Configuration注释的类和@Bean注释的方法。其实@Bean
注释与XML
中的<bean />
元素具有相同的作用 ,@Bean
注解也是主要用于标注的方法来实例化,配置和初始化要由Spring IoC
容器管理的新对象。@Bean
批注方法也可以与任何Spring @Component
一起使用。但是,一般我们都是与@Configuration
bean
一起使用。 用@Configuration
注释类表示该类的主要目的是作为Bean
定义的来源。此外,@Configuration
类允许通过调用同一类中的其他@Bean
方法来定义Bean
之间的依赖关系。
最简单的@Configuration
类的内容如下:
@Configuration
public class Javaconfig {
@Bean
public User user(){
return new User("张三",18);
}
}
其中Bean
的定义如下:
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
// get/set ...
}
使用AnnotationConfigApplicationContext实例化Spring容器的几中方式
AnnotationConfigApplicationContext
这种通用的ApplicationContext
实现不仅能够接受@Configuration
类作为输入,而且还可以接受普通的@Component
类和带有SR-330
元数据注释的类。 当提供@Configuration
类作为输入时,@Configuration
类本身将注册为Bean
定义,并且该类中所有已声明的@Bean
方法也将注册为Bean
定义。 提供@Component
和JSR-330
类时,它们将注册为bean
定义。
- 使用简单默认的方式创建
AnnotationConfigApplicationContext
与实例化ClassPathXmlApplicationContex
t时将Spring
XML
文件用作参数的方式几乎相同,实例化AnnotationConfigApplicationContext
时可以将@Configuration
类用作参数
如下面的示例所示完全不需要使用XML
就可以初始化IOC
容器
public class CommonCeshi {
public static void main(String[] args) {
ApplicationContext applicationContext =
new AnnotationConfigApplicationContext(Javaconfig.class);
User user = applicationContext.getBean("user", User.class);
System.out.println(user.toString());
}
}
如上所述,AnnotationConfigApplicationContext
不限于仅与@Configuration
类一起使用。可以将任何@Component
或JSR-330
带注释的类作为参数提供给构造函数,如以下示例所示:
public class CommonCeshi {
public static void main(String[] args) {
ApplicationContext applicationContext =
new AnnotationConfigApplicationContext(User.class);
User user = applicationContext.getBean("user", User.class);
System.out.println(user.toString());
}
}
其中上述的User
类使用了@Component
注解,让其添加到AnnotationConfigApplicationContext
也是可以构建IOC
容器的,User
定义如下:
@Component
public class User {
private String name = "历史";
private int age = 12;
// get/set ...
}
- 使用
register(Class<?>…)
的方式构建AnnotationConfigApplicationContext
容器
您可以使用无参数构造函数实例化AnnotationConfigApplicationContext
,然后使用register()
方法对其进行配置。以编程方式构建AnnotationConfigApplicationContext
时,此方法特别有用。以下示例显示了如何执行此操作:
public class CommonCeshi {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext();
applicationContext.register(User.class);
//记得这里需要执行refresh()方法刷新容器
applicationContext.refresh();
User user = applicationContext.getBean("user", User.class);
System.out.println(user.toString());
}
}
- 使用
scan(String…)
开启组件扫描来实例化AnnotationConfigApplicationContext
容器
scan()
方法的主要作用就是扫描参数中指定包结构下通过@Component
注解标注的类,把这些类定义成Bean
并加载到IOC
容器中,组件扫描可以使用以下几中方法定义-
使用
@ComponentScan(basePackages = "xxxx")
注解的形式开启扫描@Configuration @ComponentScan(value = "ioc.javabaseconfig") public class Javaconfig { }
-
使用
XML
形式定义开启扫描<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="ioc.javabaseconfig"/> </beans>
-
使用调用
scan(xxx)
方法的形式开启扫描public class CommonCeshi { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.scan("ioc.javabaseconfig"); //记得这里需要执行refresh()方法刷新容器 applicationContext.refresh(); User user = applicationContext.getBean("user", User.class); System.out.println(user.toString()); } }
-
通过AnnotationConfigWebApplicationContext支持Web应用程序
AnnotationConfigWebApplicationContext
提供了AnnotationConfigApplicationContext
的WebApplicationContext
变体。在配置Spring
ContextLoaderListener
Servlet
侦听器,Spring MVC DispatcherServlet
等时,可以使用此实现。以下web.xml
代码片段配置了典型的Spring MVC Web
应用程序(请注意contextClass context-param
和init-param
的用法):
<web-app>
<!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext
instead of the default XmlWebApplicationContext -->
<context-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</context-param>
<!-- Configuration locations must consist of one or more comma- or space-delimited
fully-qualified @Configuration classes. Fully-qualified packages may also be
specified for component-scanning -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.acme.AppConfig</param-value>
</context-param>
<!-- Bootstrap the root application context as usual using ContextLoaderListener -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Declare a Spring MVC DispatcherServlet as usual -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext
instead of the default XmlWebApplicationContext -->
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<!-- Again, config locations must consist of one or more comma- or space-delimited
and fully-qualified @Configuration classes -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.acme.web.MvcConfig</param-value>
</init-param>
</servlet>
<!-- map all requests for /app/* to the dispatcher servlet -->
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
</web-app>
@Bean注解的使用
@Bean
是方法级别的注解,跟XML <bean />
元素的作用是一样的。<bean />
提供的一些属性,例如: init-method
destroy-method
autowiring
等@Bean
注解都支持。可以在@Configuration
注解类或@Component
注解的类中使用@Bean
注解。
-
如何声明一个
Bean
要声明bean
,可以使用@Bean
注解对方法进行标注。可以使用此方法的返回值(其中这个返回值可以是这个类实现的接口或者父类,不一定必须要是自己)在ApplicationContext
中注册Bean
定义。默认情况下,Bean
名称与方法名称相同。以下示例显示了@Bean
法声明:@Configuration public class Javaconfig { @Bean public User user(){ User user = new User(); user.setAge(13); user.setName("王五"); return user; } }
以上声明
Bean
的方式等同于下面XML
声明的形式:<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <bean id="user" class="ioc.javabaseconfig.User"></bean> </beans>
-
注入
Bean
的依赖
@Bean
注解的方法可以具有任意数量的参数,这些参数描述构建该bean
所需的依赖关系。例如,如果我们的User
需要一个Address
地址参数,则可以使用方法参数来实现该依赖关系,如以下示例所示:@Configuration public class Javaconfig { @Bean public User user(Address address){ User user = new User(); user.setAge(13); user.setName("王五"); user.setAddress(address); return user; } //也可以不使用方法参数的形式,直接调用方法 // @Bean // public User user(){ // User user = new User(); // user.setAge(13); // user.setName("王五"); // user.setAddress(address()); // return user; // } @Bean public Address address(){ Address address = new Address(); address.setAddress("深圳"); return address; } }
-
Bean
的声明周期回调
任何使用@Bean
注解定义的类都支持常规的生命周期回调,并且可以使用JSR-250
中的@PostConstruct
和@PreDestroy
注解。还完全支持常规的Spring
生命周期回调。如果bean
实现了InitializingBean
,DisposableBean
或Lifecycle
,则容器将调用它们各自的方法。 还完全支持标准的Aware
接口集(例如BeanFactoryAware
,BeanNameAware
,MessageSourceAware
,ApplicationContextAware
等)。@Bean
注解支持指定任意的初始化和销毁回调方法,非常类似于`bean元素上Spring XML的init-method和destroy-method属性,如以下示例所示:@Configuration public class Javaconfig { @Bean(initMethod = "initUser",destroyMethod = "destoryUser") public User user(Address address){ User user = new User(); user.setAge(13); user.setName("王五"); user.setAddress(address); return user; } @Bean public Address address(){ Address address = new Address(); address.setAddress("深圳"); return address; } }
在
User
类中定义好初始化和销毁的方法,这样User
对象在被初始化的时候会先调用initUser
这个初始化方法在执行后续的操作,最后对象被销毁的时候也会调用destoryUser
方法进行销毁,如果不想对象被销毁的时候调用此方法,可以在Configuration
类中的destroyMethod
不给值就可以了,另外初始方法和销毁方法也可以不通过@Bean
的属性指定,可以在new
对象后直接用对象调用这两个方法也是可以的:public class User { private String name = "历史"; private int age = 12; private Address address; public void initUser(){ // init System.out.println("init User"); } public void destoryUser(){ //destory System.out.println("destory User"); } // get/set ...
-
指定
Bean
的scope
作用域
您可以指定使用@Bean
批注定义的bean
应该具有的作用域。使用Scopes
指定任何标准范围。 默认范围是单例,但是您可以使用@Scope
注释覆盖它,如以下示例所示:@Configuration public class Javaconfig { @Bean @Scope(value = "prototype") public User user(Address address){ User user = new User(); user.setAge(13); user.setName("王五"); user.setAddress(address); return user; } @Bean public Address address(){ Address address = new Address(); address.setAddress("深圳"); return address; } }
-
-
Bean
自定义一个名称
默认情况下,配置类使用@Bean
方法的名称作为结果bean
的名称。但是,可以使用name
属性覆盖此功能,如以下示例所示:@Configuration public class Javaconfig { @Bean(name = "userName") public User user(Address address){ User user = new User(); user.setAge(13); user.setName("王五"); user.setAddress(address); return user; } @Bean(name = "abc") public Address address(){ Address address = new Address(); address.setAddress("深圳"); return address; } }
-
Bean
自定义一个别名
有时希望为单个Bean
提供多个名称,也称为Bean
别名。为此,@Bean
注解的name
属性接受一个String
数组。以下示例显示了如何为bean
设置多个别名:@Configuration public class Javaconfig { @Bean(name = {"user","userName"}) public User user(Address address){ User user = new User(); user.setAge(13); user.setName("王五"); user.setAddress(address); return user; } @Bean(name = {"address","adb"}) public Address address(){ Address address = new Address(); address.setAddress("深圳"); return address; } }
-
Bean
自定义一段描述
有时,提供有关bean
的更详细的文本描述会很有帮助。对于向外暴露API的时候可以提供描述信息。 要向@Bean
添加描述,可以使用@Description
注解,如以下示例所示:@Configuration public class Javaconfig { @Bean(name = {"user","userName"}) @Description("这是一个User对象的描述") public User user(Address address){ User user = new User(); user.setAge(13); user.setName("王五"); user.setAddress(address); return user; } @Bean(name = {"address","adb"}) @Description("这是地址对象的描述") public Address address(){ Address address = new Address(); address.setAddress("深圳"); return address; } }
@Configuration注解的使用
@Configuration
是类级别的注解,指示对象是Bean
定义的源。 @Configuration
类通过公共@Bean
注解方法声明bean
。对@Configuration
类的@Bean
方法的调用也可以用于定义Bean
之间的依赖关系。
-
注入bean间的依赖关系
当bean
彼此依赖时,表达这种依赖就像让一个bean
方法调用另一个一样简单,如以下示例所示:@Configuration public class Javaconfig { @Bean public User user(Address address){ User user = new User(); user.setAge(13); user.setName("王五"); user.setAddress(address); return user; } //也可以不使用方法参数的形式,直接调用方法 // @Bean // public User user(){ // User user = new User(); // user.setAge(13); // user.setName("王五"); // user.setAddress(address()); // return user; // } @Bean public Address address(){ Address address = new Address(); address.setAddress("深圳"); return address; } }
仅当在
@Configuration
类中声明@Bean
方法时,此声明bean
间依赖关系的方法才有效。在使用@Component
注解类中声明的Bean
的依赖关系不能直接调用。 -
Lookup
查找方法注入
查找方法注入是一项高级功能,一般很少使用。在单例作用域的bean
依赖于原型作用域的bean
的情况下,这很有用。将Java
用于这种类型的配置为实现此模式提供了自然的方法。下面的示例演示如何使用查找方法注入:
首先定义一个单例的抽象类,其中有个需要原型作用域的抽象方法,最后这个抽象方法在配置类中覆盖就可以了。public abstract class LookupMethod { public Object process(){ User user = createdUser(); return user; } public abstract User createdUser(); }
配置类中配置需要的对象:
@Configuration public class Javaconfig { @Bean public LookupMethod lookupMethod(){ return new LookupMethod() { @Override public User createdUser() { return user(address()); } }; } @Bean(name = {"user","userName"}) @Scope("prototype") public User user(Address address){ User user = new User(); user.setAge(13); user.setName("王五"); user.setAddress(address); return user; } @Bean(name = {"address","adb"}) @Description("这是地址对象的描述") public Address address(){ Address address = new Address(); address.setAddress("深圳"); return address; } }
-
有关基于Java的配置如何在内部工作的更多信息
以下示例,显示了一个@Bean
注释方法被调用两次,被调用两次的Bean
应该会创建两个对象,但是其实只创建了一个对象:@Configuration public class Javaconfig { @Bean public User user1(){ User user = new User(); user.setAge(13); user.setName("王五"); user.setAddress(address()); return user; } @Bean public User user2(){ User user = new User(); user.setAge(13); user.setName("王五"); user.setAddress(address()); return user; } @Bean public Address address(){ Address address = new Address(); address.setAddress("深圳"); return address; } }
address()
在user1()
中被调用一次,并在user2()
中被调用一次。由于此方法创建了Address
的新实例并返回它,因此通常希望有两个实例(每个服务一个)。那肯定是有问题的:在Spring
中,实例化的bean
默认情况下具有单例作用域。这就是神奇的地方:所有@Configuration
类在启动时都使用CGLIB
进行了子类化。在子类中,子方法在调用父方法并创建新实例之前,首先检查容器中是否有任何缓存(作用域)的bean
,如果有就不会再调用方法创建,也就是只会创建一次单例的对象,如果作用域是原型的就创建两个对象。从Spring 3.2
开始,不再需要将CGLIB
添加到类路径中,因为CGLIB
类已经在org.springframework.cglib
下重新打包并直接包含在spring-core jar
中。
结合各个 Java配置类
-
使用
@Import
注解
与在Spring XML
文件中使用<import />
元素来帮助模块化配置一样,@Import
批注允许从另一个配置类加载@Bean
定义,如以下示例所示:@Configuration public class JavaConfigOther { @Bean public Address address(){ Address address = new Address(); address.setAddress("深圳"); return address; } } @Configuration @Import(JavaConfigOther.class) public class Javaconfig { //也可以使用这种方式 //@Autowired //private Address address; @Bean public User user(Address address){ User user = new User(); user.setAge(13); user.setName("王五"); user.setAddress(address); return user; } }
以上也可以不使用
@Import
注解导入配置类,因为@Configuration
注解也会被定义成Bean
所以也可以直接使用@Autowired
这种的注解注入进来。 -
有条件地包含
@Configuration
类或@Bean
方法
根据某些系统状态,有条件地启用或禁用完整的@Configuration
类甚至单个@Bean
方法通常很有用。一个常见的示例是仅在Spring
环境中启用了特定配置文件时才使用@Profile
注解来激活Bean
。@Profile
注解实际上是通过使用更灵活的称为@Conditional
的注释来实现的。@Conditional
注解指示在注册@Bean
之前应咨询的特定org.springframework.context.annotation.Condition
实现。Condition
接口的实现提供了一个matches(…)
方法,该方法返回true
或false
。例如,以下清单显示了用于@Profile
的实际Condition
实现:@Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { // Read the @Profile annotation attributes MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName()); if (attrs != null) { for (Object value : attrs.get("value")) { if (context.getEnvironment().acceptsProfiles(((String[]) value))) { return true; } } return false; } return true; }
-
结合
Java
和XML
配置
Spring
的@Configuration
类支持并非旨在100%完全替代Spring XML
。某些工具(例如Spring XML
名称空间)仍然是配置容器的理想方法。在使用XML
方便或有必要的情况下,您可以选择:使用ClassPathXmlApplicationContext
以以XML
为中心的方式实例化容器,或使用AnnotationConfigApplicationContext
和“AnnotationConfigApplicationContext
以Java
中心的方式实例化容器。@ImportResource
批注可根据需要导入XML
。-
以
XML
为中心的@Configuration
类的使用
在使用Spring XML
的大型现有代码库中,可以根据需要轻松创建@Configuration
类,并从现有XML
文件中包含它们-
将
@Configuration
类声明为纯Spring <bean />
元素
请记住,@Configuration
类最终是容器中的bean
定义。在本系列示例中,我们创建一个名为JavaConfig
的@Configuration
类,并将其作为<bean />
定义包含在spring-config.xml
中。由于<context:annotation-config />
处于打开状态,因此容器可以识别@Configuration
注解并正确处理JavaConfig
中声明的@Bean
方法。 以下示例显示了Java
中的普通配置类:@Configuration public class Javaconfig { @Autowired private DataSource dataSource; @Bean public User user(){ User user = new User(); user.setAge(13); user.setName("王五"); return user; } }
以下是
spring-config.xml
的配置信息如下:<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> <context:property-placeholder location="classpath*:spring.properties" file-encoding="UTF-8"/> <bean class="ioc.javabaseconfig.Javaconfig"/> <bean class="ioc.javabaseconfig.Address"> <property name="address" value="${address.name}"/> </bean> </beans>
系统属性文件
spring.properties
内容如下:address.name=广州
-
使用
<context:component-scan />
扫描@Configuration
类<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!-- 这里开启扫描来批量注入Bean,所以基于注解和<bean>都可以不用声明,系统外的jar包的类是需要声明的 --> <context:component-scan base-package="ioc.javabaseconfig"/> <!-- <context:annotation-config/>--> <context:property-placeholder location="classpath*:spring.properties" file-encoding="UTF-8"/> <!-- <bean class="ioc.javabaseconfig.Javaconfig"/>--> <!-- <bean class="ioc.javabaseconfig.Address">--> <!-- <property name="address" value="${address.name}"/>--> <!-- </bean>--> </beans>
-
-
@
Configuration
以类为中心的XML
与@ImportResource
的使用
在@Configuration
类是配置容器的主要机制的应用程序中,仍然有必要至少使用一些XML
。在这些情况下,您可以使用@ImportResource
并仅定义所需的XML
。这样做实现了以Java
为中心的方法来配置容器,并使XML
保持在最低限度。以下示例(包括配置类,定义Bean
的XML
文件,属性文件和主类)显示了如何使用@ImportResource
批注来实现按需使用XML
的以Java
为中心的配置:@Configuration @ImportResource(value = "classpath:spring-config.xml") public class Javaconfig { @Value("${address.name}") private String addre; @Bean public User user(){ User user = new User(); user.setAge(13); user.setName("王五"); user.setAddress(address()); return user; } @Bean public Address address(){ Address address = new Address(); address.setAddress(addre); return address; } }
XML
的配置如下:<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:property-placeholder location="classpath*:spring.properties" file-encoding="UTF-8"/> </beans>
系统属性文件
spring.properties
内容如下:address.name=广州
-
以上内容就是如何使用Java
配置来初始化Spring IOC
容器,注入指定Bean
的处理方式。
参考文献
【https://docs.spring.io/spring-framework/docs/current/reference/html/core.html】
【1.12. Java-based Container Configuration】