Spring IOC (11)

Big white goose breeding base 2020-11-06 20:59:00
spring ioc


1.13.Environment

Environment An interface is an abstract existence integrated in a container , It manifests itself in two key aspects of the application environment :profiles and properties.

1.13.1.Bean Definition Profiles

Bean Definition Profiles A mechanism is provided in the core container , The mechanism allows for different environment Register different Bean.

 To put it bluntly, it's actually judgment spring.profiles.active Value
This value can have multiple intermediate uses , Just separate it

“environment” The word "one" may mean different meanings for different users , And this feature can help in many use cases , Include :

  • Work on the data source in memory in the development , It's not going on QA Or produce from JNDI Find the same data source .
  • Register the monitoring infrastructure only when the application is deployed to a performance environment .
  • For customers A And the customer B Deployment registration bean Custom implementation of .

Consider the first use case that needs to be used in practice DataSource.
In the test environment , The configuration may be similar to the following :

@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("my-schema.sql")
.addScript("my-test-data.sql")
.build();
}

Now? , Assume that the application's data source is already in the production application server's JNDI Register in the directory , Consider how to deploy the application to QA Or in a production environment .
Now? , our dataSource bean It looks like the list below :

@Bean(destroyMethod="")
public DataSource dataSource() throws Exception {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}

The problem is how to switch between using these two variants depending on the current environment .
Over time ,Spring Users have designed many ways to do this , It usually depends on system environment variables and <import/> contain ${placeholder} Of XML The combination of sentences , these ${placeholder} Resolve to the correct configuration file path according to the value of the environment variable .

Bean Definition Profiles Is a core container function , Can provide a solution to this problem .

Use @Profile

@Profile Annotations register the component only when one or more of the specified profiles you specify is active .
Use the previous example , We can rewrite the data source configuration , As shown below :

@Configuration
@Profile("development")
public class StandaloneDataConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
}
@Configuration
@Profile("production")
public class JndiDataConfig {
@Bean(destroyMethod="")
public DataSource dataSource() throws Exception {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
}
 As mentioned earlier , about @Bean Method , You usually choose to use programming JNDI lookup , The method is to use Spring Of JNDIMplate/JNDilocatorDeleteGate helper ,
Or use the direct JNDIInitialContext usage ,
instead of JndiObjectFactoryBean Variable , because factoryBean Method returns FactoryBean type , instead of DataSource type .
 Explanation of principle :
1.@Profile The note specifies @Conditional In the annotations ProfileCondition.class
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {
/**
* The set of profiles for which the annotated component should be registered.
*/
String[] value();
}
2. First load bean I found that there was a way to judge whether the current should be adjusted bean
if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
configClass.skippedBeanMethods.add(methodName);
return;
}
stay shouldSkip Will query the current bean All of condition
And loop through each condition Of matches
and @Profile Of condition Of matches As shown below
class ProfileCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (Object value : attrs.get("value")) {
// here Called environment Of propertySources
// Determine all the... In the current configuration propertySources Does it contain spring.profiles.active attribute 
// If it has value, set it to environment Of activeProfiles Properties of the 
// Then judge the current class @Profile Is the value in the annotation included in the activeProfiles Within the properties 
// If included, return true
if (context.getEnvironment().acceptsProfiles(Profiles.of((String[]) value))) {
return true;
}
}
return false;
}
return true;
}
}

The profile string can contain a simple profile name ( for example production) Or profile expression .
Profile expressions allow for more complex profile logic to be expressed ( for example production & us-east).
The following operators are supported in profile expressions :

  • !: The logic of the configuration file “ Not ”
  • &: The logic of the configuration file “ And ”
  • |: The logic of the configuration file “ or ”
 You can't mix... Without using parentheses & and | Operator .
for example , production & us-east | eu-central Not a valid expression .
It has to be expressed as production & (us-east | eu-central).

You can @Profile Used as a meta annotation , To create custom composite annotations .

The following example defines a custom @Production Comments , You can use it as @Profile("production") substitute

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Profile("production")
public @interface Production {
}
 If one @Configuration Class is marked with a @Profile, Unless one or more of the specified profiles are active ,
Otherwise, all... Associated with the class will be ignored @Bean Methods and @Import annotation .
If one @Component or @Configuration Class tags are @Profile({"p1", "p2"}),
Unless the profile has been activated “ p1” or “p2”, Otherwise, the class will not be registered or processed .
If a given configuration file uses NOT Operator (!) The prefix ,
Annotated elements are registered only when the profile is not active .
for example , Given @Profile({"p1", "!p2"}),
If the profile “ p1” Active or profile “p2” Not active ,
You will register .

@Profile You can also declare at the method level , Scope is only a specific configuration class Bean, This is shown in the following example :

