Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What are the coding skills in Spring

2025-03-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article focuses on "what are the coding skills in Spring". Interested friends may wish to take a look. The method introduced in this paper is simple, fast and practical. Let's let the editor take you to learn "what are the code skills in Spring"?

one。 The power of @ Conditional

I don't know if you've ever encountered these problems:

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

A function needs to determine whether it is enabled or not based on whether there is a jar in the project.

The instantiation of a bean needs to determine whether another bean is instantiated, and then to determine whether or not to instantiate itself.

Whether a function is enabled or not, there is a parameter in the configuration file that can control it.

If you have encountered any of these problems, congratulations, this section is very suitable for you.

@ ConditionalOnClass

Problem 1 can be solved with the @ ConditionalOnClass annotation as follows:

Public class A {} public class B {} @ ConditionalOnClass (B.class) @ Configuration public class TestConfiguration {@ Bean public An a () {return new A ();}}

If class B exists in the project, class An is instantiated. If class B does not exist, class An is not instantiated.

Some people may ask: isn't it to judge whether there is a certain jar? How do you judge a class now?

It is easier to directly determine whether there is a key class under the jar.

This annotation has an upgraded application scenario: for example, a tool class mqTemplate for sending messages is written in the common project, and the common project is referenced in the business project. You only need to introduce the message middleware, such as rocketmq's jar package, to enable the mqTemplate function. If there is another business project, GM refers to the common project, and if you don't need the function of sending messages, you don't need to introduce rocketmq's jar package.

The function of this note is quite practical, isn't it?

@ ConditionalOnBean

Problem 2 can be solved with the @ ConditionalOnBean annotation as follows:

Configuration public class TestConfiguration {@ Bean public B b () {return new B ();} @ ConditionalOnBean (name= "b") @ Bean public An a () {return new A ();}}

Instance A can be instantiated only if instance B exists.

@ ConditionalOnProperty

Problem 3 can be solved with the @ ConditionalOnProperty annotation as follows:

ConditionalOnProperty (prefix = "demo", name= "enable", havingValue = "true", matchIfMissing=true) @ Configuration public class TestConfiguration {@ Bean public An a () {return new A ();}}

Configure parameters in the applicationContext.properties file:

Demo.enable=false

The meaning of each parameter:

Prefix represents the prefix of the parameter name, here is demo

Name represents the parameter name

HavingValue represents the specified value. The value configured in the parameter needs to be compared with the specified value for equality in order to meet the condition.

MatchIfMissing indicates whether the default configuration is allowed.

This function can be used as a switch, which is more elegant than the switch annotated by EnableXXX, because it can be turned on or off by parameter configuration, while the switch annotated by EnableXXX needs to be hard-coded on or off in the code.

Other Conditional comments

Of course, other Conditional annotations commonly used by spring are: ConditionalOnMissingClass, ConditionalOnMissingBean, ConditionalOnWebApplication, and so on.

Let's use a picture to get to know the @ Conditional family as a whole.

Custom Conditional

To be honest, I think the Conditional series that comes with springboot can already meet most of our needs. But if you have a special scene, you can also customize the Conditional.

The first step is to customize the comments:

@ Conditional (MyCondition.class) @ Retention (RetentionPolicy.RUNTIME) @ Target ({ElementType.TYPE, ElementType.METHOD}) @ Documented public @ interface MyConditionOnProperty {String name () default "; String havingValue () default";}

The second step is to implement Condition interface:

Public class MyCondition implements Condition {@ Override public boolean matches (ConditionContext context, AnnotatedTypeMetadata metadata) {System.out.println ("implement custom logic"); return false;}}

Third, use the @ MyConditionOnProperty annotation.

The secret of Conditional lies in the processConfigurationClass method of the ConfigurationClassParser class:

The logic of this method is not complex:

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

First determine whether Conditional annotations are used, if you do not return false directly

Collect condition into the collection

Sort the collection by order

Iterate through the collection, looping through the matchs method of condition.

two。 How to use @ Import?

Sometimes we need to introduce other classes into a configuration class, and the introduced classes are also added to the spring container. You can do this using the @ Import annotation at this point.

If you look at its source code, you will find that the introduced classes support three different types.

