Spring Basic Notes 1

To talk about the history of Spring, we need to talk about J2EE. The widespread implementation of J2EE applications started in 1999 and 2000, bringing standardization of core middle-tier concepts such as transaction management, but it was not an absolute success in practice, as development efficiency, development difficulty and actual performance were disappointing.

Anyone who has ever used EJB to develop a JAVA EE application like me must know that learning and applying EJB is very hard at the beginning and many things are not easy to understand at once. The configuration is also complex and monotonous, and the code for object lookup using JNDI is also monotonous and boring. The high cost of learning EJB, and very low development efficiency, very high resource consumption, have caused the use of EJB difficulties. And Spring emerged initially to solve these problems like these.

One of the biggest purposes of Spring is to make Java EE development easier. Much more easier.

Core Container

Spring’s core container is the foundation on which other modules are built, consisting of

  • Beans module,
  • Core module
  • Context context module
  • The SpEL expression language module

Without these core containers, it is also impossible to have AOP, Web, and other upper layer features.

AOP module

It provides a aspect-oriented programming implementation, which provides functions such as logging, permission control, performance statistics, and other general functions that separated from business logic , and can dynamically add these functions to the required code, so that each has its own role to reduce the coupling of business logic and general functions

很多开发者入门都是从Spring Boot开始的,他对Spring整体框架底层,以及发展历史不是很了解; 特别是对于一些老旧项目维护和底层bug分析没有全局观。
Many developers start with Spring Boot, he does not have a good understanding of the overall framework underlying Spring, and the development history; especially for some older projects maintenance and underlying bug analysis does not have a global view.
Spring represents a framework design philosophy that requires a global understanding of how Spring Framework components work together and the need to understand the original intent of its design and future trends.

Spring框架管理这些Bean的创建工作,即由用户管理Bean转变为框架管理Bean,这个就叫控制反转 - Inversion of Control (IoC)Spring
框架托管创建的Bean放在哪里呢? 这便是IoC Container;Spring 框架为了更好让用户配置Bean,必然会引入不同方式来配置Bean? 这便是xml配置,Java配置,注解配置等支持Spring
框架既然接管了Bean的生成,必然需要管理整个Bean的生命周期等;
应用程序代码从Ioc Container中获取依赖的Bean,注入到应用程序中,这个过程叫 依赖注入(Dependency Injection,DI) ; 所以说控制反转是通过依赖注入实现的,其实它们是同一个概念的不同角度描述。通俗来说就是IoC是设计思想,DI是实现方式在依赖注入时,有哪些方式呢?这就是构造器方式,@Autowired, @Resource, @Qualifier… 同时Bean之间存在依赖(可能存在先后顺序问题,以及循环依赖问题等)

Updating my hexo and icarus install

Updating Hexo Install

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
npm install -g hexo-cli
hexo version

npm install -g npm-check
npm-check

npm install -g npm-upgrade
npm-upgrade

npm update -g
npm install -g npm

hexo clean
hexo g -s
hexo d

Updating hexo theme

  1. Remove node_modules folder
  2. remove "hexo-theme-icarus": "^5.1.0" in package.json
1
2
3
4
5
npm install
npm install bulma-stylus@0.8.0
npm install hexo-renderer-inferno
hexo config theme icarus
hexo server

use the original icarus config

Password store paradigm

The very first thing

We should always remind ourself is that, we should not store passwords yourself. At least, we should try out best to avoid it.

As a User

Password manager and 2FA is strongly suggested. However, it is not the thing I want to discuss today.

As a develop

As a developer, we are unavoidably need to store user’s password.

Storing in plain text

The most naive way is to store user’s password in plain text. Once the database is leaked or have insider, all user’s password is leaked. Sadly, many people in the world are using the same username and password in different website. And thats why we should use password manager as a user.

Encrypt the password

It is a little bit better than store it as plain text. However, it still incredibly easy to get things wrong. Imagine that the database is leaked therefore Hacker have the whole database offline. They can see the encrypted code aka the ciphertext. Under lots of encryption algorithm, same text will generate same ciphertext. If it is a very large database, it most likely have many same password (especially for some easy, common passwords). The scariest part is that, some times the reset password system may also store “hints” of the password. Imagine there are 20 same encrypted password, that means I will have 20 different hints point to a same password.

Hashing (without salts)

hash(m)

Hashing and store the hashed password almost get the things right. However, it still can go wrong with some old hashing algorithm. let me introduce a idea, rainbow table: pre-computed hash chains. Improve on the dictionary attack to trade time for space. It is a common and strong approach to crack hash nowadays. By matching the hashed cipher text and rainbow table, hacker can easily find some of the correct match of password. If the database is compromised. Although it is the intruder who gets the hash value, it is also easy to restore the password plaintext in bulk due to the existence of rainbow tables.