@Configuration
public class AppConfig {
@Bean("dataSource")
@Profile("development")
public DataSource standaloneDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
@Bean("dataSource")
@Profile("production")
public DataSource jndiDataSource() throws Exception {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
}
  • The standaloneDataSource The method is only in development Available in the configuration file .
  • The jndiDataSource The method is only in production Available in the configuration file .

about @Bean Method @Profile, A special scenario may be applied ( In the same configuration class ):
----- For having the same Java Overload of method names @Bean Method ( Similar to constructor overloading ), You need to declare the same on all overloaded methods @Profile Conditions , Declaring the same condition is not because you can automatically select overloaded methods , It is because this batch of overloaded methods will all fail because the first method fails the verification , If the first one is qualified, we will continue to judge whether we can use other overload methods bean Registration of .
----- If the conditions are inconsistent , Only the first declared condition in the overloaded method takes effect .
therefore ,@Profile Cannot be used to select overloaded methods with specific parameter signatures .
same bean The resolution between all factory methods follows the rules of creation Spring The constructor parsing algorithm of .

If you want to define alternatives with different profile conditions bean, Please use the same point to bean Different names @Bean Method name , The method is to use @Bean Of name attribute , As shown in the previous example .

If the parameter signatures are the same ( for example , None of the variables have arg Factory method ), So this is in an effective Java Class is the only method that first represents this arrangement ( Because there can only be a method with a specific name and parameter signature ).

 analysis :
// Judge the present @Bean Do you need to skip 
// The order of judgment here is Config Class @Bean The order of the code follows @Order irrelevant 
if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
configClass.skippedBeanMethods.add(methodName);
return;
}
// When a method with the same name appears and is skipped before Here we will judge skippedBeanMethods Property contains and directly skips 
// Therefore, no matter whether the subsequent method with the same name in the same configuration class is annotated or not, it will not be processed any more 
if (configClass.skippedBeanMethods.contains(methodName)) {
return;
}

XML Bean Define configuration file XML The corresponding term is the element profile attribute <beans>.
Our previous example configuration can use two XML File rewriting , As shown below :

<beans profile="development"
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="...">
<jdbc:embedded-database id="dataSource">
<jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
<jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
</jdbc:embedded-database>
</beans>
<beans profile="production"
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="...">
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
</beans>

It can also be avoided <beans/> Split and nest elements in the same file , This is shown in the following example :

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="...">
<!-- other bean definitions -->
<beans profile="development">
<jdbc:embedded-database id="dataSource">
<jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
<jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
</jdbc:embedded-database>
</beans>
<beans profile="production">
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
</beans>
</beans>

spring-bean.xsd Restrictions have been made , Only such elements are allowed as the last element in the file . This will help to provide flexibility , And it doesn't lead to XML The confusion of documents .

XML The corresponding item does not support the profile expression described earlier
----- for example :(production & (us-east | eu-central)).
however , By using ! Operator to cancel the configuration file .
You can also apply logic by nesting configuration files “ And ”, This is shown in the following example :

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="...">
<!--- If production and us-east Configuration files are all active , be dataSource Will be registered -->
<beans profile="production">
<beans profile="us-east">
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
</beans>
</beans>
</beans>
Default Profile

The default profile represents the profile enabled by default . Consider the following example :

@Configuration
@Profile("default")
public class DefaultDataConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.build();
}
}

If no profile is activated ,dataSource Be created .
You can see that this is for one or more bean A way to provide default definitions .

If any configuration files are enabled , The default profile does not apply .

You can setDefaultProfiles() Or use declaratively spring.profiles.default Property to change the name of the default profile .

1.13.2.PropertySource Abstraction

Spring Of Environment Abstraction provides search operations for configurable hierarchies for attribute sources .

ApplicationContext ctx = new GenericApplicationContext();
Environment env = ctx.getEnvironment();
boolean containsMyProperty = env.containsProperty("my-property");
System.out.println("Does my environment contain the 'my-property' property? " + containsMyProperty);

In the previous snippet , We see a way to ask Spring Whether this is defined for the current environment my-property attribute .

To answer this question ,Environment The objects are in a group PropertySource Object to perform a search on .

PropertySource It's for any key - Simple abstraction of value to source ,Spring Of StandardEnvironment There are two configurations PropertySource object

  • A representative JVM System property set (System.getProperties())
  • One represents the set of system environment variables (System.getenv()).
 These default attribute sources are StandardEnvironment Provided , For stand-alone applications .
StandardServletEnvironment Additional default attribute sources are used , Include servlet Configuration and servlet Context parameters .
It has the option to enable JndiPropertySource.
 The search performed is hierarchical .