But I think it's best to separate the normal class from the @ Configuration annotated configuration class, so four different types are listed:

Ordinary class

This introduction is the simplest, and the introduced class will instantiate the bean object.

Public class A {} @ Import (A.class) @ Configuration public class TestConfiguration {}

By introducing class A through the @ Import annotation, spring can automatically instantiate the An object, and then inject it with the @ Autowired annotation where it is needed:

@ Autowired ivate An a

Isn't that surprising? Bean can be instantiated without the @ Bean annotation.

@ Configuration annotated configuration class

This introduction is the most complex because the @ Configuration annotation also supports a variety of combined annotations, such as:

@ Import

@ ImportResource

@ PropertySource, etc.

Public class A {} public class B {} @ Import (B.class) @ Configuration public class AConfiguration {@ Bean public An a () {return new A ();} @ Import (AConfiguration.class) @ Configuration public class TestConfiguration {}

If the configuration class annotated by @ Import is introduced into the @ Configuration annotation, the classes introduced by the @ Import, @ ImportResource, @ PropertySource and other annotations of the configuration class will be recursively introduced all at once.

Due to the limited space of the article, there will be a little suspense here. Later, there will be an article devoted to @ Configuration annotation, because it is so important.

Classes that implement the ImportSelector interface

This introduction requires the implementation of the ImportSelector interface:

Public class AImportSelector implements ImportSelector {private static final String CLASS_NAME = "com.sue.cache.service.test13.A"; public String [] selectImports (AnnotationMetadata importingClassMetadata) {return new String [] {CLASS_NAME};} @ Import (AImportSelector.class) @ Configuration public class TestConfiguration {}

The advantage of this approach is that the selectImports method returns an array, which means that multiple classes can be introduced at the same time, which is still very convenient.

Classes that implement the ImportBeanDefinitionRegistrar interface

This introduction requires the implementation of the ImportBeanDefinitionRegistrar interface:

Public class AImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {@ Override public void registerBeanDefinitions (AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {RootBeanDefinition rootBeanDefinition = new RootBeanDefinition (A.class); registry.registerBeanDefinition ("a", rootBeanDefinition);} @ Import (AImportBeanDefinitionRegistrar.class) @ Configuration public class TestConfiguration {}

This method is the most flexible, you can get the BeanDefinitionRegistry container registration object in the registerBeanDefinitions method, and you can manually control the creation and registration of the BeanDefinition.

Of course, the @ import annotation is very human and supports the introduction of many different types of classes at the same time.

@ Import ({B.class.AImportBeanDefinitionRegistrar.class}) @ Configuration public class TestConfiguration {}

These four ways of introducing classes have their own advantages, which can be summarized as follows:

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

A normal class for creating bean instances with no special requirements.

The configuration class annotated by @ Configuration is used for scenarios introduced by layers of nesting.

Classes that implement the ImportSelector interface are used in scenarios where multiple classes are introduced at once, or where different classes can be introduced according to different configuration decisions.

The class that implements the ImportBeanDefinitionRegistrar interface is mainly used in scenarios where the creation and registration of BeanDefinition can be controlled manually, and the BeanDefinitionRegistry registration container object can be obtained in its method.

You can see the processing logic in these three ways in the processImports method of the ConfigurationClassParser class:

The final else method actually contains two different processing logic: the normal class and the configuration class of the @ Configuration annotation.

three。 @ ConfigurationProperties assignment

It is very common for us to use configuration parameters in a project. For example, when we configure a thread pool, we need to define the following configuration in the applicationContext.propeties file:

Thread.pool.corePoolSize=5 thread.pool.maxPoolSize=10 thread.pool.queueCapacity=200 thread.pool.keepAliveSeconds=30

Method 1: read these configurations through the @ Value annotation.

Public class ThreadPoolConfig {@ Value ("${thread.pool.corePoolSize:5}") private int corePoolSize; @ Value ("${thread.pool.maxPoolSize:10}") private int maxPoolSize; @ Value ("${thread.pool.queueCapacity:200}") private int queueCapacity; @ Value ("${thread.pool.keepAliveSeconds:30}") private int keepAliveSeconds; @ Value ("${thread.pool.threadNamePrefix:ASYNC_}") private String threadNamePrefix @ Bean public Executor threadPoolExecutor () {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor (); executor.setCorePoolSize (corePoolSize); executor.setMaxPoolSize (maxPoolSize); executor.setQueueCapacity (queueCapacity); executor.setKeepAliveSeconds (keepAliveSeconds); executor.setThreadNamePrefix (threadNamePrefix); executor.setRejectedExecutionHandler (new ThreadPoolExecutor.CallerRunsPolicy ()); executor.initialize (); return executor;}}

This method is very simple to use, but it is recommended to add:, because: is followed by a default value, such as @ Value ("${thread.pool.corePoolSize:5}"), and the default number of core threads defined is 5.

If there is such a scenario: the ThreadPoolConfig class is defined under the business project, the api project refers to the business project, and the job project also refers to the business project, and the ThreadPoolConfig class is only intended to be used in the api project. At this point, if the default value is not configured, an error may be reported when the job project is started.

If there are fewer or more parameters, you need to annotate each parameter with @ Value. Isn't that a bit of a hassle?

In addition, there is another problem: the parameters defined by the @ Value annotation seem a bit scattered, making it difficult to tell which parameters are grouped together.

At this point, @ ConfigurationProperties comes in handy, which is a new comment in springboot.

The first step is to define the ThreadPoolProperties class

@ Data @ Component @ ConfigurationProperties ("thread.pool") public class ThreadPoolProperties {private int corePoolSize; private int maxPoolSize; private int queueCapacity; private int keepAliveSeconds; private String threadNamePrefix;}

Step two, use the ThreadPoolProperties class

@ Configuration public class ThreadPoolConfig {@ Autowired private ThreadPoolProperties threadPoolProperties; @ Bean public Executor threadPoolExecutor () {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor (); executor.setCorePoolSize (threadPoolProperties.getCorePoolSize ()); executor.setMaxPoolSize (threadPoolProperties.getMaxPoolSize ()); executor.setQueueCapacity (threadPoolProperties.getQueueCapacity ()); executor.setKeepAliveSeconds (threadPoolProperties.getKeepAliveSeconds ()); executor.setThreadNamePrefix (threadPoolProperties.getThreadNamePrefix ()) Executor.setRejectedExecutionHandler (new ThreadPoolExecutor.CallerRunsPolicy ()); executor.initialize (); return executor;}

With the @ ConfigurationProperties annotation, the parameters starting with thread.pool can be assigned directly to the parameters of the ThreadPoolProperties class with the same name, eliminating the process of manually corresponding one by one like the @ Value annotation.

This approach is obviously much more convenient, we just need to write the xxxProperties class, and spring will automatically assemble the parameters. In addition, different series of parameters can define different xxxProperties classes and are easy to manage, so it is recommended to give priority to this approach.

Its underlying layer is implemented by the ConfigurationPropertiesBindingPostProcessor class, which implements the BeanPostProcessor interface, parses the @ ConfigurationProperties annotation in the postProcessBeforeInitialization method, and binds the data to the corresponding object.

Binding is done through the bindObject method of the Binder class:

The above code recursively binds data, taking into account three main situations:

BindAggregate binding collection class

BindBean binding object

The first two cases of bindProperty binding parameters also end up calling the bindProperty method.

"in addition, a friendly reminder:"

There are problems in some scenarios using @ ConfigurationProperties annotation. For example, if a parameter is modified in apollo, normally it can be dynamically updated to the object of the xxxProperties class defined by the @ ConfigurationProperties annotation, but if more complex objects appear, such as:

Private Map urls

It may not be able to update dynamically.

What should I do at this time?

The answer is to use the ApolloConfigChangeListener listener to handle it yourself:

ConditionalOnClass (com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig.class) public class ApolloConfigurationAutoRefresh implements ApplicationContextAware {private ApplicationContext applicationContext; @ Override public void setApplicationContext (ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;} @ ApolloConfigChangeListener private void onChange (ConfigChangeEvent changeEvent {refreshConfig (changeEvent.changedKeys ()) } private void refreshConfig (Set changedKeys) {System.out.println ("Update the changed parameters to the appropriate object");}}

four。 How to avoid the pit of spring transaction?

The transaction functions in spring are mainly divided into declarative transaction and programmatic transaction.

Declarative transaction

In most cases, we may use more declarative transactions in the development process, that is, transactions defined with @ Transactional annotations, because it is simpler and easier to use.

Simply add the @ Transactional annotation to the transaction method that needs to be executed to start the transaction automatically:

@ Service public class UserService {@ Autowired private UserMapper userMapper; @ Transactional public void add (UserModel userModel) {userMapper.insertUser (userModel);}}

This declarative transaction works because its underlying layer uses AOP, creates a proxy object, and calls the TransactionInterceptor interceptor to implement the transaction function.

There is something special about spring transactions: the database connections it acquires are placed in ThreadLocal, that is, the same database connection can be obtained from beginning to end in the same thread, which ensures that multiple database operations in the same thread are performed in the same transaction.

Under normal circumstances, there is no problem, but if used improperly, the transaction will fail for the following main reasons:

In addition to the problems listed above, because the minimum granularity of the @ Transactional annotation is to be defined on the method, if there are multiple layers of transaction method calls, it may cause large transaction problems.

Therefore, it is recommended that you use less @ Transactional annotations to start transactions in practice.

Programmatic transaction

In general, we can turn on the transaction function through the TransactionTemplate class. The good news is that springboot has instantiated this object by default, and we can use it directly in the project.

@ Service public class UserService {@ Autowired private TransactionTemplate transactionTemplate;... Public void save (final User user) {transactionTemplate.execute ((status) = > {doSameThing...) Return Boolean.TRUE;})}}

Programmatic transactions using TransactionTemplate can avoid many transaction failures, but large transaction problems may not be solved, just that it is better than using @ Transactional annotations.

five。 Solutions to cross-domain problems

With regard to cross-domain problems, there are still many front-and back-end solutions. Here, I will focus on the solutions of spring. At present, there are three:

one。 Use @ CrossOrigin annotations

@ RequestMapping ("/ user") @ RestController public class UserController {@ CrossOrigin (origins = "http://localhost:8016") @ RequestMapping (" / getUser ") public String getUser (@ RequestParam (" name ") String name) {System.out.println (" name: "+ name); return" success ";}}

This solution needs to add @ CrossOrigin annotation to the interface for cross-domain access. The access rules can be controlled by the parameters in the annotation, and the control granularity is finer. You can use this scheme if the number of interfaces that require cross-domain access is small.

two。 Increase global configuration

@ Configuration public class WebConfig implements WebMvcConfigurer {@ Override public void addCorsMappings (CorsRegistry registry) {registry.addMapping ("/ * *") .allowedOrigins ("*") .allowedMethods ("GET", "POST") .allowCredentials (true) .maxAge (3600) .allowedHeaders ("*");}}

This scheme needs to implement the WebMvcConfigurer interface and rewrite the addCorsMappings method, in which the rules of cross-domain access are defined. This is a global configuration that can be applied to all interfaces.

three。 Custom filter

@ WebFilter ("corsFilter") @ Configuration public class CorsFilter implements Filter {@ Override public void init (FilterConfig filterConfig) throws ServletException {} @ Override public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {HttpServletResponse httpServletResponse = (HttpServletResponse) response; httpServletResponse.setHeader ("Access-Control-Allow-Origin", "*"); httpServletResponse.setHeader ("Access-Control-Allow-Methods", "POST, GET") HttpServletResponse.setHeader ("Access-Control-Max-Age", "3600"); httpServletResponse.setHeader ("Access-Control-Allow-Headers", "x-requested-with"); chain.doFilter (request, response); @ Override public void destroy () {}}

This scheme solves the cross-domain problem by adding parameters such as Access-Control-Allow-Origin to the requested header.

By the way, using the @ CrossOrigin annotation and the scheme that implements the WebMvcConfigurer interface, spring ends up calling the handleInternal method of the DefaultCorsProcessor class at the bottom:

In the end, the three solutions will achieve the same goal by adding cross-domain required parameters to header, but the implementation forms are different.

six。 How to customize starter

In the past, when we were not using starter, we needed to introduce new features into the project, and the steps generally looked like this:

Find the jar package required for this function in the maven warehouse

Find other jar packages that the jar depends on in the maven repository

Configure the parameters required for the new feature

The above approach brings three problems:

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

If you rely on more packages, it is troublesome to find, easy to find wrong, and it will take a lot of time.

There may be version compatibility issues between dependent packages, and the project may not start properly after introducing these jar packages.

If some parameters are not configured properly, the startup service will also report an error, and there is no default configuration.

In order to solve these problems, the starter mechanism of springboot came into being.

The starter mechanism brings these benefits:

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

It can start the corresponding default configuration.

It can manage the required dependencies and get rid of the need to look everywhere for dependencies and compatibility problems.

The automatic discovery mechanism automatically injects the classes configured in the spring.factories file into the spring container.

Follow the concept of "convention is greater than configuration".

It's cool to just introduce the starter package into the business engineering and use its functions.

Here is a diagram to summarize several elements of starter:

Next, let's work together to define our own starter.

The first step is to create the id-generate-starter project:

The pom.xml configuration is as follows:

4.0.0 1.3.1 com.sue id-generate-spring-boot-starter id-generate-spring-boot-starter com.sue id-generate-spring-boot-autoconfigure 1.3.1

Step 2, create the id-generate-spring-boot-autoconfigure project:

The project includes:

Pom.xml

Spring.factories

IdGenerateAutoConfiguration

IdGenerateService

The IdProperties pom.xml configuration is as follows:

Org.springframework.boot spring-boot-starter-parent 2.0.4.RELEASE 4.0.0 1.3.1 com.sue id-generate-spring-boot-autoconfigure id-generate-spring-boot-autoconfigure org.springframework.boot spring-boot-starter org.springframework.boot Spring-boot-autoconfigure org.springframework.boot spring-boot-configuration-processor true org.apache.maven.plugins maven-compiler-plugin 1.8 1.8

The spring.factories configuration is as follows:

Org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.sue.IdGenerateAutoConfiguration

IdGenerateAutoConfiguration class:

@ ConditionalOnClass (IdProperties.class) @ EnableConfigurationProperties (IdProperties.class) @ Configuration public class IdGenerateAutoConfiguration {@ Autowired private IdProperties properties; @ Bean public IdGenerateService idGenerateService () {return new IdGenerateService (properties.getWorkId ());}

IdGenerateService class:

Public class IdGenerateService {private Long workId; public IdGenerateService (Long workId) {this.workId = workId;} public Long generate () {return new Random () .nextInt + this.workId;}}

IdProperties class:

@ ConfigurationProperties (prefix = IdProperties.PREFIX) public class IdProperties {public static final String PREFIX = "sue"; private Long workId; public Long getWorkId () {return workId;} public void setWorkId (Long workId) {this.workId = workId;}}

This introduces related dependencies into the business project:

Com.sue id-generate-spring-boot-starter 1.3.1

You can use injection to use the function of IdGenerateService.

@ Autowired private IdGenerateService idGenerateService

Perfect.

seven。 Additional functions when the project is started

Sometimes we need to customize some additional functions at the start of the project, such as loading some system parameters, completing initialization, preheating the local cache, and so on.

The good news is that springboot provides:

CommandLineRunner

ApplicationRunner

These two interfaces help us achieve the above requirements.

Their usage is quite simple. Take the ApplicationRunner interface as an example:

@ Component public class TestRunner implements ApplicationRunner {@ Autowired private LoadDataService loadDataService; public void run (ApplicationArguments args) throws Exception {loadDataService.load ()}}

Implement the ApplicationRunner interface, rewrite the run method, and implement your own customized requirements in this method.

If there are multiple classes in the project that implement the ApplicationRunner interface, how do you specify the order of their execution?

The answer is to use the @ Order (n) annotation, where the smaller the value of n, the first to execute. Of course, you can also specify the order through the @ Priority annotation.

The main process when the springboot project starts is as follows:

In the callRunners method of the SpringApplication class, we can see the specific calls to these two interfaces:

Finally, there is one more question: what is the difference between the two interfaces?

The parameter of run method in CommandLineRunner interface is String array

The parameter to the run method in ApplicationRunner is ApplicationArguments, which contains String array parameters and some optional parameters.

At this point, I believe you have a deeper understanding of "what are the code skills in Spring?" you might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report