preface
Before understanding IOC, we need to know the following knowledge
Servlet development in Java Web
Coupling / dependency
Dependency injection DI / control inversion IOC
Servlet development in Java Web
Servlet is the basis of Java Web, that is, network programming. The bottom layer of most frameworks we use now is servlet.
Servlet development 1.0
As shown in the figure, there are many servlets in the server. For a server, a Servlet will occupy one thread (resource), so reducing servlets appropriately is the goal in the future
Disadvantages: there are too many servlets, which takes up a lot of resources
Servlet Development 2.0
Implementation method:
When the front end sends a request, it will attach a parameter, that is http://localhost:8080:/pro?operate=add
After receiving the request, the FruitServlet uses request Getparameter ("operate") gets the string add,
Judge by if and perform the subsequent add method.
@WebServlet("/fruit.do") public class FruitServlet extends ViewBaseServlet { private FruitDAO fruitDAO = new FruitDAOImpl(); @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //Set code request.setCharacterEncoding("UTF-8"); String operate = request.getParameter("operate");//Get the parameter of Url, operate if(StringUtil.isEmpty(operate)){ operate = "index" ; } switch(operate){ case "index": index(request,response); break; case "add": add(request,response); break; case "del": del(request,response); break; case "edit": edit(request,response); break; case "update": update(request,response); break; default: throw new RuntimeException("operate Illegal value!"); } } private void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); String fname = request.getParameter("fname"); Integer price = Integer.parseInt(request.getParameter("price")) ; Integer fcount = Integer.parseInt(request.getParameter("fcount")); String remark = request.getParameter("remark"); Fruit fruit = new Fruit(0,fname , price , fcount , remark ) ; fruitDAO.addFruit(fruit); response.sendRedirect("fruit.do"); } }
Advantages: it solves the resource occupation of multiple servlets in a server
Disadvantages: when there are too many services, there are too many switch case codes, which are bloated and difficult to maintain
Servlet development 2.5
Using reflection method to solve the code bloated problem of switch case
We delete the switch case and change it to the reflection method,
Know the currently required Servlet class + method name through the URL;
Get the Servlet class using reflection and run the method;
package com.atguigu.servlet; import java.lang.reflect.Method; public class reflect { public static void main(String[] args) throws Exception { String a = "say"; Class clazz = Class.forName("com.atguigu.servlet.Person");// Get runtime class Object o = clazz.newInstance();// Instantiate class Method[] declaredMethod = o.getClass().getDeclaredMethods();// Get its internal method for (Method method : declaredMethod) { System.out.println(method.getName()); } } } class Person { public void say() { System.out.println("im a good boy"); } public void bye() { System.out.println("im a good boy"); } }
Advantages: reduces the amount of switch case code
Disadvantages: when I have multiple servlets, such as FruitServlet and UserServlet, I will have hundreds or thousands of the same reflection calling methods, resulting in code redundancy
Servlet development 3.0
Due to servlet2 Each controller in 5 has a reflection code, which has high code redundancy. We passed a
Dispatcher / Servlet / central controller / core controller merge the reflection code
Implementation method:
- Prepare a configuration file: ApplicationContext XML, which records the information (name, directory) of multiple controller s
<?xml version="1.0" encoding="utf-8"?> <beans> <!-- this bean The function of the label is the future servletpath The names referred to in correspond to fruit,Then you have to FruitController This class handles --> <bean id="fruit" class="com.atguigu.fruit.controllers.FruitController"/> <bean id="user" class="com.atguigu.fruit.controllers.UserController"/> <bean id="order" class="com.atguigu.fruit.controllers.OrderController"/> <bean id="product" class="com.atguigu.fruit.controllers.ProductController"/> </beans>
-
Store the entity objects in multiple bean tags into a container for plug and play
Step 1 read the configuration file ApplicationContext xml
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("applicationContext.xml"); //1. Create DocumentBuilderFactory DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); //2. Create DocumentBuilder object DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder() ; //3. Create Document object Document document = documentBuilder.parse(inputStream);
Step 2 get all bean nodes and instantiate them into the container
private Map<String,Object> beanMap = new HashMap<>(); //4. Get all bean nodes NodeList beanNodeList = document.getElementsByTagName("bean"); for(int i = 0 ; i<beanNodeList.getLength() ; i++){ Node beanNode = beanNodeList.item(i); if(beanNode.getNodeType() == Node.ELEMENT_NODE){ Element beanElement = (Element)beanNode ; String beanId = beanElement.getAttribute("id"); String className = beanElement.getAttribute("class"); Class controllerBeanClass = Class.forName(className); Object beanObj = controllerBeanClass.newInstance() ; beanMap.put(beanId , beanObj) ;// Parse the XML file to obtain all objects in the configuration file } }
-
Obtain the Controller through the value passed from the URL parameter
@Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //Set code request.setCharacterEncoding("UTF-8"); //Suppose the url is: http://localhost:8080/pro15/hello.do ? operate=add //Then servletPath is: / Hello do // My idea is: // Step 1: / Hello Do - > hello or / fruit do -> fruit // Step 2: Hello - > hellocontroller or fruit - > fruitcontroller String servletPath = request.getServletPath(); servletPath = servletPath.substring(1); int lastDotIndex = servletPath.lastIndexOf(".do") ; servletPath = servletPath.substring(0,lastDotIndex); Object controllerBeanObj = beanMap.get(servletPath);
-
Call the method in the current Controller through the operate parameter
Traverse all methods, and lock the method to be executed through method name + method parameter + method parameter type
Execute this method with invoke(Object obj, Object... args)
Method[] methods = controllerBeanObj.getClass().getDeclaredMethods(); for(Method method : methods){ if(operate.equals(method.getName())){ //1. Obtain request parameters uniformly //1-1. Get the parameters of the current method and return the parameter array Parameter[] parameters = method.getParameters(); //1-2.parameterValues is used to carry the value of the parameter Object[] parameterValues = new Object[parameters.length]; for (int i = 0; i < parameters.length; i++) { Parameter parameter = parameters[i]; String parameterName = parameter.getName() ; //If the parameter names are request, response and session, then it is not the way to obtain parameters in the request if("request".equals(parameterName)){ parameterValues[i] = request ; }else if("response".equals(parameterName)){ parameterValues[i] = response ; }else if("session".equals(parameterName)){ parameterValues[i] = request.getSession() ; }else{ //Get parameter value from request String parameterValue = request.getParameter(parameterName); String typeName = parameter.getType().getName(); Object parameterObj = parameterValue ; if(parameterObj!=null) { if ("java.lang.Integer".equals(typeName)) { parameterObj = Integer.parseInt(parameterValue); } } parameterValues[i] = parameterObj ; } } //2. Method call in controller component method.setAccessible(true); Object returnObj = method.invoke(controllerBeanObj,parameterValues); //3. View processing String methodReturnStr = (String)returnObj ; if(methodReturnStr.startsWith("redirect:")){ //For example: redirect: fruit do String redirectStr = methodReturnStr.substring("redirect:".length()); response.sendRedirect(redirectStr); }else{ super.processTemplate(methodReturnStr,request,response); // For example: "edit" } } }
IOC implementation
Coupling / dependency
When dependency refers to, the successful operation of class a requires the use of a method in class B. In this way, class a depends on class B. That is, if class B is not in class A, it cannot be implemented.
This dependency is equivalent to coupling
Our pursuit is high cohesion and low coupling
As shown in the figure:
The Controller Service Dao is interdependent. This is
MVC mode
MVC: model, View, Controller
View layer: an interface for data presentation and user interaction
Control layer: it can accept the request of the client, and the specific business functions still need to be completed with the help of model components
Model layer: there are many kinds of models: simple pojo/vo(value object), business model components and data access layer components
pojo/vo : Value object AO : Data access object BO : Business object
IOC - control reversal
- Previously, in the Servlet, we created a service object, fruitservice. Fruitservice = new fruitserviceimpl();
If this sentence appears inside a method in the servlet, the scope (life cycle) of the fruitService should be the method level;
If this sentence appears in the servlet class, that is, fruitService is a member variable, the scope (life cycle) of this fruitService should be the servlet instance level - ApplicationContext after us The fruitService is defined in XML. Then, by parsing the XML, the fruitService instance is generated and stored in the beanMap, which is in a BeanFactory
Therefore, we transferred (changed) the life cycle of the previous service instances, dao instances, and so on. Control is transferred from the programmer to BeanFactory. This phenomenon is called inversion of control
DI - dependency injection
- Previously, we found the code in the control layer: fruitservice, fruitservice = new fruitserviceimpl();
The coupling layer and the control layer exist. - After that, we modify the code to fruitservice, fruitservice = null; Then, configure in the configuration file:
code implementation
configuration file
First write the configuration file. In the configuration file, write the class to be used, and declare its name + class path.
// application.xml <?xml version="1.0" encoding="utf-8"?> <beans> <bean id="fruitDAO" class="com.alex.fruit.dao.impl.FruitDAOImpl"/> <bean id="fruitService" class="com.alex.fruit.service.impl.FruitServiceImpl"> <!-- property Tags are used to represent attributes; name Indicates the attribute name; ref Indicates a reference to another bean of id value--> <property name="fruitDAO" ref="fruitDAO"/> </bean> <bean id="fruit" class="com.alex.fruit.controllers.FruitController"> <property name="fruitService" ref="fruitService"/> </bean> </beans>
Controller layer
As shown below, you need to use FruitService in FruitController
public class FruitController { private FruitService fruitService = null ; private String update(Integer fid , String fname , Integer price , Integer fcount , String remark ){ //3. Execute update fruitService.updateFruit(new Fruit(fid,fname, price ,fcount ,remark )); return "redirect:fruit.do"; } private String edit(Integer fid , HttpServletRequest request){ if(fid!=null){ Fruit fruit = fruitService.getFruitByFid(fid); request.setAttribute("fruit",fruit); return "edit"; } return "error" ; } }
Service layer
Write a Service interface to facilitate future maintenance
public interface FruitService { //View specified inventory records by id Fruit getFruitByFid(Integer fid); //Modify specific inventory records void updateFruit(Fruit fruit); }
FruitServiceImpl is an implementation class of a Service
public class FruitServiceImpl implements FruitService { private FruitDAO fruitDAO = null ; @Override public Fruit getFruitByFid(Integer fid) { return fruitDAO.getFruitByFid(fid); } @Override public void updateFruit(Fruit fruit) { fruitDAO.updateFruit(fruit); } }
DAO layer
FruitDAOImpl inherits the JDBC baseDAO and implements the FruitDAO interface at the same time
public class FruitDAOImpl extends BaseDAO<Fruit> implements FruitDAO { @Override public Fruit getFruitByFid(Integer fid) { return super.load("select * from t_fruit where fid = ? " , fid); } @Override public void updateFruit(Fruit fruit) { String sql = "update t_fruit set fname = ? , price = ? , fcount = ? , remark = ? where fid = ? " ; } }
BeanFactory
BeanFactory is used to parse XML files and obtain objects declared in any XML file.
public interface BeanFactory { Object getBean(String id); }
The implementation class of BeanFactory is DOM operation
Step 1 get Document object
Read configuration file
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("applicationContext.xml"); //1. Create DocumentBuilderFactory DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); //2. Create DocumentBuilder object DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder() ; //3. Create Document object Document document = documentBuilder.parse(inputStream);
Step 2 store the objects in the configuration file into the container
Parse Document
Through bean tag id attribute class attribute + reflection = instantiate object + store in container m
//4. Get all bean nodes NodeList beanNodeList = document.getElementsByTagName("bean"); for(int i = 0 ; i<beanNodeList.getLength() ; i++){ Node beanNode = beanNodeList.item(i); if(beanNode.getNodeType() == Node.ELEMENT_NODE){ Element beanElement = (Element)beanNode ; String beanId = beanElement.getAttribute("id"); String className = beanElement.getAttribute("class"); Class beanClass = Class.forName(className); //Create bean instance Object beanObj = beanClass.newInstance() ; //Save the bean instance object to the map container beanMap.put(beanId , beanObj) ; //So far, it should be noted that the dependency between bean s has not been set } }
Step3 dependencies between assembly objects (dependency injection)
In the previous step, we instantiated the object through bean tag id attribute, class attribute + reflection = instantiation
However, as shown below, the property tag in the XML file has not been parsed, indicating that we have not carried out dependency injection
// application.xml <?xml version="1.0" encoding="utf-8"?> <beans> <bean id="fruitDAO" class="com.alex.fruit.dao.impl.FruitDAOImpl"/> <bean id="fruitService" class="com.alex.fruit.service.impl.FruitServiceImpl"> <!-- property Tags are used to represent attributes; name Indicates the attribute name; ref Indicates a reference to another bean of id value--> <property name="fruitDAO" ref="fruitDAO"/> </bean> <bean id="fruit" class="com.alex.fruit.controllers.FruitController"> <property name="fruitService" ref="fruitService"/> </bean> </beans>
Dependency injection
Parsing Document files through DOM operations
Use if to judge whether the bean tag contains the property tag,
If yes, inject attributes through reflection
for(int i = 0 ; i<beanNodeList.getLength() ; i++){ Node beanNode = beanNodeList.item(i); if(beanNode.getNodeType() == Node.ELEMENT_NODE) { Element beanElement = (Element) beanNode; String beanId = beanElement.getAttribute("id"); NodeList beanChildNodeList = beanElement.getChildNodes(); for (int j = 0; j < beanChildNodeList.getLength() ; j++) { Node beanChildNode = beanChildNodeList.item(j); if(beanChildNode.getNodeType()==Node.ELEMENT_NODE && "property".equals(beanChildNode.getNodeName())){ Element propertyElement = (Element) beanChildNode; String propertyName = propertyElement.getAttribute("name"); String propertyRef = propertyElement.getAttribute("ref"); //1) Find the instance corresponding to propertyRef Object refObj = beanMap.get(propertyRef); //2) Set refObj to the property property property of the corresponding instance of the current bean Object beanObj = beanMap.get(beanId); Class beanClazz = beanObj.getClass(); Field propertyField = beanClazz.getDeclaredField(propertyName); propertyField.setAccessible(true); propertyField.set(beanObj,refObj);// Injection by reflection } } } }
Summary:
-
Write Servlet classes one by one to realize a basic function
-
There are too many Servlet classes. Merge the servlets of a class of entities and use switch case to realize functions
-
**Get the method by reflecting * * instead of using switch case, which reduces the amount of code
-
Since multiple servlets each have to write reflection code (fruit, employee and customer), resulting in code redundancy, the code is simplified by extracting the common part
This public part (dispatcher) needs to judge which class and method to call through URL parameters. When the judgment is successful, it will call the method directly. That is, there is a dispatcher between the controller and the client, and the dispatcher is responsible for calling the methods in the controller.
-
Service is introduced to further refine the whole process. As shown above, many operation related codes are still stored in the controller. These methods are extracted and placed in a single layer, that is, the service layer.
-
So far, we can find that we now have many layers of Controller, Service and DAO. There is a problem with these layers: too many dependencies and too high coupling.
Solution: create a factory, BeanFactory, through the configuration file, which instantiates each object for us.
Core idea: change the life cycle of dependent classes.
Original: FruitService fruitService = new FruitServiceImpl(); Write in a class
Now: FruitService fruitService = null, Assign this object by reflection,
Original: the life cycle of the fruitService changes with a class or a method (mainly depending on where the new comes out). When a class is destroyed, the fruitService is also destroyed
Now: fruitService is placed in a Map container, and its life cycle will not change with the objects that depend on it. It is only controlled by the Map container.
Benefits: it is conducive to code maintenance,
In the future, if a large number of dependent objects pass fruitservice, fruitservice = new fruitserviceimpl();
So if I change fruitserviceimpl() - > fruitserviceimpl2(), I need to change all fruitservices. Fruitservice = new fruitserviceimpl();
Using the configuration file, I only need to change the bean tag in the configuration file,
Greatly reduce the cost of code maintenance.