By default , System properties take precedence over environment variables .
therefore , If you're calling env.getProperty(“my-property”) period , It happens to be set in both places my-property attribute , Then the system property value “ Win ” And be returned to .
Be careful , Attribute values are not merged , It's completely covered by the previous entry .
about common StandardServletEnvironment, The complete hierarchy is shown below , The highest priority item is at the top :
ServletConfig Parameters ( If applicable —— for example , stay DispatcherServlet In the context )
ServletContext Parameters (web.xml Context parameter item )
JNDI environment variable (java:comp/env/ entries)
JVM System attribute (-D Command line arguments )
JVM System environment ( Operating system environment variables )

most important of all , The whole mechanism is configurable .
Maybe you have a custom property source to integrate into this search .
So , Please implement and instantiate your own instance PropertySource And add it to PropertySourcescurrent The collection of Environment.
The following example shows how to do this :

ConfigurableApplicationContext ctx = new GenericApplicationContext();
MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
//MyPropertySource Has been added the highest priority . 
sources.addFirst(new MyPropertySource());

The MutablePropertySources API A lot of methods have been made public , These methods allow precise manipulation of attribute source sets .

1.13.3. Use @PropertySource

@PropertySource Annotations provide a convenient declaration mechanism , Can be PropertySource Add to Spring In the environment of .

Give a name to app.properties The file of ,
It contains key value pairs testbean.name=myTestBean,
Below @Configuration Class uses @PropertySource,
call env.getProperty("testbean.name") Returns the myTestBean:

@Configuration
@PropertySource("classpath:/com/myco/app.properties")
public class AppConfig {
@Autowired
Environment env;
@Bean
public TestBean testBean() {
TestBean testBean = new TestBean();
testBean.setName(env.getProperty("testbean.name"));
return testBean;
}
}

Anything that appears in @PropertySource Resource location ${…} The placeholders are parsed according to the attribute source that has been registered in the environment , As the following example shows :

// Assume my.placeholder Exists in one of the registered attribute sources ( for example , System properties or environment variables ) in , The placeholder resolves to the corresponding value . 
// If not , be default/path Use as default value . 
// If no default value is specified and the property cannot be resolved , IllegalArgumentException Throw out .
@Configuration
@PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties")
public class AppConfig {
@Autowired
Environment env;
@Bean
public TestBean testBean() {
TestBean testBean = new TestBean();
testBean.setName(env.getProperty("testbean.name"));
return testBean;
}
}
 according to Java 8 The agreement of ,@PropertySource Annotations are repeatable .
however , All of these @PropertySource Annotations need to be declared at the same level , Or declare it directly on the configuration class , Or declare it as a meta annotation in the same custom annotation .
Mixing direct and meta annotations is not recommended , Because direct annotations effectively override meta annotations .

1.13.4. Placeholder resolution in declaration In the past , The value of a placeholder in an element can only be based on JVM Parsing environment variables or system properties .
Now the situation is different .
Because the environment abstraction is integrated into the whole container , So it's easy to parse the placeholder through it .
This means that you can configure the parsing process in any way you like .
You can change the priority of search system properties and environment variables , Or delete them completely .
You can also add your own attribute sources where appropriate .

To be specific , No matter where the customer attributes are defined , As long as it's available in the environment , All of the following statements apply :

<beans>
<import resource="com/bank/service/${customer}-config.xml"/>
</beans>
1.15.ApplicationContext Other functions of

As discussed in the introduction ,
org.springframework.beans.factory Packages provide management and operation bean Basic functions of , Including programming .
org.springframework.context The package added ApplicationContext Interface , The interface extends BeanFactory Interface ,
In addition, other interfaces are extended , Provide additional functionality in a more application oriented style .

Many people use... In a completely declarative way ApplicationContext, It's not even programmed to create it , It depends on the support class ( Such as ContextLoader) From the dynamic instantiation of a ApplicationContext, As Java EE web Part of the normal startup process of an application .

To enhance in a more framework oriented style BeanFactory The function of , The context pack also provides the following functions :

  • adopt MessageSource Interface access i18n Style news .
  • adopt ResourceLoader Interface access resources , for example url And documents .
  • Event publishing , By using ApplicationEventPublisher Interface published to implementation ApplicationListener Interface bean.
  • adopt HierarchicalBeanFactory Interface loads multiple ( A layered ) Context , Let each context focus on a particular layer , For example, applications web layer .
1.15.1. International use MessageSource

ApplicationContext The interface extends an extension called MessageSource The interface of , So it provides internationalization (“i18n”) function .
Spring It also provides HierarchicalMessageSource Interface , This interface can parse messages hierarchically .

Together, these interfaces provide Spring The basis of realizing message parsing .

