Trong những bài viết trước, chúng ta đã cùng nhau tìm hiểu tổng quan về Spring Boot và làm thế nào để tạo một Spring Boot project bằng CLI, bằng Spring Initialzr Web và bằng Spring Tool Suite. Có những bạn chưa làm việc nhiều về Spring Boot sẽ thắc mắc về cách hoạt động của nó. Do đó, trong bài viết này mình sẽ dành chút thời gian để nói về cách hoạt động của Spring Boot nhé các bạn. Bạn nào đã biết rồi thì hãy coi như review lại xem mình hiểu có đúng không nhé!
OK, đầu tiên mình sẽ tạo mới một Spring Boot project với Web Starter và một class HelloController như sau:
Nội dung của class HelloController:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package com.huongdanjava.springboot; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class HelloController { @RequestMapping(value = "/") public ResponseEntity<String> hello() { return new ResponseEntity<>("Hello World!", HttpStatus.OK); } } |
Bây giờ, nếu các bạn chạy project này lên và truy cập vào đường dẫn http://localhost:8080/, các bạn sẽ thấy kết quả như sau:
Rõ ràng như các bạn thấy, mặc dù chúng ta không khai báo các tập tin cấu hình cần thiết như web.xml, annotation @Configuration hay bất kỳ bean nào trong Spring container, nhưng ứng dụng của chúng ta vẫn chạy bình thường và hiển thị nội dung mà chúng ta muốn.
Để làm được điều này, Spring Boot phải dựa vào annotation @SpringBootApplication đã được khai báo trong class SpringBootExampleApplication:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package com.huongdanjava.springboot; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringBootExampleApplication { public static void main(String[] args) { SpringApplication.run(SpringBootExampleApplication.class, args); } } |
Chính annotation này đã giúp cho Spring Boot có thể tự động quét tất cả các cấu hình và các bean có trong ứng dụng của chúng ta.
Nội dung của annotation này như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
/* * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.boot.autoconfigure; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.context.TypeExcludeFilter; import org.springframework.context.annotation.AnnotationBeanNameGenerator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.core.annotation.AliasFor; import org.springframework.data.repository.Repository; /** * Indicates a {@link Configuration configuration} class that declares one or more * {@link Bean @Bean} methods and also triggers {@link EnableAutoConfiguration * auto-configuration} and {@link ComponentScan component scanning}. This is a convenience * annotation that is equivalent to declaring {@code @Configuration}, * {@code @EnableAutoConfiguration} and {@code @ComponentScan}. * * @author Phillip Webb * @author Stephane Nicoll * @author Andy Wilkinson * @since 1.2.0 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { /** * Exclude specific auto-configuration classes such that they will never be applied. * @return the classes to exclude */ @AliasFor(annotation = EnableAutoConfiguration.class) Class<?>[] exclude() default {}; /** * Exclude specific auto-configuration class names such that they will never be * applied. * @return the class names to exclude * @since 1.3.0 */ @AliasFor(annotation = EnableAutoConfiguration.class) String[] excludeName() default {}; /** * Base packages to scan for annotated components. Use {@link #scanBasePackageClasses} * for a type-safe alternative to String-based package names. * <p> * <strong>Note:</strong> this setting is an alias for * {@link ComponentScan @ComponentScan} only. It has no effect on {@code @Entity} * scanning or Spring Data {@link Repository} scanning. For those you should add * {@link org.springframework.boot.autoconfigure.domain.EntityScan @EntityScan} and * {@code @Enable...Repositories} annotations. * @return base packages to scan * @since 1.3.0 */ @AliasFor(annotation = ComponentScan.class, attribute = "basePackages") String[] scanBasePackages() default {}; /** * Type-safe alternative to {@link #scanBasePackages} for specifying the packages to * scan for annotated components. The package of each class specified will be scanned. * <p> * Consider creating a special no-op marker class or interface in each package that * serves no purpose other than being referenced by this attribute. * <p> * <strong>Note:</strong> this setting is an alias for * {@link ComponentScan @ComponentScan} only. It has no effect on {@code @Entity} * scanning or Spring Data {@link Repository} scanning. For those you should add * {@link org.springframework.boot.autoconfigure.domain.EntityScan @EntityScan} and * {@code @Enable...Repositories} annotations. * @return base packages to scan * @since 1.3.0 */ @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses") Class<?>[] scanBasePackageClasses() default {}; /** * The {@link BeanNameGenerator} class to be used for naming detected components * within the Spring container. * <p> * The default value of the {@link BeanNameGenerator} interface itself indicates that * the scanner used to process this {@code @SpringBootApplication} annotation should * use its inherited bean name generator, e.g. the default * {@link AnnotationBeanNameGenerator} or any custom instance supplied to the * application context at bootstrap time. * @return {@link BeanNameGenerator} to use * @see SpringApplication#setBeanNameGenerator(BeanNameGenerator) * @since 2.3.0 */ @AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator") Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class; /** * Specify whether {@link Bean @Bean} methods should get proxied in order to enforce * bean lifecycle behavior, e.g. to return shared singleton bean instances even in * case of direct {@code @Bean} method calls in user code. This feature requires * method interception, implemented through a runtime-generated CGLIB subclass which * comes with limitations such as the configuration class and its methods not being * allowed to declare {@code final}. * <p> * The default is {@code true}, allowing for 'inter-bean references' within the * configuration class as well as for external calls to this configuration's * {@code @Bean} methods, e.g. from another configuration class. If this is not needed * since each of this particular configuration's {@code @Bean} methods is * self-contained and designed as a plain factory method for container use, switch * this flag to {@code false} in order to avoid CGLIB subclass processing. * <p> * Turning off bean method interception effectively processes {@code @Bean} methods * individually like when declared on non-{@code @Configuration} classes, a.k.a. * "@Bean Lite Mode" (see {@link Bean @Bean's javadoc}). It is therefore behaviorally * equivalent to removing the {@code @Configuration} stereotype. * @since 2.2 * @return whether to proxy {@code @Bean} methods */ @AliasFor(annotation = Configuration.class) boolean proxyBeanMethods() default true; } |
Annotation @SpringBootApplication là sự kết hợp giữa 3 annotation khác của Spring bao gồm: @SpringBootConfiguration, @ComponentScan và @EnableAutoConfiguration trong đó annotation @EnableAutoConfiguration là quan trọng nhất.
Với @EnableAutoConfiguration annotation, Spring Boot sẽ tự động cấu hình cho ứng dụng của chúng ta dựa vào classpath, các annotations và các thông tin cấu hình mà chúng ta định nghĩa.
Tất cả các annotation này sẽ giúp cho Spring Boot có thể tự động cấu hình cho ứng dụng của chúng ta một cách phù hợp và chúng ta không cần phải quan tâm nhiều đến việc cấu hình.
Trong ví dụ trên của mình, để thực hiện việc cấu hình một cách tự động, đầu tiên Spring Boot sẽ kiểm tra classpath trong project của mình, và bởi vì dependency của mình có spring-boot-starter-web nên Spring Boot sẽ cấu hình ứng dụng của mình là một ứng dụng web.
Thêm nữa, Spring Boot sẽ coi HelloController của mình là một web controller dựa vào annotation @Controller và annotation @RequestMapping.
Mặc định thì Spring Boot sẽ sử dụng Tomcat server để chạy ứng dụng web nên các bạn sẽ thấy có một số dòng log mention về Tomcat server khi các bạn chạy ứng dụng ví dụ này. Các bạn có thể thay đổi Server runtime nếu muốn nhé!