Hashing (with salts)

The rainbow table is generated for a specific function H. If H changes, the existing rainbow table data is completely unusable. If using salt value, then a different rainbow table must be generated for each user. It greatly increases the difficulty of cracking. And the best practice is to use a different salt for each user since it is worth mentioning that, the tensor computing provided by display card to highly speed up the hack cask. Which make it even more danger now and in the future.

A practices that I have implement is make use of the fact that username is usually cannot change. Use username for as the salt.

Upgrading the old method from the past

It is common that we need to upgrade the hashing algorithm form the past but at the same time do not want to affect the user. For plain text, it just need to hash the password. And so is the encryption, it is just need to decrypt the password before hashing.

What if we already have hashed password? The fact that we cannot restore the password because hashing is a many-to-one compression. The solution is salt and pepper.

  • m: message, password in this case
  • h1: the outdated hash algorithm
  • h2: the modern hash algorithm
  • salt: salt is not secret (merely unique) and can be stored alongside the hashed output
  • pepper: pepper is secret and must not be stored with the output.

h2(h1(m)+pepper) , salt is optional in this case. the h1 might have salted already.

hash(m+salt)
it must be safe to ensure that each user’s salt is different.

Setup mysql with docker

The docker compose file I use:

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
version: '3'

services:
mysql:
image: mysql/mysql-server
container_name: mysql
environment:
MYSQL_ROOT_PASSWORD: <mypw>
MYSQL_DATABASE: app_db
MYSQL_USER: dev
MYSQL_PASSWORD: <mypw>
ports:
- "3306:3306"
volumes:
- ./data:/var/lib/mysql
phpmyadmin:
image: phpmyadmin/phpmyadmin
container_name: pma
links:
- mysql
environment:
PMA_HOST: mysql
PMA_PORT: 3306
PMA_ARBITRARY: 1
restart: always
ports:
- 8081:80

Access into the docker container

docker exec -it <container id> bash

The container id can be find by docker ps

Create new database

mysql -u root -p

CREATE DATABASE 'newdatabase';

1
2
3
4
CREATE USER 'dev'@'localhost' IDENTIFIED BY 'mypw';
CREATE USER 'dev'@'%' IDENTIFIED BY 'mypw';
GRANT ALL ON *.* TO 'dev'@'localhost';
GRANT ALL ON *.* TO 'dev'@'%';

flush privileges;

CREATE USER 'newuser'@'%' IDENTIFIED BY 'newpassword';

Then give the new account “newuser” permission to read and write the new database

GRANT ALL PRIVILEGES ON newdatabase.* TO 'newuser'@'localhost';

quit the root login and switch to the user

quit

mysql -u <newuser> -p

How to build a hello world docker image by docker file