Methods defined on these interfaces include :

  • String getMessage(String code, Object[] args, String default, Locale loc):
    For from MessageSource The basic way to retrieve information .
    If the message for the specified locale is not found , The default message is used .
    By using the standard library MessageFormat function , Any parameter passed in will be the replacement value .
  • String getMessage(String code, Object[] args, Locale loc):
    It's essentially the same as the previous method , But there's a difference : Cannot specify default message .
    If no message is found , Throw out NoSuchMessageException.
  • String getMessage(MessageSourceResolvable, Locale Locale): All the properties used in the previous method are also wrapped in a name called MessageSourceResolvable Class , Can be used with this method .

load ApplicationContext when , It automatically searches for the context defined MessageSource bean.
bean The name of must be messageSource.

  • If you find one like this bean, All calls to the previous methods are delegated to the message source .
  • If you don't find the source ,ApplicationContext An attempt will be made to find the name containing bean The parent source of . If it is , Then use the bean As a source of information .
  • If ApplicationContext No source found , Then instantiate an empty DelegatingMessageSource, To be able to accept calls to the methods defined above .

Spring Two message source implementations are provided :
ResourceBundleMessageSource and StaticMessageSource.

Both are realized HierarchicalMessageSource To perform nested messaging .
Rarely used StaticMessageSource, But it provides a programming way to add messages to the source .

The following example shows ResourceBundleMessageSource:

<beans>
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>format</value>
<value>exceptions</value>
<value>windows</value>
</list>
</property>
</bean>
</beans>

This example assumes that you have three so-called resource bundles format.properties,exceptions.properties,windows.properties Define... In the classpath .
Any request to parse the message goes through JDK The adoption of standards ResourceBundle Object to parse the message to handle .
For the purposes of this example , Suppose the contents of the above two resource bundle files are as follows :

# stay format.properties in
message=Alligators rock!
# stay exceptions.properties in
argument.required=The {0} argument is required.

The next example shows running this MessageSource Function of the program .
please remember , all ApplicationContext The realization is also MessageSource Realization , So it can be cast to MessageSource Interface .

public static void main(String[] args) {
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
String message = resources.getMessage("message", null, "Default", Locale.ENGLISH);
System.out.println(message);
}

The output of the above program is as follows :

Alligators rock!

All in all ,MessageSource It's in a name called beans.xml As defined in the document , It exists in classpath in .
MessageSource bean Define by its basenames Attribute refers to a large number of resource packages .
Pass to in the list basenames Attribute exists as the root file of the classpath , They are called format.properties,exceptions.properties,and windows.properties.

The next example shows the parameters passed to the message lookup .

These parameters are converted to string objects , And insert it into a place holder in the find message .

<beans>
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="exceptions"/>
</bean>
<bean id="example" class="com.something.Example">
<property name="messages" ref="messageSource"/>
</bean>
</beans>
public class Example {
private MessageSource messages;
public void setMessages(MessageSource messages) {
this.messages = messages;
}
public void execute() {
String message = this.messages.getMessage("argument.required",
new Object [] {"userDao"}, "Required", Locale.ENGLISH);
System.out.println(message);
}
}

execute() The result of the method call is as follows :

The userDao argument is required.

About internationalization (“i18n”),Spring Various MessageSource Achieve compliance and standards JDK ResourceBundle The same locale resolution and fallback rules .
In short , Continue with the example defined earlier messageSource, If you wish according to the UK (en-GB) Regional resolution message , You will create a format_en_GB.properties, exceptions_en_GB.properties, and windows_en_GB.properties.

Usually , Locale resolution is managed by the environment around the application .
In the following example , Manually specify resolution ( The British ) The language context of the message :

# in exceptions_en_GB.properties
argument.required=Ebagum lad, the ''{0}'' argument is required, I say, required.
public static void main(final String[] args) {
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
String message = resources.getMessage("argument.required",
new Object [] {"userDao"}, "Required", Locale.UK);
System.out.println(message);
}

The output of running the above program is as follows :

Ebagum lad, the 'userDao' argument is required, I say, required.

You can also use it MessageSourceAware Interface to get a reference to any defined message source .
When creating and configuring bean when , stay ApplicationContext As defined in bean Realization MessageSourceAware Interfaces are injected into the application context MessageSourceAware Interface .

 As ResourceBundleMessageSource alternatives ,Spring Provides a ReloadableResourceBundleMessageSource class .
