After reading this article, don’t say you don’t know what IoC is?

Have a dream, feelings, temperature, dry goods full; Follow me on wechat search [PoXing].

preface

Many friends may have some doubts about Spring’s IoC. What is inversion of control? Why should I use IoC to give control to the container and what’s in it for me? The book is only theoretical, I can not understand the benefits of Spring IoC when used in comparison, what are the benefits of hosting by Spring? Now I feel that using Spring’s set injection is like looking at the code too much. I don’t understand what the advantages are. Based on the above let you completely understand IoC, no longer confused

Without further ado, let’s get started!

Native to the Servlet

Let’s start with the three-tier architecture (MVC) of the native Servlet era

Create a Maven project to introduce dependencies

 <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>

Copy the code

Create the test

@WebServlet(urlPatterns = "/poXing")
public class PoXingServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.getWriter().println("PoXingServlet run ......"); }}Copy the code

Browser access

Access path (http://ip: http://localhost:8080/spring-framework-ioc-learning/poXing port / [context – path (default is able to modify project name)] / uri)

The browser can print PoXingServlet run…… The base Servlet is now complete

Further optimize adding services and DAOs

For the sake of simple demonstration, there is no dependency related to input library. Therefore, Dao here only simulates the existence of database.

No further

The project directory creates the following classes and interfaces

The components and dependencies in a three-tier architecture should look like the following figure

Add the Dao

public interface IDemoDao {
    List<String> findAll(a);
}


Copy the code
public class DemoDaoImpl implements IDemoDao {
    @Override
    public List<String> findAll(a) {
        // For demonstration purposes, the database list result is returned from the database emulation without connecting to the database
        return Arrays.asList("aaa"."bbb"."ccc"); }}Copy the code

Add the Service

public interface IDemoService {
    List<String> findAll(a);
}

Copy the code
public class DemoServiceImpl implements IDemoService {

    private IDemoDao demoDao = new DemoDaoImpl();

    @Override
    public List<String> findAll(a) {
        returndemoDao.findAll(); }}Copy the code

Modify DemoServlet

Copy and modify the contents of the PoXingServlet we created earlier

@WebServlet(urlPatterns = "/demoServlet")
public class DemoServlet extends HttpServlet {

    IDemoService demoService = new DemoServiceImpl();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException { resp.getWriter().println(demoService.findAll().toString()); }}Copy the code

Run the test

Access path (http://ip: http://localhost:8080/spring-framework-ioc-learning/demoServlet port / [context – path (default is able to modify project name)] / uri)

The browser can output [‘ AAA ‘, ‘BBB ‘,’ CCC ‘] when the base Servlet is complete

The infrastructure is complete, and now is our focus! Our focus!! Our focus!!

Well, the requirements start to change

Now that you have completed the development work, you can add, delete, change, and check the Mysql database. Although I did not add, delete or change) the project was delivered immediately, at this time your leader (XX leader does not know technology at all) called DA.

I heard that big companies use Oracle database and customers seem to think Oracle database is more reliable. Well, you can switch to Oracle database for me

At this point in your mind should be such XX leader, XX quit,

Wood method ah, look for next home too troublesome, still want just rice at present, obey za small za humble za is the wrong za of dry rice person za changes

Modify the data

For the database switch we know not only to modify the database connection pool and other related configuration but also to modify the corresponding SQL statement (specific SQL for different database writing method is not the same drop, paging is not the same), how to do? Change the Dao layer and start modifying all Dao implementation classes in the project, namely DaoImpl

public class DemoDaoImpl implements IDemoDao {
    @Override
    public List<String> findAll(a) {
        // For demonstration purposes, the database list result is returned from the database emulation without connecting to the database
// return Arrays.asList("aaa", "bbb", "ccc");
        // Simulate a statement modified to Orcale SQL
        return Arrays.asList("Xx Leader (Oracle Database)"."Xx Leader (Oracle Database)"."Xx Leader (Oracle Database)"); }}Copy the code

Ha ha, check all the automated tests have been executed. It’s time to fish…

The dudu dudu requirement has changed again

The project is finally finished to deliver, dried a cup of wolfberry red date tea, fish ready to work…

That, that, that… How can Oracle database cost money? The company has not received payment from the customer recently. Please change the database back to MySQL and we will consider using Oracle after the customer has received payment

This is when you…

Ah again again again 2 I think probably also can have again and again whole this TIME I want to think of a way I put your again and again 4 direct to you get on the impact of the province I touch fish.

Introducing static factories

I’m going to create a static factory, whatever database you want, and I’ll come up with a data base for you to temporarily name it BeanFactory. Don’t ask me why I came up with this name. I’m humble

public class BeanFactory {
    public static IDemoDao getDemoDao(a) {
        // Return the mysql Dao
        // return new DemoDaoImpl();
        // Return the Oracle Dao
        return newDemoOracleDaoImpl(); }}Copy the code

Transform the Service implementation to ServiceImpl

The Dao referenced in ServiceImpl can no longer be manually new and should be returned by a static method from the BeanFactory

public class DemoServiceImpl implements IDemoService {

// private IDemoDao demoDao = new DemoDaoImpl();
    private IDemoDao demoDao = BeanFactory.getDemoDao();

    @Override
    public List<String> findAll(a) {
        returndemoDao.findAll(); }}Copy the code

Well, even if you change it again, it won’t affect me anymore. I’m just changing the static method in the BeanFactory

Now the project can finally be delivered, finally…..

Sorry (not what you had in mind) Everything went well the boss was satisfied the customer was satisfied the meeting should draw you a cake first

A new problem has arisen

The system is up and running and you’re already moving on to your next project, but… However, your boss feels that your tasks are limited recently and asks you to optimize and expand the last project. At this time, you open the old project again and find that the project does not compile properly. Delete demodaoimp.java here to demonstrate that the compilation fails

@beanFactory (DemoDaoImpl. Java) {@beanfactory (DemoDaoImpl. Java) {@beanfactory (DemoDaoImpl. Java) {@beanfactory (DemoDaoImpl

Dependencies lead to tight coupling


public class BeanFactory {
    public static IDemoDao getDemoDao(a) {
        // Return the mysql Dao
        // Failed to compile because DemoDaoImpl. Java does not exist
         return new DemoDaoImpl();
        // Return the Oracle Dao
// return new DemoOracleDaoImpl();}}Copy the code

The BeanFactory class is red because DemoDaoImpl. Java does not exist, so the compilation cannot rely on DemoDaoImpl as strongly as the current BeanFactory does

Resolve tight coupling

I can’t do anything without this Java file. What if tomorrow DemoOracleDaoImpl is gone again? I can’t keep playing around with nothing and all of a sudden you have an Epiphany and it seems like reflection might be able to solve this problem and reflection can declare a class’s fully qualified class name, and then get its bytecode, and that can also construct an object

So this is what BeanFactory looks like

public class BeanFactory {

    public static IDemoDao getDemoDao(a) {
        try {
            return (IDemoDao) Class.forName("com.huodd.dao.impl.DemoDaoImpl").newInstance();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("IDemoDao instantiation error, cause: "+ e.getMessage()); }}}Copy the code

The compilation problem is now resolved, and while DemoService has problems with initialization, the project is at least not failing to compile

Introduction of weak dependencies

Currently, reflection is compiled and passed, but after the project starts, ClassNotFoundException will be thrown because the BeanFactory does not have DemoDaoImpl when it constructs it, but the BeanFactory throws a ClassNotFoundException against DemoDaoImpl The degree of dependence (you are willing to throw you throw don’t affect my compilation) even if the top is weak dependence

Hard coded

DemoDaoImpl. Java = DemoDaoImpl. Java = DemoDaoImpl. Java = DemoDaoImpl. I wrote the fully qualified class name in the factory class, and I had to recompile the project to make it work when I switched databases, so there should be a solution. Think about it.

Import externalized configuration files

I’m so smart that you came up with the idea of using IO to store and configure the file so every time the BeanFactory is initialized it reads the configuration file and I’ll just change the configuration file next time and the hard coding problem will be solved

Factory. The properties to create
demoDao=com.huodd.dao.impl.DemoDaoImpl

Copy the code

In order to obtain the fully qualified class name, we give the class an “alias “(similar to the alias idea in mybatis XML), so that we can directly use the” alias “to find the fully qualified name of the corresponding class.

The BeanFactory transformation
public class BeanFactory {
    private static Properties properties;

    // Initialize properties with a static code block and load the factord.properties file
    static {
        properties = new Properties();
        try {
            // You must use the classloader to read the configuration files under the resource folder
            properties.load(BeanFactory.class.getClassLoader().getResourceAsStream("factory.properties"));
        } catch (IOException e) {
            // Static initialization of the BeanFactory class failed, so there is no need to continue
            throw new ExceptionInInitializerError("BeanFactory initialize error, cause: "+ e.getMessage()); }}public static IDemoDao getDemoDao(a) {
        String beanName = properties.getProperty("demoDao");
        try{ Class<? > beanClazz = Class.forName(beanName);return (IDemoDao) beanClazz.newInstance();
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("BeanFactory have not [" + beanName + "] bean!", e);
        } catch (IllegalAccessException | InstantiationException e) {
            throw new RuntimeException("[" + beanName + "] instantiation error!", e); }}}Copy the code

The name getDemoDao will need to be changed to getBean. The name getBean will need to be changed to getBean and the name will need to be changed to getBean

public static IDemoDao getBean(String beanAlias) {
        String beanName = properties.getProperty("beanAlias");
        try{ Class<? > beanClazz = Class.forName(beanName);return (IDemoDao) beanClazz.newInstance();
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("BeanFactory have not [" + beanName + "] bean!", e);
        } catch (IllegalAccessException | InstantiationException e) {
            throw new RuntimeException("[" + beanName + "] instantiation error!", e); }}Copy the code
ServiceImpl transformation

DemoServiceImpl can’t call getDao (let’s change it to getBean(String beanAlias)).

public class DemoServiceImpl implements IDemoService {

	IDemoDao demoDao = (IDemoDao) BeanFactory.getBean("demoDao");
    
	@Override
    public List<String> findAll(a) {
        returndemoDao.findAll(); }}Copy the code

At this point, you suddenly realize that you can now externalize all the components you want to extract!

PS: Go to DemoServlet and correct the tight coupling of DemoServiceImpl

Forget it simply In the factory. The properties external configuration files to join demoService = com. Huodd. Service. Impl. DemoServiceImpl DemoService = (IDemoService) beanFactory.getBean (“DemoServiceImpl”);

External configuration

For such configurations and attributes that may change, they are usually extracted into some configuration files (properties, XML, JSON, YML, etc.) rather than hard-coded directly in the source code, and then loaded and parsed by the program to achieve dynamic configuration and reduce configuration coupling. Perhaps we can see from this that the IoC idea is not just the brainchild of foreigners who slap their heads around casually

Multiple build problem

As some of you may have noticed, there are also problems with projects that affect performance. The big problem is that BeanFactory#getBean(String beanAlias) creates multiple instances of the same object repeatedly We don’t need to create new object instances every time. The old one is enough

You might want to consider caching the instance that you create every time you read it from the cache. PS: Think about it a little bit more and don’t forget about multi-threaded concurrency

By now, I wonder if you have a new understanding of IOC

Here’s a summary of some of the key points that emerged

  • Static factories can extract and separate multiple dependencies
  • Externalizing the configuration file + reflection solves the configuration hardcoding problem
  • Caches the number of controllable object instances.

Did you solve the confusion of your friends at the beginning of the article?

The idea of IOC was introduced


private IDemoDao dao = new DemoDaoImpl();

private IDemoDao dao = (IDemoDao) BeanFactory.getBean("demoDao");
Copy the code

Compare the above two methods to obtain dao

  • The former is strongly/tightly coupled and the latter is weakly/loosely coupled
  • The former shall be guaranteedDemoDaoImplIt exists in order to compile and the latter is not guaranteedDemoDaoImplExistence can be compiled iffactory.propertiesOccurs when the fully qualified class name declared inClassCastException

Consider the following method of object acquisition: we could have used the above method to declare the implementation class, but if we choose the following method, we no longer declare ourselves, but give the method of object acquisition to the BeanFactory. This idea of handing Control to someone else can be termed the Inverse of Control (IOC). A BeanFactory obtains and creates objects based on the specified beanName, which can be called Dependency Lookup (DL).

The source code for

This article source code can be added to the public number “PoXing” reply [IOC] for access

I am Baoxing, the best time to start doing something is either ten years ago or now. Thank you for your likes, favorites and comments


Article update continuously, you can search wechat [Poxing] for the first time to read