IoC control reversal
- IoC control inversion, fully known as Inverse of Control, is a design concept
- Agents create and manage objects, and consumers obtain objects through agents
- The purpose of IoC is to reduce the direct coupling between programs
Solve what problem?
The direct drinking of objects leads to the rigid Association of objects, and the program is difficult to expand. For example, if customers want to eat all kinds of apples, they need customers to buy apples all over the world. It's very troublesome.
Add the Ioc container to manage the objects uniformly, so that the object association becomes weak coupling. Just like the above scenario: if there are vendors, customers don't have to go everywhere to buy fruit. After the vendors buy, customers buy responsive apples according to their own needs.
DI dependency injection
- IoC is a design concept, a standard followed by modern programming and a grand goal
- DI(Dependency Injection) is a specific technical implementation and a micro implementation
- DI uses reflection technology in Java, which is object injection
Spring
meaning
- Spring can be viewed from both narrow and broad perspectives
- In a narrow sense, Spring refers to the Spring framework
- In a broad sense, Spring refers to the Spring ecosystem
Narrow Spring framework
- The Spring framework is a one-stop solution to the complexity of enterprise development
- The core of SPring framework is IoC container and AOP aspect oriented programming
- Spring IoC is responsible for creating and managing system objects and extending functions on this basis
Generalized Spring ecosystem
Spring IoC containerIoC container is the foundation of Spring ecology, which is used to uniformly create and manage object dependencies
As above: the Spring IoC container injects A's dependent B objects, and users only need to extract them.
Spring IoC container responsibilities
- The control of the object is uniformly managed by a third party (IoC control reversal)
- Using Java reflection technology to realize runtime object creation and Association (DI dependency injection)
- Improving maintainability and scalability of applications based on configuration
Spring IoC first experience
eg: for example, three children Lily, Andy and Luna like to eat sweet, sour and soft apples respectively. There are three apples on the plate: Red Fuji, green apple and Jin Shuai.
How do children get their favorite apples?
Apple.class
package com.imooc.spring.ioc.entity; public class Apple { private String title; private String color; private String origin; public Apple() { //System.out.println("Apple object has been created," + this); } public Apple(String title, String color, String origin) { this.title = title; this.color = color; this.origin = origin; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public String getOrigin() { return origin; } public void setOrigin(String origin) { this.origin = origin; } }
Child.class
package com.imooc.spring.ioc.entity; public class Child { private String name; private Apple apple; public Child() { } public Child(String name, Apple apple) { this.name = name; this.apple = apple; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Apple getApple() { return apple; } public void setApple(Apple apple) { this.apple = apple; } public void eat() { System.out.println(name + "Yes" + apple.getOrigin() + "Planted" + apple.getTitle()); } }
Traditional way:
Application.class
public class Application { public static void main(String[] args) { Apple apple1 = new Apple("Red Fuji", "gules", "Europe"); Apple apple2 = new Apple("green apple", "green", "Central Asia"); Apple apple3 = new Apple("Jin Shuai", "yellow", "China"); Child lily = new Child("Lily", apple1); Child andy = new Child("Andy", apple2); Child luna = new Child("Luna", apple3); lily.eat(); andy.eat(); luna.eat(); } }
Output results:
But this will have a disadvantage. If we need to change our favorite Apple later, we need to modify it here in the source code. In the development process, the source code often involves a lot of content, the modification is complex, it is easy to affect other places, and it is easy to make mistakes.
How to use IoC container:
Create the applicationcontext.xml file in the resources directory:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- stay IoC When the container starts,Automatically by Spring instantiation Apple object,Name sweetApple Put into container --> <bean id="sweetApple"> <property name="title" value="Red Fuji"></property> <property name="origin" value="Europe"></property> <property name="color" value="gules"></property> </bean> <bean id="sourApple"> <property name="title" value="green apple"></property> <property name="origin" value="Central Asia"></property> <property name="color" value="green"></property> </bean> <bean id="softApple"> <property name="title" value="Jin Shuai"></property> <property name="origin" value="China"></property> <property name="color" value="yellow"></property> </bean> <bean id="rdApple"> <property name="title" value="Snake fruit"></property> <property name="origin" value="U.S.A"></property> <property name="color" value="gules"></property> </bean> <bean id="lily"> <property name="name" value="Lily"/> <property name="apple" ref="softApple"/> </bean> <bean id="andy"> <property name="name" value="Andy"/> <property name="apple" ref="rdApple"/> </bean> <bean id="luna"> <property name="name" value="Luna"/> <property name="apple" ref="sweetApple"/> </bean> </beans>
SpringApplication.class
public class SpringApplication { public static void main(String[] args) { //Create a Spring IoC container and instantiate objects in the container according to the configuration file ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); Apple sweetApple = context.getBean("sweetApple", Apple.class); System.out.println(sweetApple.getTitle()); //Extract the object with beanId=lily from the IoC container Child lily = context.getBean("lily", Child.class); lily.eat(); Child andy = context.getBean("andy", Child.class); andy.eat(); Child luna = context.getBean("luna", Child.class); luna.eat(); } }
Execution results:
The advantage of this is that when it comes to modification, we only need to modify the content in applicationContent.xml
Using XML to implement Spring IoCapplicationContext.xml mentioned above is a way to implement Spring IoC by using XML files.
Implementation method:
- Instantiation of objects based on construction method
- Based on dynamic factory instantiation
- Factory instance based method instantiation
Spring framework component module
ApplicationContext implementation class
- ClassPathXmlApplicationContext
- AnnotationConfigApplicationContext
- WebApplicationContext
Instantiation of objects based on construction method
Default construction method
In the applicationContext.xml file:
<!--bean Labels create objects using the default construction method--> <bean id="apple1">
In Apple.class, it is modified as:
public Apple() { System.out.println("Apple Object created," + this); }
Printout:
Structural method with parameters
applicationContext.xml
<!--Instantiate an object using the parameterized construction method--> <bean name="apple2"> <constructor-arg name="title" value="Red Fuji"/> <constructor-arg name="color" value="gules"/> <constructor-arg name="origin" value="Europe"/> <constructor-arg name="price" value="19.8"/> </bean> <bean id="apple3"> <constructor-arg index="0" value="Red Fuji"/> <constructor-arg index="1" value="Europe"/> <constructor-arg index="2" value="gules"/> <constructor-arg index="3" value="19.8"/> </bean>
Apple.class
public Apple(String title, String origin, String color, Float price) { System.out.println("Creating objects with parametric construction methods, " + this); this.title = title; this.color = color; this.origin = origin; this.price = price; }
Printout:
Factory instantiated objects
Static factory
/** * Static factory creates objects through static methods to hide the details of creating objects */ public class AppleStaticFactory { public static Apple createSweetApple(){ //logger.info("") Apple apple = new Apple(); apple.setTitle("Red Fuji"); apple.setOrigin("Europe"); apple.setColor("gules"); return apple; } }
In applicationContext.xml
<!--Get objects using static factories--> <bean id="apple4" factory-method="createSweetApple"/>
Test:
Apple apple4 = context.getBean("apple4", Apple.class); System.out.println(apple4.getTitle());
Output:
Factory example
/** * Factory instance method creation object refers to the process that IoC container instantiates the factory class and calls the corresponding instance method to create an object */ public class AppleFactoryInstance { public Apple createSweetApple(){ Apple apple = new Apple(); apple.setTitle("Red Fuji"); apple.setOrigin("Europe"); apple.setColor("gules"); return apple; } }
In applicationContext.xml
<!--Get objects using factory instance method--> <bean id="factoryInstance"/> <bean id="apple5" factory-bean="factoryInstance" factory-method="createSweetApple"/>
Then test the output
Extract from IoC container Bean
Mode 1: Apple apple = context.getBean("apple", Apple.class); Mode 2: Apple apple = (Apple)context.getBean("apple");
Recommended usage 1
The id and name attributes are the same
- Both bean id and name are unique identifiers of the setting object in the IoC container
- Both are not allowed to duplicate in the same configuration file
- Both allow duplication in multiple configuration files, and the new object overwrites the old object
Differences between id and name attributes
- id requirements are more stringent. Only one object id can be defined at a time (recommended)
- name is more relaxed, allowing multiple object IDs to be defined at a time
- Tips: the naming requirements of ID and name are meaningful and written according to the hump name
<bean name="apple2,apple7"> <constructor-arg name="title" value="Fuji 2"/> <constructor-arg name="color" value="gules"/> <constructor-arg name="origin" value="Europe"/> <constructor-arg name="price" value="29.8"/> </bean> <!-- No, id And name of bean The full name of the class is used as the default bean identification --> <bean> <constructor-arg name="title" value="Fuji 3"/> <constructor-arg name="color" value="gules"/> <constructor-arg name="origin" value="Europe"/> <constructor-arg name="price" value="29.8"/> </bean>
Path matching expression
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
Load multiple profiles:
String[] configLocations = new String[]{"classpath:applicationContext.xml","classpath:applicationContext-1.xml"}; //Initialize the IoC container and instantiate the object ApplicationContext context = new ClassPathXmlApplicationContext(configLocations);
Path expression
Object dependency injection
Dependency injection refers to the operation of assigning objects in the container to other objects by reflection at run time
- Object injection based on setter method
- Injecting objects based on construction method
Object injection based on setter method
< property name = "" value = "" / >: static property
< property name = "" ref = "" / >: dynamic properties
<bean id="sweetApple"> <!-- IoC The container automatically invokes the runtime using the reflection mechanism setXXX Method to assign a value to a property --> <property name="title" value="Red Fuji"/> <property name="color" value="gules"/> <property name="origin" value="Europe"/> <property name="price" value="19.8"/> </bean> <bean id="lily"> <property name="name" value="Lily"/> <!-- utilize ref Inject dependent objects --> <property name="apple" ref="sweetApple"/> </bean>
Advantages of dependency injection
give an example:
applicationContext-dao.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="bookDao"> </bean> </beans>
applicationContext-serice.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="bookService"> <!--id=bookDao--> <property name="bookDao" ref="bookDao"/> </bean> </beans>
BookDao
public interface BookDao { public void insert(); }
BookDaoImpl
public class BookDaoImpl implements BookDao { public void insert() { System.out.println("towards MySQL Book Insert a piece of data into the table"); } }
BookService
public class BookService { private BookDao bookDao ; public void purchase(){ System.out.println("Executing book purchase business method"); bookDao.insert(); } public BookDao getBookDao() { return bookDao; } public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } }
BookShopApplication
public class BookShopApplication { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext-*.xml"); BookService bookService = context.getBean("bookService", BookService.class); bookService.purchase(); } }
Output:
Advantages: for example, the database is migrated from MySQL to Oracle. At this time, you only need to change the class of BookDao Bean to Oracle
Injection collection object
Inject List
Injection set
Injection map
Inject Properties
key and value in Properties can only be String type
View objects in container
//Gets an array of all beanids in the container String[] beanNames = context.getBeanDefinitionNames(); for (String beanName:beanNames){ System.out.println(beanName); System.out.println("type:" + context.getBean(beanName).getClass().getName()); System.out.println("content:" + context.getBean(beanName)); }
Computer computer = context.getBean("com.imooc.spring.ioc.entity.Computer", Computer.class);
When there are multiple beans of the same class:
<bean> <constructor-arg name="brand" value="MICROSTAR"/> <constructor-arg name="type" value="Desktop"/> <constructor-arg name="sn" value="8389280012"/> <constructor-arg name="price" value="3000"/> </bean> <bean> <constructor-arg name="brand" value="ASUS"/> <constructor-arg name="type" value="notebook"/> <constructor-arg name="sn" value="9089380012"/> <constructor-arg name="price" value="6000"/> </bean>
Computer computer1 = context.getBean("com.imooc.spring.ioc.entity.Computer#0", Computer.class); Computer computer1 = context.getBean("com.imooc.spring.ioc.entity.Computer#1", Computer.class);Scope and life cycle of Bean object
bean scope property
- The bean scope property is used to determine when an object is created and scope
- The bean scope configuration will affect the number of objects in the container
- By default, bean s will be instantiated automatically after the IoC container is created, which is globally unique
scope usage
bean scope attribute list
Thread safety of singleton
singleton is a single instance of multi-threaded execution in the container, which has thread safety risks
Under single thread:
In multithreading: after user A operates a.setNum(1), user B operates a.setNum(2) in another thread . At this time, 2 will appear when user A prints a.num, which is different from the setting value of user A
Multiple cases of prototype
prototype has multiple instances in the container, occupying more resources, and there is no thread safety problem
Comparison between singleton and prototype
bean lifecycle
Detail adjustment
- prototype enables object creation with init_method delay to execution of business
- prototype makes the object no longer managed by the IoC container and no longer triggers the destroy method
- Delaying loading the lazy init attribute delays object creation and initialization until the code execution phase
Application of life cycle in actual combat
Initialization of singleton and prototype
Initialization of singleton:
In applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="userDao"/> </beans>
Add in spring application:
public class SpringApplication { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); } }
Printout:
That is, when the IoC container is initialized, bean s are created for us
prototype initialization:
In applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="userDao" scope="prototype"/> </beans>
Then test in spring application:
public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); System.out.println("======IoC Container initialized======="); UserDao userDao1 = context.getBean("userDao", UserDao.class); }
Output is:
That is, when the IoC container is created, it does not initialize the bean object for us, but only when we obtain the object. The initialization order of the singleton mode is consistent with the writing order.
Next, two bean objects will be initialized
<bean id="userDao" scope="prototype"/> <bean id="userService"> <property name="userDao" ref="userDao"/> </bean>
In most cases, Dao layer, server layer and control layer are singleton.
Implement a minimalist IoC containerDirectory structure:
Apple class
package com.imooc.spring.ioc.entity; public class Apple { private String title; private String color; private String origin; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public String getOrigin() { return origin; } public void setOrigin(String origin) { this.origin = origin; } }
Then add bean information in applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans> <bean id="sweetApple"> <property name="title" value="Red Fuji"/> <property name="color" value="gules"/> <property name="origin" value="Europe"/> </bean> </beans>
How to customize the logging profile:
Interface: ApplicationContext
package com.imooc.spring.ioc.context; public interface ApplicationContext { public Object getBean(String beanId); }
Implementation class ClassPathXmlApplicationContext
public class ClassPathXmlApplicationContext implements ApplicationContext { private Map iocContainer = new HashMap(); /** * Read configuration file */ public ClassPathXmlApplicationContext() { try { String filePath = this.getClass().getResource("/applicationContext.xml").getPath(); // URL decoding filePath = new URLDecoder().decode(filePath, "UTF-8"); /** * Introducing dom4j and jaxen * Dom4j Is an XML parsing component of Java * Jaxen Is an Xpath expression interpreter */ SAXReader reader = new SAXReader(); Document document = reader.read(new File(filePath)); // Read "bean" tag List<Node> beans = document.getRootElement().selectNodes("bean"); for (Node node : beans) { Element ele = (Element) node; String id = ele.attributeValue("id"); String className = ele.attributeValue("class"); Class c = Class.forName(className); Object obj = c.newInstance(); List<Node> properties = ele.selectNodes("property"); for (Node p : properties) { Element property = (Element) p; String propName = property.attributeValue("name"); String propValue = property.attributeValue("value"); // set method String setMethodName = "set" + propName.substring(0, 1).toUpperCase() + propName.substring(1); System.out.println("Ready to execute" + setMethodName + "Method injection data"); Method setMethod = c.getMethod(setMethodName, String.class); setMethod.invoke(obj, propValue);//Inject data through setter method } iocContainer.put(id, obj); } System.out.println(iocContainer); System.out.println("IOC Container initialization completed"); } catch (Exception e) { e.printStackTrace(); } } public Object getBean(String beanId) { return iocContainer.get(beanId); } }
Then test:
public class Application { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext(); Apple apple = (Apple)context.getBean("sweetApple"); System.out.println(apple); } }
Execution results:
Implementing Spring IoC with annotations
Annotation based advantages
- Get rid of cumbersome XML bean s and dependency injection configuration
- Based on the "declarative" principle, it is more suitable for lightweight modern enterprise applications
- Make the code more readable, and the R & D personnel have a better development experience
Three types of notes:
- Component type annotation - declares the functions and functions of the current class
- Auto assembly annotation - automatically inject objects according to special attributes
- Metadata annotation - more detailed annotation of auxiliary IoC container management objects
Annotation of four component types
- @Compponent : Component annotation, general annotation, and the class described by this annotation will be managed and instantiated by the IoC container
- @Controller : Semantic annotation, indicating that the current class is the controller class in MVC application
- @Service.: semantic annotation indicating that the current class is a service business service class
- @Repository: semantic annotation, which indicates that the current class is used for the business persistence layer, and usually describes the corresponding Dao class
Two types of automatic assembly annotation
- Assemble by type
- @Autowired: dynamically inject attributes according to the object type in the container, which is provided by the Spring mechanism
- @Inject: Based on JSR-330(Dependency Injection for Java) standard, the others are the same as @ Autowired, but the required attribute is not supported
- Assemble by name
- @Named: used with @ Inject, JSR-330 specification, automatically assemble attributes by attribute name
- @Resource: Based on JSR-330 specification, intelligent matching is preferred by name and then by type
Metadata annotation
@Read properties file for Value
@Value("com.imooc") private String config;
It is equivalent to that during initialization, the value of config is "com.inooc", and the main user loads the data in the configuration file: @ Value("$")
Using Java Config to implement Spring IoCAdvantages based on Java Config
- Completely get rid of the shackles of XML and use independent Java classes to manage objects and dependencies
- The annotation configuration is relatively decentralized, and the configuration can be centrally managed by using Java Config
- Dependency checking can be performed at compile time, which is not error prone
Java Config core annotation
Java Config initialization mode
give an example:
package com.imooc.spring.ioc; import com.imooc.spring.ioc.controller.UserController; import com.imooc.spring.ioc.dao.EmployeeDao; import com.imooc.spring.ioc.dao.UserDao; import com.imooc.spring.ioc.service.UserService; import org.springframework.context.annotation.*; @Configuration //The current class is a configuration class that replaces applicationContext.xml @ComponentScan(basePackages = "com.imooc") public class Config { @Bean //Java Config uses methods to create objects, puts the returned object of the method into the container, and beanId = method name public UserDao userDao(){ UserDao userDao = new UserDao(); System.out.println("Created" + userDao); return userDao; } @Bean //Java Config uses methods to create objects, puts the returned object of the method into the container, and beanId = method name @Primary public UserDao userDao1(){ UserDao userDao = new UserDao(); System.out.println("Created" + userDao); return userDao; } @Bean //Try to inject by name first. If name does not exist, inject by type public UserService userService(UserDao udao , EmployeeDao employeeDao){ UserService userService = new UserService(); System.out.println("Created" + userService); userService.setUserDao(udao); System.out.println("call setUserDao:" + udao); userService.setEmployeeDao(employeeDao); return userService; } @Bean //<bean id="xxx" clas="xxx"> @Scope("prototype") public UserController userController(UserService userService){ UserController userController = new UserController(); System.out.println("Created" + userController); userController.setUserService(userService); System.out.println("call setUserService:" + userService); return userController; } }
Test:
public class SpringApplication { public static void main(String[] args) { //Configuring initialization of IoC container based on Java Config ApplicationContext context = new AnnotationConfigApplicationContext(Config.class); System.out.println("========================="); String[] ids = context.getBeanDefinitionNames(); for(String id : ids){ System.out.println(id + ":" + context.getBean(id)); } } }