This variant supports the same bundle File format , But it's based on JDK Standards for ResourceBundleMessageSource More flexible .
especially , It allows from any Spring Resource location read file ( It's not just from the classpath ), And support bundle Hot reload of properties file ( And cache them effectively at the same time ).
For more information , Please see the ReloadableResourceBundleMessageSource javadoc.
1.15.2. Standard and custom events

ApplicationContext Event handling in is done by ApplicationEvent Classes and ApplicationListener Interface provided .
If you realize ApplicationListener Interface bean Deployed into context , So whenever one ApplicationEvent Published to ApplicationContext when , The bean You will be informed .
Essentially , This is the standard observer design pattern .

 from Spring 4.2 Start , The event infrastructure has been significantly improved , And provides an annotation based model ,
And the ability to post arbitrary events ( in other words , No need to ApplicationEvent Extended objects ).
When publishing such an object , We wrap it in the event for you .

surface 7. Built in events

event explain
ContextRefreshedEvent In initialization or refresh ApplicationContext Time release <br> for example ,ConfigurableApplicationContext.refresh() Method <br><br> here , Initialization means that all of the bean<br> Detects and activates the postprocessor bean<br> Pre instantiated singleton<br> also ApplicationContext Object can be used .<br><br> As long as the context is not closed <br> And the chosen ApplicationContext Actually support this kind of “ heat ” Refresh <br> You can trigger refresh several times <br> for example ,XmlWebApplicationContext Support hot refresh ,<br> but GenericApplicationContext I won't support it .
ContextStartedEvent stay ConfigurableApplicationContext.start() Method <br> start-up ApplicationContext Time release .<br><br> here , Start up means all life cycles bean Both receive an explicit start signal .<br> Usually , This signal is used to restart after an explicit stop bean<br>, But it can also be used to start components that have not been configured for automatic start <br> for example , Components that have not been started at initialization time .
ContextStoppedEvent stay ConfigurableApplicationContext.stop() Method <br> stop it ApplicationContext Time release .<br><br>“ stop it ” It means all life cycles bean Both receive an explicit stop signal .<br> Stop context can be done by start() Call restart .
ContextClosedEvent By using ConfigurableApplicationContext.close() Method <br> Or through JVM shutdown hook close ApplicationContext Time release .<br><br>,“ close ” It means all the singletons bean Will be destroyed .<br> Once the context is closed ,<br> It will reach the end of life , Unable to refresh or restart .
RequestHandledEvent One is specific to web Events ,<br> Tell all bean One HTTP The request has been served .<br> This event is published after the request is completed .<br> This event only applies to the use of Spring Of DispatcherServlet Of web Applications .
ServletRequestHandledEvent Subclasses of this class RequestHandledEvent<br> Added Servlet-specific The context of .

You can also create and publish your own custom events .
The following example shows a simple class , This class extends Spring Of ApplicationEvent Base class :

public class BlockedListEvent extends ApplicationEvent {
private final String address;
private final String content;
public BlockedListEvent(Object source, String address, String content) {
super(source);
this.address = address;
this.content = content;
}
// accessor and other methods...
}

To publish a custom ApplicationEvent, Please be there. ApplicationEventPublisher On the call publishEvent() Method .

Usually , This is done by creating an implementation ApplicationEventPublisherAware And register as Spring bean Class to complete .

The following example shows this kind of :

public class EmailService implements ApplicationEventPublisherAware {
private List<String> blockedList;
private ApplicationEventPublisher publisher;
public void setBlockedList(List<String> blockedList) {
this.blockedList = blockedList;
}
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public void sendEmail(String address, String content) {
if (blockedList.contains(address)) {
publisher.publishEvent(new BlockedListEvent(this, address, content));
return;
}
// send email...
}
}

When in the configuration ,Spring The container detected the ApplicationEventPublisherAware Realization EmailService And automatically call setApplicationEventPublisher().

actually , The parameter passed in is Spring The container itself .
You are passing through its ApplicationEventPublisher The interface interacts with the application context .

To receive custom ApplicationEvent, You can create an implementation ApplicationListener And register as Spring bean Class .
The following example shows this kind of :

public class BlockedListNotifier implements ApplicationListener<BlockedListEvent> {
private String notificationAddress;
public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}
public void onApplicationEvent(BlockedListEvent event) {
// notify appropriate parties via notificationAddress...
}
}

Be careful ,ApplicationListener It's usually parameterized with the type of a custom event ( In the previous example BlockedListEvent).
It means onApplicationEvent() Method can keep type safe , Avoid downward coercion .
You can register any number of event listeners , But notice , By default , Event listeners receive events synchronously .
It means publishEvent() Method will block , Until all listeners have finished processing the event .
One of the advantages of this synchronous and single threaded approach is , When the listener receives the event , If the transaction context is available , It will operate within the transactional context of the publisher .

The following example shows the... Used to register and configure each of the above classes bean Definition :

