Error arose: Cannot inline bytecode built with JVM target 17 into bytecode that is being built with JVM target 1.8. Please specify proper ‘-jvm-target’ option
This is a very common version mismatch error in Kotlin development. It happens because a dependency or a module in your project was compiled using Java 17, but your current module/project is configured to compile down to Java 1.8 (Java 8).
Kotlin cannot inline code from a higher Java version into a lower one because the lower version doesn’t understand the newer bytecode.
open build.gradle.kts
copy these things to it.
java { toolchain { languageVersion = JavaLanguageVersion.of(21) } // ...}kotlin { jvmToolchain(21) // ....}// make sure to include it alldependencies { implementation("org.springframework.boot:spring-boot-starter-data-jdbc") implementation("org.springframework.boot:spring-boot-starter-security") implementation("org.jetbrains.kotlin:kotlin-reflect") compileOnly("org.projectlombok:lombok") developmentOnly("org.springframework.boot:spring-boot-devtools") runtimeOnly("com.mysql:mysql-connector-j") annotationProcessor("org.projectlombok:lombok") testImplementation("org.springframework.boot:spring-boot-starter-data-jdbc-test") testImplementation("org.springframework.boot:spring-boot-starter-security-test") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") testCompileOnly("org.projectlombok:lombok") testRuntimeOnly("org.junit.platform:junit-platform-launcher") testAnnotationProcessor("org.projectlombok:lombok") implementation("org.springframework.boot:spring-boot-starter-web")}
rename application.properties to application.yaml
spring: application: name: backend datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/study?useUnicode=true&characterEncoding=utf-8 # study is the database you need to create username: root password: 1234
create SecurityConfiguration in the config directory
By default, Spring Security requires all POST, PUT, DELETE, and PATCH requests to include a valid CSRF token, such as:
_csrf
X-CSRF-TOKEN
If the request does not contain a valid CSRF token, Spring Security rejects it and returns: 403 Forbidden
In many frontend-backend separated applications, developers disable CSRF protection because authentication is usually handled with mechanisms such as JWT or API tokens instead of traditional HTML forms.
loginProcessingUrl: Changes the default URL that Spring Security listens to for authentication requests.
The successHandler is a custom object (implementing AuthenticationSuccessHandler). Instead of redirecting, it typically writes a JSON response back to the frontend containing data like a JWT Token, user profile, or a success status code, and vice versa.
object JsonUtil { private val gson = Gson() fun toJson(any: Any): String { return gson.toJson(any) } fun <T> fromJson(json: String, clazz: Class<T>): T { return gson.fromJson(json, clazz) }}
Custom Validation Logic
Write custom validation logic to handle a wide variety of dynamic scenarios.
Since custom validation is required, we need to implement either the UserDetailsService interface or the more feature-rich UserDetailsManager interface ourselves. For simplicity, we’ll choose to implement UserDetailsService directly.
@Serviceclass AuthorizeService( private val userService: UserService,) : UserDetailsService{ override fun loadUserByUsername(username: String): UserDetails {// println("loadUserByUsername $username, this method was invoked" +// "") val user = userService.findAccountByUsernameOrEmail(username) ?: throw UsernameNotFoundException("User not found") return User( user.username, user.password, // 必须是 String listOf() // 先不给权限,后面可以加 ROLE_USER ) }}
In addition to that, InMemoryUserDetailsManager is a built-in implementation provided by Spring Security. It implements the UserDetailsManager interface.
public class InMemoryUserDetailsManager implements UserDetailsManager, UserDetailsPasswordService
is a unique identifier used in Java web applications (Tomcat/Jetty/Servlet containers) to identify a user’s session. Spring Security relies on the Servlet HttpSession to maintain a user’s login state.
When a user first accesses the server, the server creates a new HttpSession object and generates a unique JSESSIONID. This JSESSIONID is sent to the browser via the Set-Cookie response header. The browser automatically includes this Cookie in all subsequent requests, allowing the server to look up the corresponding Session and identify the user.
After clicking remermber-me, the browser will receive response like my_custom_remember_me_cookie=Vm91WTZ4eHY0R0xibDNOS1J5SHRudyUzRCUzRDp0Sjd6Mzl6d0dLNDhtRmZjVmQzZW1RJTNEJTNE; Expires=Fri, 24 Jul 2026 15:07:26 GMT; Max-Age=1209600; Path=/; HttpOnly
Spring Validation allows developers to enforce data integrity and security by intercepting invalid incoming data before it reaches core business logic.
private const val EMAIL_REGEX : String = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"@Validated@RestController@RequestMapping("/api/auth/")class AuthorizeServiceController ( private val service: AuthorizeService){ private val log = com.ferryside.util.Log.logger<AuthorizeServiceController>() @PostMapping("/valid-email") fun validateEmail(@Pattern(EMAIL_REGEX)@RequestParam("email") email: String): RestBean<String>{ // @RequestParam 获取请求参数 log.info("Validating email: $email") return if (service.sendValidatedEmail(email)) RestBean.success("sent email successfully") else RestBean.failure(HttpStatus.BAD_REQUEST.ordinal, "Invalid email") }}
When using annotations like @Pattern, the arguments passed into them must be compile-time constants.
So, we move EMAIL_REGEX to the top of the file (as a top-level constant, highly recommended)
Log in SpringBoot
fun main() { test()}//添加inline表示内联函数inline fun test(){ println("这是一个内联函数") println("这是一个内联函数") println("这是一个内联函数")}
To optimize performance, overhead can be eliminated by inlining lambda expressions. Using the inline keyword affects both the function itself and the lambdas passed to it, allowing the method call to be directly replaced by its execution code at compile time.
The so-called ‘inline expansion’ means that during compilation, the compiler directly replaces the place where you call the function with the function’s actual code. It’s just like ‘copying and pasting’ while writing code.
inline fun hello() { println("Hello")}fun main() { hello() // during [[compilation]],println("Hello") will replace hello()}
class UserService { private val log = LogFactory.logger<UserService>() fun register() { log.info("Hello") }}
The modern way to use log in Springboot is:
@Slf4j@Controllerpublic class MainController { @ResponseBody @GetMapping("/test") public User test(){ log.info("用户访问了一次测试数据"); return mapper.findUserById(1); } ...
function post(url: string, data: object, success: (message: string, status: number) => void, failure = defaultFailure, err = defaultError) { axios.post(url, data,{ headers: { // config Request Header 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', // the data will be sent looking like a traditional HTML form submission }, withCredentials: true // include cookies automatically in the request }).then(({data})=>{ // 接受相应,解构 data if(data.success){ success(data.message, data.status) } else{ failure() } }).catch(err)}
Now we can invoke this method after clicking the login button.
const login = () =>{ if(form.username && form.username.length > 0){ post('/api/auth/login', { // pass the object to the method 'post' username: form.username, password: form.password, remember: form.remember, }, (message)=>{ ElMessage.success(message) router.push('/index') }) } else{ ElMessage.warning("Please enter username and password") }}
Callback function
function createFn() { return function () { console.log("我是被返回的函数") }}const fn = createFn() //调用函数返回一个函数fn() //在调用函数返回的函数
const isEmailValid = ref(false)// define a function that is prepared to be invoked by Element Plusconst onValidate = (prop, isValid) => { if (prop === 'email') { isEmailValid.value = isValid }}
@validate Event: The <el-form> component emits a validate event whenever a form item undergoes validation (either on blur, on change, or during a manual form submission check). Arguments Passed: Element Plus automatically passes two main arguments to the callback function:
prop: A string representing the field’s property name (matching the prop attribute on <el-form-item>).
isValid: A boolean indicating whether the field passed its validation rules. State Management: The onValidate function filters for the 'email' property and updates the reactive isEmailValid ref accordingly. This allows you to conditionally enable submit buttons, display custom UI alerts, or trigger side effects based on the real-time validity of the email field.
Common issues
c.f.backend.BackendApplicationKt : Starting BackendApplicationKt using Java 21.0.3 with PID 22488
2026-06-27T22:42:10.695+08:00 INFO 22488 --- [backend] ``[ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated…
2026-06-27T22:42:10.714+08:00 INFO 22488 --- [backend][ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed
Based on the logs you provided, your project actually completed its startup successfully without any errors (Started BackendApplicationKt in 1.452 seconds).
However, immediately after starting, it triggered a ShutdownHook, which closed the database connection pool (HikariPool-1 - Shutdown completed) and terminated the process.
The most common reason for a Spring Boot project to close automatically right after starting is: Your project is missing the Web Starter dependency. Without it, Spring Boot treats your application as a standard command-line program. It initializes everything (like connecting to your MySQL database), realizes it has no tasks left to perform, and gracefully shuts down.
That exception happens because Spring MVC is trying to treat your UserService parameter in the controller method as a data object to bind from the HTTP request, rather than injecting it as a service bean.
By default, any complex object parameter in a Spring @RequestMapping (like @PostMapping) that isn’t explicitly annotated is treated by Spring as a @ModelAttribute. Spring looks at UserService (which is an interface), tries to find a constructor to instantiate it to pass request data into it, and fails with IllegalStateException.
Here is the exact issue and how to fix it:
@RestController@RequestMapping("/auth") // or whatever your base path isclass AuthController( // Inject the service here via constructor injection private val userService: UserService) { @PostMapping("/reset-password") fun resetPassword( @RequestParam("password") password: String, @RequestParam("email") emailAddress: String, session: HttpSession // Make sure session is passed or injected if needed ): RestBean<String> { val email = session.getAttribute("resetting-password") as? String if (email.isNullOrEmpty()) { return RestBean.failure(400, "Verify your email first") } // Use the class-level injected service userService.updatePassword(emailAddress, password) return RestBean.success("Reset password successfully") }}
Terminal tips
In the terminal, the command to move to the parent directory (one level up) is very simple. You can use the following command:
cd ..
Database
create a table in the database study
CREATE TABLE users ( id INT NOT NULL AUTO_INCREMENT, username VARCHAR(255), password VARCHAR(255), email VARCHAR(255), PRIMARY KEY (id));
Insert a row into this table
INSERT INTO users (id, username, password, email)VALUES (1, 'admin', 'the password that must be encoded', NULL);
After pushing…
Open your GitHub repository, go to Settings → Pages.
Under Build and deployment → Source, select GitHub Actions (do not select Deploy from a branch).
Difference between workflows and actions
Folder
What’s inside?
Responsibility
.github/workflows/
放 .yml 配置文件。
定义整个自动化的触发时机(如 push)和整体流程(Pipeline)。
.github/actions/
放子文件夹(包含 action.yml 和代码)。
执把某一段特定的、可能重复使用的代码(如特定步骤的脚本)封装起来。
If my-custom-build exits, How to use it(workflows.yml) in a workflow?
jobs: build-and-deploy: runs-on: ubuntu-latest steps: - name: Check out repository uses: actions/checkout@v4 # 调用本地 .github/actions/my-custom-build 目录下的自定义 Action - name: Run my local custom action uses: ./.github/actions/my-custom-build