The warehouse address
w4ngzhen/springboot-simple-guide: This is a project that guides SpringBoot users to get started quickly through a series of examples (github.com)
Chapter00 Learn basic knowledge before SpringBoot
A reflection
In Java, we can use Reflection to get the type information of any Class, but the Class Class is the most interesting one. With the Class Class, we get the relevant context of any object.
For example, we have a User class that looks like this:
public class User {
private String name;
private int age;
public User(a) {
System.out.println("User no-argument constructor");
}
public User(String name, int age) {
this.name = name;
this.age = age;
System.out.printf("User parameter constructor: name = %s, age = %d%n", name, age);
}
// Ignore a bunch of getters and setters
}
Copy the code
In the following code, we can get the declared fields and various methods on the class:
Class<User> userClass = User.class;
System.out.println(userClass.getCanonicalName());
// 1. Display the declared fields
Field[] fields = userClass.getDeclaredFields();
System.out.println(This class contains the following fields:);
for (Field field : fields) {
System.out.println(field);
}
// 2. Display the declaration method
Method[] methods = userClass.getDeclaredMethods();
System.out.println(This class contains the following methods:);
for (Method method : methods) {
System.out.println(method);
}
Copy the code
Of course, we can also take the constructor and create the instance by reflection:
// 3. Display the constructor object on the classConstructor<? >[] constructors = userClass.getConstructors();for(Constructor<? > constructor : constructors) { System.out.println("Find the constructor:" + constructor);
}
// 4. Create an instance using the no-argument constructorConstructor<? > constructor = userClass.getConstructor(); Object user1 = constructor.newInstance(); System.out.println(user1);// 5. Create an instance with the argument constructor
// Notice that the argument constructor gets the class object as the argument
// In newInstance, the actual values need to be passed inConstructor<? > constructor1 = userClass.getConstructor(String.class,int.class);
Object user2 = constructor1.newInstance("Mr.Hello".18);
System.out.println(user2);
Copy the code
In the above code, we get the corresponding Class object using code symbols:
Class<User> userClass = User.class;
Copy the code
In this case, we must be able to get the User symbol. But more often than not, we don’t have class symbols, and reflection allows us to use the name of the type:
Class<? > userClass = Class.forName("com.compilemind.guide.chapter00.manual.User");
Copy the code
In addition, we can get annotations on the class. First we define an annotation:
@Target(ElementType.TYPE) // Put it on the type
@Retention(RetentionPolicy.RUNTIME) // Runtime retention
public @interface UserInfo {
String name(a);
int age(a);
}
Copy the code
Then add this annotation to User:
@UserInfo(name = "annotationName", age = 99) // Use annotations
public class User {
// ...
public User(String name, int age) {
this.name = name;
this.age = age;
System.out.printf("User parameter constructor: name = %s, age = %d%n", name, age);
}
// ...
}
Copy the code
Using reflection, we can get annotations and create our User object by way of annotations:
// 1. Get the Class objectClass<? > userClass = Class.forName("com.compilemind.guide.chapter00.manual.User");
// 2. Get the @userinfo annotation on the class
UserInfo userInfo = userClass.getAnnotation(UserInfo.class);
if (userInfo == null) {
System.out.println(userClass.getCanonicalName() + "No notes included." + UserInfo.class + ", terminate creation");
return;
}
// 3. Get the annotation information
String name = userInfo.name();
int age = userInfo.age();
// 4. Create an instance by using the parameter constructor in conjunction with the context on the annotation
Object user = userClass.getConstructor(String.class, int.class).newInstance(name, age);
System.out.println(user);
Copy the code
The source code for this section can be viewed under the chapter00.manual package.
Spring constructs the object
Readers may wonder why 1. Reflection and 2.Spring” construct “objects are put together, when in fact the underlying object construction of Spring uses reflection. Next, let’s look at how to “construct” an object in Spring.
Write a new example, UserEx:
@Component // Use annotations to mark the class as a component
public class UserEx {
public UserEx(a) {
System.out.println("UserEx no-argument constructor"); }}Copy the code
In this UserEx, we add the @Component annotation to the class to mark it as a Component. Then create a new class, IocApp, and write the following code:
/ * * *"@SpringBootApplication"The flag is a SpringBoot application * when started, the SpringBoot framework scans the current package and (by default) all the packages that have@ComponentMark the class, * and create an instance of the class by reflection in Spring's Bean container. * /
@SpringBootApplication //
public class IocApp {
public static void main(String[] args) {
/ / 1. The start
ConfigurableApplicationContext context =
SpringApplication.run(IocApp.class, args);
// 2. Class symbol fetch
System.out.println("Get Bean by class symbol");
UserEx userEx = context.getBean(UserEx.class);
System.out.println(userEx);
// 3. Obtain by Bean name
System.out.println("Get Bean by class symbol");
UserEx userEx2 = (UserEx) context.getBean("userEx"); System.out.println(userEx2); }}Copy the code
In this code, on the class that has the main function, add @SpringBootApplication to mark it as a SpringBoot application.
Next, we call SpringApplication.run(iocapp. class, args) in the main function; To launch the SpringBoot application. Upon startup, the SpringBoot framework scans the current package and (by default) all classes with the @Component flag under the package and creates reflective instances of that class in Spring’s Bean container.
. In the end, we by calling ConfigurableApplicationContext getBean for instance and for printing. Here, we use two ways to get the Bean:
// Pass in the class symbol. Class
<T> T getBean(Class<T> requiredType) throws BeansException;
// The name of the Bean passed in
Object getBean(String name) throws BeansException;
Copy the code
As a quick note, Bean names have certain rules. By default, this is the small hump of the class name, where UserEx corresponds to UserEx; But we can customize the Bean’s name in the container by setting the annotation’s name field: @Component(“myUserEx”).
With that in mind, let’s go back to reflection in section 1: We add the annotation @UserInfo to the User class, and then, through reflection, we get the information about the annotation and create the User instance.
Now, can you relate the above in section 2 to the operations in reflection? If you have a rough idea of what I’m talking about now, congratulations, you have a good idea of the inner workings of How Spring does object building.
2.1.IOC inversion of control
As careful readers have noticed, in the previous section we created a class that contains the startup code: IocApp. An IOC is an Inversion of Control.
Whereas the traditional way to create objects is directly through the new keyword, Spring creates objects through the IOC container, which means we give control of object creation to the IOC container. IOC can be summed up in one sentence:
IOC lets programmers focus not on how objects are created, but on what happens after they are created, leaving the creation, initialization, and destruction of objects to the Spring container.
Specifically, for the UserEx class, without IOC thinking, we create the UserEx class as follows:
UserEx userEx = new UserEx(); / /... Using this exampleCopy the code
Under the Spring IOC framework, we did the following:
- Write UserEx class, annotate
@Component
; - Initialize the container:
SpringApplication.run(...)
; - From a container:
UserEx userEx = context.getBean(UserEx.class);
Looking at this, you might think that without IOC mode, I would only need one line of code, but now with Spring IOC, there is so much more configuration and manipulation. Isn’t that more of a problem? In this case, that’s true, but think about it. As the project gets bigger and bigger, more and more class instances need to be created. Furthermore, the IOC container does much more for us than just inversion of control. Dependency injection is an important capability.
2.2.DI dependency injection
When it comes to dependency injection, we first need to be clear about what a dependency is in our code. From the Internet there is a definition of dependency, WHICH I think is very good:
Every piece of software is made up of many “components”. By “component,” I mean a component in a broad sense — a component that could be a function, a class, a package, or a microservice. The architecture of software is the components and the relationships between them. The relationships between these components are (in the broad sense) dependencies.
In a narrow sense, if we define a GameEx class that contains UserEx, we can say that GameEx depends on UserEx:
public class GameEx {
private UserEx userEx; // GameEx relies on UserEx
public void setUserEx(UserEx userEx) {
this.userEx = userEx;
System.out.println("Call setUserEx");
}
/** * Prints the GameEx UserEx */
public void printUserEx(a) {
if (this.userEx == null) {
System.out.println("No user");
} else {
System.out.println(this.userEx); }}}Copy the code
In the GameEx class above, you have a field of type UserEx, UserEx. It also contains a method called printUserEx to print the UserEx instance. To avoid getting the output “no user”, we need to set the instance of UserEx before calling this method:
/ / pseudo codeUserEx userEx = ... ;// 1. Somehow, get an instance of UserExGameEx gameEx = ... ;// 2. Get an instance of GameEx somehow
gameEx.setUserEx(userEx); // 3. Call setter methods to set the UserEx instance to GameEx
gameEx.printUserEx(); // 4. Output: UserEx@xxxx
Copy the code
In step 3 of the pseudocode above, we call the setter function manually by code. This process, in essence, is that we control the dependencies: since we understand that GameEx’s functionality is dependent on UserEx, we need to code manually to handle such dependencies for the desired purpose.
Let’s look at steps 1 and 2 in the pseudocode above: get UserEx and GameEx instances. In Section 2, we learned how to use Spring’s IOC container to create objects, so for the GameEx class, we can also add the @Component annotation to mark GameEx as a Bean and let Spring’s IOC container manage it:
@Component
public class GameEx {
// Other code ignores...
}
Copy the code
We then implement the above pseudo-code effect in IocApp:
@SpringBootApplication //
public class IocApp {
public static void main(String[] args) {
/ / 1. The start
ConfigurableApplicationContext context =
SpringApplication.run(IocApp.class, args);
manualDependencySet(context);
}
/** * Section 2.2 Pseudo-code implementation: Manually set dependencies */
private static void manualDependencySet(ConfigurableApplicationContext context) {
UserEx userEx = context.getBean(UserEx.class); / / 1.
GameEx gameEx = context.getBean(GameEx.class); / / 2.
gameEx.setUserEx(userEx); / / 3.
gameEx.printUserEx(); / / 4.}}Copy the code
After executing, we can observe the related output from the console:
UserEx a no-parameter constructor / / other log information call setUserEx com.com pilemind. Guide. Chapter00. Spring. F14a UserEx @ 5300Copy the code
In the example above, we managed dependencies manually. Can Spring’s IOC container help us manage dependencies? The answer is yes. All we need to do is add the @AutoWired annotation to setter methods that need to inject dependent fields (more on that later) :
@Component
public class GameEx {
private UserEx userEx;
@Autowired // Use the @autowired annotation to indicate that we want the IOC container to inject this instance of UserEx for us
public void setUserEx(UserEx userEx) {
this.userEx = userEx;
System.out.println("Call setUserEx");
}
// Ignore other code
}
Copy the code
At this point, we no longer need to get UserEx and GameEx from the IOC container to manually set up dependencies, because the SpringIOC container already helps us do this:
@SpringBootApplication //
public class IocApp {
public static void main(String[] args) {
/ / 1. The start
ConfigurableApplicationContext context =
SpringApplication.run(IocApp.class, args);
dependencyInject(context);
}
/** * no longer requires manual Settings */
private static void dependencyInject(ConfigurableApplicationContext context) { GameEx gameEx = context.getBean(GameEx.class); gameEx.printUserEx(); }}Copy the code
The final output is the same as the manual setting dependency above.
This chapter summarizes
In this chapter, we learned some of the basics of reflection in Java and learned how to create objects by reflection instead of new. On this basis, we introduced the Spring IOC container to give you an understanding of the underlying principles of Spring IOC. Finally, we introduced the concepts of IOC inversion of control and DI dependency injection and demonstrated them with the Spring framework. In the next chapter, we’ll look at several ways to create beans using the SpringIOC container.
The warehouse address
w4ngzhen/springboot-simple-guide: This is a project that guides SpringBoot users to get started quickly through a series of examples (github.com)