<!-- When calling emailService bean Of sendEmail() When the method is used ,
If there are any email messages that need to be blocked ,
Then the release type is BlockedListEvent Custom events for . -->
<bean id="emailService" class="example.EmailService">
<property name="blockedList">
<list>
<value>known.spammer@example.org</value>
<value>known.hacker@example.org</value>
<value>john.doe@example.org</value>
</list>
</property>
</bean>
<!--blockedListNotifier
bean Register as a ApplicationListener And receive BlockedListEvent,
At this point it can inform the appropriate party .-->
<bean id="blockedListNotifier" class="example.BlockedListNotifier">
<property name="notificationAddress" value="blockedlist@example.org"/>
</bean>
Spring The event mechanism of is for Spring bean Designed for simple communication between .
However , For more complex enterprise integration requirements ,
Independently maintained Spring integration The project provides a lightweight build 、 Pattern oriented 、 Full support for event driven architecture ,
These architectures are built on what is known as Spring On the programming model .
Annotation based event listeners

from Spring 4.2 Start , You can use @EventListener Annotations are hosted Bean Register an event listener on any public method of .

The BlockedListNotifier It can be rewritten as follows :

public class BlockedListNotifier {
private String notificationAddress;
public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}
@EventListener
public void processBlockedListEvent(BlockedListEvent event) {
// notify appropriate parties via notificationAddress...
}
}

The method signature declares again the type of event it listens to , But this time with a flexible name , And there is no specific listener interface implemented .
As long as the actual event type resolves generic parameters in its implementation hierarchy , You can narrow the event type by generics .

If your method should listen for multiple events , Or you want to define it without using any parameters , You can also specify the event type on the annotation itself .

The following example shows how to do this :

@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
// ...
}

You can also use definitions SpEL Conditional properties of the expression's annotations to add additional runtime filtering , The annotation should match the actual method call for a specific event .

The following example shows how our notification program is rewritten , Only in content Attribute is equal to the my-event Is called :

@EventListener(condition = "#blEvent.content == 'my-event'")
public void processBlockedListEvent(BlockedListEvent blockedListEvent) {
// notify appropriate parties via notificationAddress...
}

Every SpEL Expressions are evaluated against specific contexts . The following table lists the items available for context , So that you can use them for conditional event handling :

surface 8. Event SpEL Available metadata

name Example
Event #root.event or event
Parameters of the array #root.args or args;<br> args[0] Access to the first parameter, etc .
Parameter name #blEvent or #a0<br>( You can also use it #p0 or #p<#arg> Parameter representation as an alias )
 Please note that ,root.event Allows you to access the underlying events , Even if your method signature actually references any published object .

If you need to post an event as a result of handling another event , You can change the method signature to return the event that should be published , As the following example shows :

@EventListener
public ListUpdateEvent handleBlockedListEvent(BlockedListEvent event) {
// notify appropriate parties via notificationAddress and
// then publish a ListUpdateEvent...
}
asynchronous listeners. This feature is not supported .

This new method deals with each one BlockedListEvent Events will release a new event ListUpdateEvent.
If you need to publish multiple events , You can return a Collection event .

asynchronous listeners Asynchronous listeners

If you need a specific listener to handle events asynchronously , You can reuse regular @Async Support .
The following example shows how to do this :

@EventListener
@Async
public void processBlockedListEvent(BlockedListEvent event) {
// BlockedListEvent is processed in a separate thread
}

When using asynchronous events , Please note the following restrictions :

  • If the asynchronous event listener throws Exception, It doesn't spread to the caller .
  • An asynchronous event listener method cannot publish subsequent events with a return value . If you need to publish another event as a result of processing , Please insert a ApplicationEventPublisher Manually publish Events .
Ordering Listeners

If you need to call a listener first , Then you can put @Order Annotations are added to the method declaration ,
This is shown in the following example :

@EventListener
@Order(42)
public void processBlockedListEvent(BlockedListEvent event) {
// notify appropriate parties via notificationAddress...
}
General events

You can also use generics to further define the structure of events .
Consider using EntityCreatedEvent<T>, among T Is the type of actual entity created .
for example , You can create the following listener definitions to receive for only one person EntityCreatedEvent:

@EventListener
public void onPersonCreated(EntityCreatedEvent<Person> event) {
// ...
}

Because of type erasure , Only when the triggered event resolves the general parameters that the event listener is based on
( namely class PersonCreatedEvent extends EntityCreatedEvent<Person> { …​ }) when , This method works .

In some cases , If all events follow the same structure ( The same should be true of the events in the previous example ), So it can be very boring .
under these circumstances , You can achieve ResolvableTypeProvider To guide the framework provided by the runtime environment .
The following events show how to do this :

public class EntityCreatedEvent<T> extends ApplicationEvent implements ResolvableTypeProvider {
public EntityCreatedEvent(T entity) {
super(entity);
}
@Override
public ResolvableType getResolvableType() {
return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getSource()));
}
}
 It doesn't just apply to ApplicationEvent Any object sent as an event , And it applies to the object .
