Brief introduction
Our writing program, there are between the various categories dependent, dependent on the class of the instance of the need to manually re-assigned to it. Since we are automatically managed by the IoC container Bean, each use need to manage such dependencies too cumbersome.
So there by way of the configuration file to automatically inject dependent bean.
spring assembly 3 xml装配
ways: java装配
, 自动装配
, .
Compared to
xml装配
recommend it is to usejava装配
.
Commonly used 自动装配
to reduce the configuration file.
Automated assembly
From the perspective of two spring assembly automatically:
组件扫描
: Spring bean will automatically find the application context created.自动装配
: Spring assembly automatically satisfy the bean.
Project structure
.
├── build.gradle
└── src/
├── main/
│ ├── java/
│ │ └── com/
│ │ └── yww/
│ │ ├── Main.java
│ │ ├── Message.java
│ │ ├── ReaderConfig.java
│ │ └── Reader.java
│ └── resources/
│ └── beans.xml
└── test/
├── java/
│ └── com/
│ └── yww/
│ └── MainTest.java
└── resources/
Code
build.gradle
Project build configuration.
Part of the comment is the use of plug-ins and direct
gradle run
command to run the project.jar{}
Section configuration, as can be packaged with ajava
command to runjar
the package. (Note that you need to specify the full name of a good master class)
plugins {
id 'java'
// id 'application'
}
// mainClassName = 'com.yww.Main'
group 'com.yww'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
ext{
springVersion = '5.2.0.RELEASE'
}
dependencies {
compile "org.springframework:spring-core:$springVersion"
compile "org.springframework:spring-context:$springVersion"
compile "org.springframework:spring-beans:$springVersion"
compile "org.springframework:spring-expression:$springVersion"
compile "org.springframework:spring-aop:$springVersion"
compile "org.springframework:spring-aspects:$springVersion"
testCompile "junit:junit:4.12"
testCompile "org.springframework:spring-test:$springVersion"
}
jar {
from {
configurations.runtime.collect{zipTree(it)}
}
manifest {
attributes 'Main-Class': 'com.yww.Main'
}
}
Message.java
By using @Component
annotations, when the component scanning, registration bean.
@Value
Notes straightforward assignment. Bean configuration is different from the xml configuration file so cumbersome.
package com.yww;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class Message {
@Value("---hello world---")
private String msg;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = "this mssage is : " + msg;
}
}
Reader.java
It depends on the Message.java
use of @Autowired
injection Message
the bean into Reader
the.
Here can be
@Autowired
written on the member variables can also be written in the constructor, choose one of the two can be. (Constructor automatically connect can do some other operations only)
package com.yww;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Reader {
// @Autowired
private Message msg;
@Autowired
public Reader(Message msg){
this.msg = msg;
}
public void print(){
System.out.println(msg.getMsg());
}
}
ReaderConfig.java
Profile, mainly when used in acquiring application context, such a complete open configuration provided therein. Its class name any. Here is the main component scans open the default package with the same name @Component
etc. annotation registered as a bean.
Traditionally, we can do some configuration work here, such as setting the initial value, or how to assemble. For convenience, the initial value used in the previous
@Value
set.
package com.yww;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan
public class ReaderConfig {
// 可以在配置文件中设置初始值
// 为了方便直接,就在Message的成员上使用@Value设置了值。
// @Bean
// public Message message(){
// Message message = new Message();
// message.setMsg("---hi---");
// return message;
// }
}
Main.java
The main function, for starting applications. We show here how to get to a dependent relationship, and in accordance with good bean automatic assembly.
Is different from the web application, app need to have a primary function to start the application, conventional web applications packaged into war will be loaded into the tomcat, but
spring boot
the web application can also have a main function to start web application.
Code comment section is to use
xml配置
, demonstrate how to obtain the bean.
package com.yww;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args){
// xml配置(beans.xml)
// ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// Message msg = (Message) context.getBean("msg1");
// System.out.println(msg.getMsg());
// java配置(ReaderConfig.java) + 获取组件扫描到的bean
// 测试文件AppTest.java中,可以通过注解@ContextConfiguration加载配置文件上下文,方便使用@Autowired注解获取Bean。
ApplicationContext context = new AnnotationConfigApplicationContext(com.yww.ReaderConfig.class);
Reader reader = (Reader) context.getBean(Reader.class);
reader.print();
}
}
The last remaining test file MainTest.java
, not much say in here, but you can use @ContextConfiguration
annotation get the context of the bean, the use of @Autowired
automatic injection bean, convenient test.
run
Because the sake of simplicity, the project is non-web project, few posts to explain this package and run. (Web projects easily by adding
war
plug-ins andgradle build
packaging)
The method of operation to try out a few:
Method 1: Use idea run directly
main
class can also be run the test classMainTest.java
.- Method 2: Use gradle, need to
build.gradle
file, add the plug-inapplication
and set good name of the main functionmainClassName
. Finally, the project root directory (build.gradle
execute the command the same directory):
gradle run
Problem
In the
非web
application, you will find a problem, not by@Autowired
acquiring the bean, which is due to non-web applications not know bean, did not provide the appropriate annotation to deal only throughApplicationContext
context acquisition bean application. And between the bean can be achieved through@Autowired
acquired.
java assembly
In many cases, automated configuration is the recommended way to scan through components and automated assembly. But if you want a third-party library to assemble their applications can not use @Component
annotations to automate the assembly.
To explicitly configured bean, can be used in the configuration file @Bean
to obtain.
// src/main/java/com/yww/ReaderConfig.java
// ...
@Bean
public Message message(){
Message message = new Message();
message.setMsg("---hi---");
return message;
}
// ...
The use of automatic assembly, the same can not create more than one type of bean.
Bean renamed
The default bean's ID and @Bean
the same annotation method name.
Available name
parameter settings other names.
@Bean(name="msgX")
Without using the scan assembly configuration
If you do not use the component scanned, how to find a configuration of another configuration, or bean?
At this time, you may be used @Import
to import that class.
package com.yww;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import({MessageConfig.class})
public class ReaderConfig {
// 可以在配置文件中设置初始值
// 为了方便直接,就在Message的成员上使用@Value设置了值。
// @Bean
// @Bean(name="msgX")
// public Message message(){
// Message message = new Message();
// message.setMsg("---hi---");
// return message;
// }
// 这个bean传入的名称可以任意.
// 但如果在同一个类里声明了名称,像上面那样指定了名词,这里的参数名也必须相同。
@Bean
public Reader reader(Message msg){
System.out.println(msg.getMsg());
msg.setMsg("---hello---");
return new Reader(msg);
}
}
Wherein, another bean imported java配置
file as follows.
// src/main/java/com/yww/MessageConfig.java
package com.yww;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MessageConfig {
@Bean
public Message message(){
return new Message();
}
}
xml Assembly
- gradle app is
xml配置
placedsrc/main/resources/
in the directory. - the web
xml配置
on theweb/WEB-INF/
directory, you needweb.xml
configuration<context-param>
.
Shaped configuration such as the following.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/
<bean id="msg1" class="com.yww.Message">
<property name="msg" value="hello world"/>
</bean>
</beans>
If not given id
, will be named by the fully qualified class name, the bean will be named herein com.yww.Message#0
, #0
it is a count of the same type of other bean.
Other settings
Because not recommended xml配置
, it is just a simple configuration in which a note of this.
There are a lot of configuration
c-命名空间
,p-命名空间
to replace the complicated labels.
- Injected bean reference configuration, injection strings, numbers, lists. (<Constructor-arg>)
- Xml configuration using <bean> JavaConfig import configuration.
<beans>
Ofprofile
property.<jdbc>
,<jee:jndi-lookup>
。<beans>
Nesting<beans>
.
Advanced Assembly
More configuration, to achieve more control assembly.
Profile
Usually we need to configure different environments, such as: test environment using the embedded database h2
, the production environment using the jndi
acquired database.
(Starting from spring4)
@Profile
is also based on@Conditional
implementation.
Configuration settings
You can use @Profile
annotations in 类
or 方法
on, to generate Bean. (As used herein, JavaConfig
the configuration can also be used xml
to configure)
package com.yww;
import org.springframework.context.annotation.*;
@Configuration
@ComponentScan
@PropertySource("application.properties")
public class ReaderConfig {
@Bean
@Profile("dev")
public Message message1(){
Message message = new Message();
message.setMsg("---hi---1");
return message;
}
@Bean
@Profile("prod")
public Message message2(){
Message message = new Message();
message.setMsg("---hi---2");
return message;
}
}
@PropertySource
Import the configuration file.
Active configuration
Determine which profile is activated by two properties spring.profiles.active
and spring.profiles.default
.
If you set up active
, use this value to determine which profile is activated. If not set active
, use default
OK. If you did not set, it will only create a profile does not define the bean.
#src/main/resources/application.properties
spring.profiles.default=dev
spring.profiles.active=prod
In the test class, you can be used
@ActiveProfiles("dev")
conveniently activated configuration.
Otherwise
There are many ways to set properties:
- DispatcherServlet initialization parameters.
- Web application context parameter.
- JNDI entries.
- Environment variables.
- JVM system properties.
- The integrated test class, using @ActiveProfiles annotation setting.
Embodiment, web.xml
configuration.
<!-- ... -->
<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>dev</param-value>
</context-param>
<!-- ... -->
Appendix: The configuration file is how related?
How to find the program .properties
file?
In app project, we passed @PropertySource
notes to JavaConfig class, set the .properties
path to the configuration file.
In gradle project, the configuration file in
src/main/resources/
path, can also be placed in this file in the directory folder. Such as:src/main/resources/demo/app.properties
setting@PropertySource("demo/app.properties")
.
In a web project, spring web has a profile set up, no @PropertySource
configuration.
Conditioned Bean
If you want to achieve will be created after a bean contains only when creating a specific library in the classpath of the application, or if you want a bean will be created only when a particular bean declaration after another, or set a specific environment variable a bean.
By @Conditional
annotation, which can be used with @Bean
the method of annotations. If the condition is created bean is true, otherwise the bean will be ignored.
Conditioned bean is
spring boot
to achieve the principles of automatic assembly.
Examples
When the configuration file contains a "reader" value, create bean.
Directory Structure
.
├── build.gradle
└── src/
├── main/
│ ├── java/
│ │ └── com/
│ │ └── yww/
│ │ ├── Main.java
│ │ ├── Message.java
│ │ ├── ReaderConfig.java
│ │ ├── ReaderExistsCondition.java
│ │ └── Reader.java
│ └── resources/
│ ├── application.properties
└── test/
├── java/
│ └── com/
│ └── yww/
│ └── MainTest.java
└── resources/
Key Code
Cancel the Reader.java
class @Component
notes, convenient here to create conditions of use of the Reader mode of bean.
Conditions set for ReaderExistsCondition.java
this class inherits the interface Condition
.
package com.yww;
import org.springframework.context.annotation.*;
@Configuration
@ComponentScan
@PropertySource("application.properties")
public class ReaderConfig {
@Bean
public Message message1(){
Message message = new Message();
message.setMsg("---hi---");
return message;
}
@Bean
@Conditional(ReaderExistsCondition.class)
public Reader reader(Message message){
return new Reader(message);
}
}
The conditions set for, to find out whether there is named from the configuration file reader
configuration properties, if the method matches
returns true, which will be set up to create conditions of bean.
package com.yww;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class ReaderExistsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment env = context.getEnvironment();
return env.containsProperty("reader");
}
}
Appendix: Interface Condition
Condition
Interface source code below, contains only one matches
method, if the method returns true
That will satisfy the conditions created bean, false
that is not created.
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
ConditionContext
You can check the interface Bean, environment variables, resources and classes.
AnnotatedTypeMetadata
Interface can be checked with@Bean
a method annotated what other notes.
Ambiguity automatic assembly process
When a plurality of the same type bean is created, the component can not be selected by the scan assembly.
spring throws
NoUniqueBeanDefinitionException
.
Solution
There are two processing methods:
- In
@Component
or@Bean
on the use of@Primary
annotations, in the face of ambiguity, select the preferred bean. - In
@Autowired
or@Inject
on the use of@Qualifier
defining the bean to be injectedID
. When the notes to@Component
, or@Bean
when, it is to create your own qualifier (similar renamed the bean ID).
@Qualifier
Defining a name tag provided similar to bean, spring loading will meet bean tag.
To set a plurality of tags, you can define your own annotated
@Qualifier
interface, as with a custom label annotation is defined. (Java 8 allows the definition to add annotations@Repeatable
to achieve repeat the comments, but Spring is@Qualifier
not added to this repeat annotations.)
Annex: byName and byType
In general, although the bean and injection parameters were created not the same (here the bean name message1
and parameter name message
), but spring will be byType
a way to find the same type of bean assembled.
package com.yww;
import org.springframework.context.annotation.*;
@Configuration
@ComponentScan
public class ReaderConfig {
@Bean
public Message message1(){
Message message = new Message();
message.setMsg("---hi---1");
return message;
}
@Bean
public Reader reader(Message message){
return new Reader(message);
}
}
But when there are multiple different types of bean, you can not rely on byType
to find the corresponding bean, by changing the parameters passed name (here use parameter names message2
), the use of byName
designated bean.
package com.yww;
import org.springframework.context.annotation.*;
@Configuration
@ComponentScan
public class ReaderConfig {
@Bean
public Message message1(){
Message message = new Message();
message.setMsg("---hi---1");
return message;
}
@Bean
public Message message2(){
Message message = new Message();
message.setMsg("---hi---2");
return message;
}
@Bean
public Reader reader(Message message2){
return new Reader(message2);
}
}
External value injection
Environmental values can be obtained in three ways:
- Implantation using
Environment
. - Placeholder property (property placeholder).
- Spring expression language
SpEL
.
#src/main/resources/application.properties
info.message=this is a message.
info.counter=10
Environment:
import org.springframework.context.annotation.*;
import org.springframework.core.env.Environment;
@Component
@PropertySource("application.properties")
public class Message {
@Autowired
Environment env;
public void print(){
String msgStr = env.getProperty("info.message", "this is a msg");
int msgInt = env.getProperty("info.counter", Integer.class, 30);
}
}
Property placeholder:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
@Component
@PropertySource("application.properties")
public class Message {
@Value("${info.message}")
private String msg;
}
Spring EL:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
@Component
@PropertySource("application.properties")
public class Message {
@Value("#{systemProperties['info.message']}")
private String msg;
}