First, create a `hello-world.js’ file

1
console.log("Hello World")

Then, create a Dockerfile in the same directory, the Dockerfile should look like that:

1
2
3
4
5
6
7
8
9
10
FROM ubuntu

RUN apt update && apt install nodejs -y

WORKDIR /app

COPY . .

CMD ["node", "/app/hello-would.js"]

FROM ubuntu means we ubuntu the ubuntu image as the base

RUN apt update && apt install nodejs -y means install nodejs into the ubuntu image

WORKDIR is used to define the working directory of a Docker container.

COPY . . is to copy the current directory(the hello-world.js) to the WORKDIR

CMD ["node", "/app/hello-would.js"] is the command to run after the image load.

Finally, Build the image

docker build -t {image name and version} .

-t is the tag for the image name and version, for example, etklam/hello_app:0.1

. is the Dockerfile directory

MYSQL | what's the different betweenUTF8 and UTF8-mb4

Main different

The main different between utf8 and utf8-mb4 is utf8 is only 3 bytes but utf8-mb4 is 4bytes instead. Note that UTF8 have 4 bytes, utf8mb4 is the true utf8 character set.

In general, it is enough to use MySQL utf8 to set up a website, however, Some special (Chinese) characters or common emoji are not included in 3 bytes. Therefore, most Chinese characters are sufficient, but they cannot be used for all characters. If you want to use special characters or emoticons, you cannot use MySQL’s utf8 character set to store them.

UTF8 to UTF8mb4 does not cause incompatibility problems.

Notes about ajax request

Create XMLHttpRequest object

XMLHttpRequest is basic of Ajax request,

1
const xhr = new XMLHttpRequest();

Send a request to the server

1
2
xmlhttp.open("GET", "url"); // url is the api endpoint
xml.send();

onreadystatechange

There are 5 state of xmlhttprequest, from 0 to 4

  1. Request not initialized
  2. Server connection is established
  3. Request has been received
  4. Request is being processed
  5. Request completed and response is ready

Hence, When readyState is equal to 4 and the status is 200, the response is ready.

1
2
3
4
5
xml.onreadystatechange=function() {
if(xml.readyState==4 && xml.status==200) {
document.getElementById("myDiv").innerHTML=xml.responseText;
}
}

Ajax json examples

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
function loadXMLDoc(){
// init a xmlhttprequest
const xmlhttp = new XMLHttpRequest();

// init a onreadystatechange and parse xml to json
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
var myArr = JSON.parse(this.responseText);
myFunction(myArr)
}
}
//send the xml request
xmlhttp.open("GET","/try/ajax/json_ajax.json",true);
xmlhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xmlhttp.send();
}
// function to change the dom
function myFunction(arr) {
var out = "";
var i;
for(i = 0; i < arr.length; i++) {
out += '<a href="' + arr[i].url + '">' +
arr[i].title + '</a><br>';
}
document.getElementById("myDiv").innerHTML=out;
}

Ajax with Promise examples

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

const p = new Promise((resolve, reject)=>{
const xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4){
if (xmlhttp.status==200)
{
//success
resolve(this.responseText)
}else{
//fail
reject(xmlhttp.status)
}
}
}
})

//promise followup
const result = p.then(function(value){
let myArr = JSON.parse(value)
myFunction(myArr)
return myArr
},function(reason){
console.log(reason)
return 'error'
})

// function to change the dom
function myFunction(arr) {
var out = "";
var i;
for(i = 0; i < arr.length; i++) {
out += '<a href="' + arr[i].url + '">' +
arr[i].title + '</a><br>';
}
document.getElementById("myDiv").innerHTML=out;
}

Learn Spring Boot 1

Spring initialization

  • spring-boot-starter-web
  • spring-boot-starter-data-jpa
  • spring-boot-devtools
  • mysql-connector-java
  • lombok

Connect to database

in the resource/application.properties, we can config the hibernate connection for mysql

1
2
3
4
5
6
spring.jpa.hibernate.ddl-auto=create-drop
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=username
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.show-sql= true

Simple API

To build a simple api, we need to add two annotaion to the main

  • RestController
  • @GetMapping/@PostMapping/ others
1
2
3
4
5
6
7
8
9
10
11
12
13
@SpringBootApplication
@RestController
public class PpfaSpringApplication {

public static void main(String[] args) {
SpringApplication.run(PpfaSpringApplication.class, args);
}

@GetMapping
public String hello() {
return "Hello World";
}
}

Manage multiple jdks in macos, M1

First, install jenv by using Homebrew

1
brew install jenv

After that, need to config the zsh

1
2
3
echo 'export PATH="$HOME/.jenv/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(jenv init -)"' >> ~/.zshrc
source ~/.zshrc

It only found the system default Java:

1
jenv versions

Add the jdk you installed to the jenv. Personally my jdks are installed at /Users/klam/Library/Java/JavaVirtualMachines/

For example:

jenv add /Users/klam/Library/Java/JavaVirtualMachines/azul-17.0.3/Contents/Home/

jenv global 17 to swap between different jdk for the default jdk

you can also use jenv local 17 to specifies the Java version of a folder

Java Error and exception

In Java, there are two main types of problems that can occur during the execution of a program: Errors and Exceptions.

1. Errors

Errors are serious issues that occur beyond the control of the application. These are typically problems related to the Java Virtual Machine (JVM) itself, such as:

  • StackOverflowError
  • OutOfMemoryError
  • VirtualMachineError

These errors are often unrecoverable and should not be handled in the code. When an error occurs, it’s best to let the system crash or shut down gracefully, as the environment may no longer be stable.

2. Exceptions

Exceptions are issues that arise during the normal operation of a program and can usually be anticipated and handled. For example:

  • Trying to read a file that doesn’t exist.
  • Invalid user input.
  • Attempting to divide by zero.

Java provides a robust mechanism to handle exceptions using try-catch-finally blocks. Exceptions are further categorized into two types:

a. Checked Exceptions

These are exceptions that are checked at compile time. The compiler requires the developer to handle these exceptions explicitly, either by using a try-catch block or by declaring them in the method signature using the throws keyword.

Examples:

  • IOException
  • SQLException

b. Unchecked Exceptions

These are exceptions that are not checked at compile time. They usually indicate programming bugs, such as logic errors or improper use of an API. These exceptions inherit from RuntimeException.

Examples:

  • NullPointerException
  • ArrayIndexOutOfBoundsException
  • IllegalArgumentException

Summary

Type Checked at Compile Time Typically Caused By Should Be Handled?
Error No JVM/Internal system issues No
Checked Exception Yes External issues (I/O, DB) Yes
Unchecked Exception No Programming bugs Yes (when possible)

Understanding the difference between errors and exceptions—and between checked and unchecked exceptions—helps in writing more robust and fault-tolerant Java applications.


Let me know if you’d like a more casual tone or if you want to turn this into a tutorial-style post!