1.15.3. Easy access to low-level resources

To optimize the use and understanding of application context , You should be familiar with Spring Of Resource class , As mentioned in resources .

The application context is a ResourceLoader, Can be used to load resource objects .

Resource Essentially, JDK java.net.URL Rich versions of classes .
in fact ,Resource The implementation of encapsulates a java.net.URL Example .

Resource You can get the underlying resources from almost anywhere transparently , Including Classpaths 、 File system location 、 Available standards URL Any location described , And other variants .

If Resource The location string is a simple path without any special prefix , So the source of these resources is specific , And suitable for the actual application context type .

You can configure bean, To implement a special callback interface ResourceLoaderAware, Automatic callback when initializing , And the application context itself acts as ResourceLoader Pass in .
You can also make public Resource Properties of type , To access static resources .Resource Like any other attribute, it can be injected .

You can specify these resource properties as simple string paths , And deploy bean Depends on the automatic conversion from these text strings to the actual resource object .

Provide to ApplicationContext The location path or path of the constructor is actually a resource string , And in a simple form , Implement the appropriate processing according to the specific context .
for example ,ClassPathXmlApplicationContext Consider a simple location path as a classpath location .
You can also use location paths with special prefixes ( Resource string ) To force from the classpath or URL Load definition , Regardless of the actual context type .

1.15.4. Application startup trace

ApplicationContext management Spring Application lifecycle , And provide a rich programming model around components .
therefore , A complex application may have the same complex component diagram and startup phase .

Tracking the start-up steps of an application using specific metrics can help understand where the startup phase is spent , It can also be used as a way to better understand the whole context lifecycle .

AbstractApplicationContext( And its subclasses ) from ApplicationStartup testing , It's a collection of StartupStep data :

  • Application context lifecycle ( Basic package scanning , Configuration class management )
  • bean Life cycle ( Instantiation 、 Intelligent initialization 、 post-processing )
  • Application event handling

Here is AnnotationConfigApplicationContext An example of instrumentation in :

// Create and start a record 
StartupStep scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan");
// Add tag information to the current step 
scanPackages.tag("packages", () -> Arrays.toString(basePackages));
// Execute the actual phase we are measuring 
this.scanner.scan(basePackages);
// end 
scanPackages.end();
1.15.5.Web The convenience of the app ApplicationContext Instantiation

for example , have access to ContextLoader Create... Declaratively ApplicationContext example . Of course , You can also use ApplicationContext One of the implementations is to create... Programmatically ApplicationContext example .

You can use ContextLoaderListener To register a ApplicationContext, This is shown in the following example :

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

Monitor check contextConfigLocation Parameters .
If the parameter does not exist , The listener will use /WEB-INF/applicationContext.xml As default .

When parameters do exist , Listeners use predefined separators ( comma 、 Semicolons and blanks ) Delimited string , And use these values as a location to search for the application context .

Also support Ant The path pattern of style .
for example :
/WEB-INF/*Context.xml( For in WEB-INF The names in the catalogue are as follows Context All the files at the end )

/WEB-INF/**/*Context.xml( about WEB-INF All such files in any subdirectory )

1.16.1.BeanFactory or ApplicationContext?

This section explains BeanFactory and ApplicationContext Differences between container levels , And the meaning of guidance .

You should use ApplicationContext, Unless you have a good reason not to , Use GenericApplicationContext And its subclasses AnnotationConfigApplicationContext As a general implementation of custom guidance .
These are for all common purposes Spring The main entry point to the core container : Load profile 、 Trigger classpath scan 、 Register programmatically bean Definition and annotated classes , as well as ( from 5.0 Start ) Registration functionality bean Definition .

because ApplicationContext Contains BeanFactory All functions of , So it is generally recommended that it is superior to the ordinary BeanFactory, Unless you need to be right about bean Except for scenarios where full control is performed .

For many extended container features , Such as annotation processing and AOP agent ,BeanPostProcessor Extension points are essential . If you only use ordinary DefaultListableBeanFactory, By default, this postprocessor is not detected and activated .

The following table lists them BeanFactory and ApplicationContext Features provided by interfaces and implementations . surface 9. Function matrix

features BeanFactory ApplicationContext
Bean Instantiation / wiring Yes Yes
Integrated Lifecycle Management No Yes
Automatically BeanPostProcessor registration No Yes
Convenient source access ( For internalization ) No Yes
built-in ApplicationEvent Publishing mechanism No Yes

To use explicit registration Bean Post processor DefaultListableBeanFactory, You need to call... Programmatically addBeanPostProcessor, This is shown in the following example :

DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
factory.addBeanPostProcessor(new AutowiredAnnotationBeanPostProcessor());
factory.addBeanPostProcessor(new MyBeanPostProcessor());
// now start using the factory

You have to put one BeanFactoryPostProcessor Apply to a common DefaultListableBeanFactory in , You need to call it postProcessBeanFactory Method , As the following example shows :

DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(new FileSystemResource("beans.xml"));
// bring in some property values from a Properties file
PropertySourcesPlaceholderConfigurer cfg = new PropertySourcesPlaceholderConfigurer();
cfg.setLocation(new FileSystemResource("jdbc.properties"));
// now actually do the replacement
cfg.postProcessBeanFactory(factory);

As you can see, manual registration is very inconvenient , Especially relying on BeanFactoryPostProcessor and BeanPostProcessor When expanding functions .

 One AnnotationConfigApplicationContext After registering all public annotations, the processor , And maybe by configuring annotations ( Such as @EnableTransactionManagement) Introducing additional processors in the background .
stay Spring On the abstract layer of annotation based configuration model ,bean The concept of a postprocessor is only a detail of the inside of the container .
版权声明
本文为[Big white goose breeding base]所创,转载请带上原文链接,感谢

  1. 【计算机网络 12(1),尚学堂马士兵Java视频教程
  2. 【程序猿历程,史上最全的Java面试题集锦在这里
  3. 【程序猿历程(1),Javaweb视频教程百度云
  4. Notes on MySQL 45 lectures (1-7)
  5. [computer network 12 (1), Shang Xuetang Ma soldier java video tutorial
  6. The most complete collection of Java interview questions in history is here
  7. [process of program ape (1), JavaWeb video tutorial, baidu cloud
  8. Notes on MySQL 45 lectures (1-7)
  9. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  10. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  11. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  12. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  13. 【递归,Java传智播客笔记
  14. [recursion, Java intelligence podcast notes
  15. [adhere to painting for 386 days] the beginning of spring of 24 solar terms
  16. K8S系列第八篇(Service、EndPoints以及高可用kubeadm部署)
  17. K8s Series Part 8 (service, endpoints and high availability kubeadm deployment)
  18. 【重识 HTML (3),350道Java面试真题分享
  19. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  20. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  21. [re recognize HTML (3) and share 350 real Java interview questions
  22. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  23. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  24. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  25. RPC 1: how to develop RPC framework from scratch
  26. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  27. RPC 1: how to develop RPC framework from scratch
  28. 一次性捋清楚吧,对乱糟糟的,Spring事务扩展机制
  29. 一文彻底弄懂如何选择抽象类还是接口,连续四年百度Java岗必问面试题
  30. Redis常用命令
  31. 一双拖鞋引发的血案,狂神说Java系列笔记
  32. 一、mysql基础安装
  33. 一位程序员的独白:尽管我一生坎坷,Java框架面试基础
  34. Clear it all at once. For the messy, spring transaction extension mechanism
  35. A thorough understanding of how to choose abstract classes or interfaces, baidu Java post must ask interview questions for four consecutive years
  36. Redis common commands
  37. A pair of slippers triggered the murder, crazy God said java series notes
  38. 1、 MySQL basic installation
  39. Monologue of a programmer: despite my ups and downs in my life, Java framework is the foundation of interview
  40. 【大厂面试】三面三问Spring循环依赖,请一定要把这篇看完(建议收藏)
  41. 一线互联网企业中,springboot入门项目
  42. 一篇文带你入门SSM框架Spring开发,帮你快速拿Offer
  43. 【面试资料】Java全集、微服务、大数据、数据结构与算法、机器学习知识最全总结,283页pdf
  44. 【leetcode刷题】24.数组中重复的数字——Java版
  45. 【leetcode刷题】23.对称二叉树——Java版
  46. 【leetcode刷题】22.二叉树的中序遍历——Java版
  47. 【leetcode刷题】21.三数之和——Java版
  48. 【leetcode刷题】20.最长回文子串——Java版
  49. 【leetcode刷题】19.回文链表——Java版
  50. 【leetcode刷题】18.反转链表——Java版
  51. 【leetcode刷题】17.相交链表——Java&python版
  52. 【leetcode刷题】16.环形链表——Java版
  53. 【leetcode刷题】15.汉明距离——Java版
  54. 【leetcode刷题】14.找到所有数组中消失的数字——Java版
  55. 【leetcode刷题】13.比特位计数——Java版
  56. oracle控制用户权限命令
  57. 三年Java开发,继阿里,鲁班二期Java架构师
  58. Oracle必须要启动的服务
  59. 万字长文!深入剖析HashMap,Java基础笔试题大全带答案
  60. 一问Kafka就心慌?我却凭着这份,图灵学院vip课程百度云