Factory pattern - understand Spring's Bean factory (classic example of horse soldier)

Factory pattern - understand Spring's Bean factory

Connect the scene of "Lao Zhang drives to the northeast" in object-oriented. Link name

Encapsulate the vehicle in "Lao Zhang drives to the northeast", encapsulate the vehicle Car

Only one car is given to the driver (single case, multiple cases)

By the way, explain the single example

It is required to have only one Car. Others can't new Car. Only Car can control the logic of new Car. Privatization construction method, others can not new.

/**
 * Vehicle Car
 * 
 */
public class Car {
	
	//private static Car car = new Car();
	private Car(){}
	
	public static Car getInstance(){
		return new Car();
	}
	
	public void run(){
		System.out.println("Running in smoke...");
	}

}

Factories produce their own products independently and no longer rely on new. For example, if you want to new a drawer in my house, you can take the money if you want, but you can't.

But I want to provide you with a method: getChouTi(); I can make various restrictions in the get method.

For example, if you return the getInstance method of Car, you can make logical judgment.

/**
 * Vehicle Car
 */
public class Car {

    private Car(){}

    public static Car getInstance(){
        if(Have a driver's license){
            return new Car();
        }
        return null;
    }
    public void run(){
        System.out.println("Running in smoke...");
    }
}

Go back to the above requirements. There is only one Car. Do this: create a new Car. When calling getInstance, return the Car.

public class Car {
	
	private static Car car = new Car();
	private Car(){}
	
	public static Car getInstance(){
		return car;
	}
	
	public void run(){
		System.out.println("Running in smoke...");
	}

}

test

getInstance checks whether it is a car twice:

public static void main(String[] args) {
        Car car1 = Car.getInstance();
        Car car2 = Car.getInstance();
        System.err.println(car1 == car2);
    }

Print: true

Print true to indicate that it is a car

This pattern is called singleton, and some people call this getInstance method static factory method.

Any method that controls the logic of generating objects can be called a factory method.

Multiple cases

If not a Car is returned in the Car class, there is a List containing a pile of cars. When getInstance returns one at random, someone names it --- multiple cases

public class Car {
	
	//private static Car car = new Car();
	private static List<Car> cars = new ArrayList<>();
	
	static{
		//Statically initialize cars
		cars.add(new Car());
		cars.add(new Car());
	}
	
	private Car(){}
	
	public static Car getInstance(){
		//return car;
		//Return a Car randomly, so it's not random here
		return cars.get(1);
	}
	
	public void run(){
		System.out.println("Running in smoke...");
	}

}

JDBC Connection pool, which contains connections, is a number of examples.

Type and production process of any customized vehicle

Naturally, I think of polymorphism. Extract an interface: Moveable, and then let Car implement the Moveable interface:

public interface Moveable {
	void run();
}

Implementation of Car:

public class Car implements Moveable{
	@Override
	public void run() {
		System.out.println("Running in smoke...");
	}
}

Realization of aircraft:

public class Plane implements Moveable{

	@Override
	public void run() {
		System.out.println("Flapping wings and flying...");
	}
}

Test class:

When calling, the parent class reference points to the subclass object, which is polymorphic. I call whoever I new. I change the means of transportation at will:

Moveable m = new Car();
m.run();
m = new Plane();
m.run();
Print results:
Running in smoke...
Flapping his wings and flying...

There are any other means of transportation. For example, if the means of transportation is Harry Potter's broom, you can directly implement the Moveable interface, and you can directly new Broom(); Yes.

The problem now is that you can use any new vehicle, and the construction method is open. Now if you want to customize the production process of any vehicle. With the idea of the above single example, the first thing I think of now is to write a static method in the vehicle class to control the process of new. Here, for example, aircraft, take out a separate class of the process of generating aircraft, such as aircraft factory PlaneFactory:

//Aircraft factory
public class PlaneFactory {
	public Plane createPlane(){
		//Single case, multiple cases, condition check and self control
		return new Plane();
	}
}

Test:

//Aircraft factory
		PlaneFactory factory = new PlaneFactory();
		Moveable m = factory.createPlane();
		m.run();

Print results:
Flapping wings and flying...

If you want to have a Car factory now, it's very simple, that's it:

//Car factory
public class CarFactory {
	public Car createPlane(){
		//Single case, multiple cases, condition check and self control
		return new Car();
	}
}

Test code:

I used to drive an airplane, but now I want to drive a Car instead. I need to change the airplane factory into a Car factory and call his createCar method. This is too awkward.

//Aircraft factory
		//PlaneFactory factory = new PlaneFactory();
		//Change to Car factory, the whole factory method has to be changed
		CarFactory factory = new CarFactory();
		Moveable m = factory.createCar();
		m.run();

Is there any way to just change the realization of the factory when changing from aircraft to Car? Naturally, I think of polymorphism. If there is polymorphism, there must be parent and child classes. Therefore, the factory class needs to have a parent class. Factory is originally used to generate vehicles. It abstracts a factory that generates vehicles:

//Vehicle factory
public abstract class VehicleFactory {

	//The specific vehicle generated is determined by the subclass, which is abstract here.
	public abstract Moveable create();
}

At this time, let CarFactory and PlaneFactory inherit VehicleFactory:

//Car factory
public class CarFactory extends VehicleFactory{
	
	@Override
	public Moveable create() {
		//Single case, multiple cases, condition check and self control
		return new Car();
	}
}
//Aircraft factory
public class PlaneFactory extends VehicleFactory {
	
	@Override
	public Moveable create() {
		//Single case, multiple cases, condition check and self control
		return new Plane();
	}
}

If you change the implementation of the factory, you can change the means of transportation. For example, if you add a Harry Potter magic Broom, you need to add a Broom class to implement the Moveable interface, and a BroomFactory factory class to inherit the VehicleFactory.

//broom
public class Broom implements Moveable{

	@Override
	public void run() {
		System.out.println("The broom wagged its tail...");
	}
}

//Broom factory
public class BroomFactory extends VehicleFactory {
	
	@Override
	public Moveable create() {
		//Single case, multiple cases, condition check and self control
		return new Broom();
	}
}

At this point, the test code is like this:

//Examples of locomotive factory and new aircraft factory
		VehicleFactory factory = new PlaneFactory();
		Moveable m = factory.create();
		m.run();
		//Change to Car factory
		factory = new CarFactory();
		m = factory.create();
		m.run();
		//Change to broom factory
		factory = new BroomFactory();
		m = factory.create();
		m.run();
Print results;
Flapping wings and flying...
Running in smoke...
The broom wagged its tail...

It is extensible in a certain dimension, which can not only control the type of vehicles, but also control the production process of vehicles. There is only one place that needs to be changed: if you need to change the means of transportation, from the perspective of customers, only the factories of the means of transportation need to be changed, and no other places need to be moved. If the configuration file is used, the code doesn't need this. Just change the configuration file. Later.

Selection of abstract classes and interfaces:

For example, the above vehicle factory is an abstract class, which can also be designed as an interface.

If this concept really exists in our minds, we can use abstract classes,

If this concept is only a feature of some aspects: for example, those who can fly and run, use the interface

If the two concepts are vague and you don't know which one to choose, use the interface. The reason is that after implementing this interface, you can inherit from other abstract classes, which is more flexible.

Abstract factory

Take a look at the JDK. First, take a look at the getInstance method. There are a lot of methods. There are the same getInstance method in different classes.

Most of these are static factory methods. Whether they are single cases or not depends on the specific implementation.

There are many kinds of factories. For example, the following encrypted key is not suitable for new. It can be implemented in a Factory. When it is generated, it can implement various algorithms and detect various qualifications. The construction method of new can only be written dead. When a subclass is implemented in a Factory, it can also control the production process and be more flexible.

In a word, getInstance and Factory are commonly used in JDK. Let's start with the abstract Factory.

Back to the original state, we have a Car.

Control a range of products (vehicles, weapons, food supplies)

Now let this man drive a car, hold an AK47 and eat an apple. This means that these are some columns of products, and the production of these columns of products should be controlled.

For example, you need to decorate Haier's overall kitchen, including microwave oven, range hood, washing machine and induction cooker. A range of products.

public class Car{
	
	public void run() {
		System.out.println("Running in smoke...");
	}
}

public class AK47 {

	public void shoot(){
		System.out.print("Dada dada....");
	}
}
public class Apple {
	public void getName(){
		System.out.println("Apple..."); 
	}
}

Test class:

Car car = new Car();
		car.run();
		AK47 ak = new AK47();
		ak.shoot();
		Apple apple = new Apple();
		apple.getName();
Print:
Running in smoke...
Dada dada....
Apple...

A default factory is required to produce this series of products:

//Default factory
public class DefaultFactory {

	public Car createCar(){
		return new Car();
	}
	public AK47 createAK47(){
		return new AK47();
	}
	public Apple createApple(){
		return new Apple();
	}
}

At this time, the test program can produce this series of products as long as a default factory comes out of new:

DefaultFactory factory = new DefaultFactory();
		Car car = factory.createCar();
		car.run();
		AK47 ak = factory.createAK47();
		ak.shoot();
		Apple apple = factory.createApple();
		apple.getName();
Print:
Running in smoke...
Dada dada....
Apple...

If you need to replace all the products in this series, you can replace this factory:

Here is a new factory, magic factory:

//Harry Potter's magic factory
public class MagicFactory {

	//Transportation: broom
	public Broom createBroom(){
		return new Broom();
	}
	
	//Weapon: magic wand
	public MagicStick createMagicStick(){
		return new MagicStick();
	}
	//Food: poisonous mushroom
	public MushRoom createMushRoom(){
		return new MushRoom();
	}
}

//broom
public class Broom{

	public void run() {
		System.out.println("The broom wagged its tail...");
	}
}


//Weapon: magic wand
public class MagicStick {
}

//Food: poisonous mushroom
public class MushRoom {

}

But at this time, from the perspective of the customer, I want to change the factory from DefaultFactory to MagicFactory:

With previous experience, it is natural to think that the factory cannot create a specific class. If it is an interface / or an abstract class, such as your Car, the factory cannot create a Car. If it is the parent class of Car, the following code can not be changed when changing the factory. Therefore, we need to build an abstract factory, and then let DefaultFactory and MagicFactory inherit / implement this factory. Moreover, the return values of factories are abstract classes or interfaces.

//Abstract factory
public abstract class AbstractFactory {
	//Production vehicles
	public abstract Vehicle createVehicle();
	//Production of weapons
	public abstract Weapon createWeapon();
	//Produce food
	public abstract Food createFood();
}


//vehicle
public abstract class Vehicle {
	//Implementation is determined by subclasses
	public abstract void run();
}

//food
public abstract class Food {
	public abstract void printName();
}


//arms
public abstract class Weapon {
	//
	public abstract void shoot();
}

Product class: abstract classes that inherit products

public class Car extends Vehicle{
	@Override
	public void run() {
		System.out.println("Running in smoke...");
	}
}
//broom
public class Broom extends Vehicle{

	@Override
	public void run() {
		System.out.println("The broom wagged its tail...");
	}
}
//Food: poisonous mushroom
public class MushRoom extends Food {

	@Override
	public void printName() {
		System.out.println("mushroom");
	}

}

public class Apple extends Food {
	@Override
	public void printName() {
		System.out.println("apple");
	}
}

public class AK47 extends Weapon{

	public void shoot(){
		System.out.println("Dada dada....");
	}
}

//Weapon: magic wand
public class MagicStick extends Weapon {
	@Override
	public void shoot() {
		System.out.println("fire hu hu hu ...");
	}

}

Magic factory inherits Abstract Factory:

//Harry Potter's magic factory
public class MagicFactory extends AbstractFactory {

	//Transportation: broom
	public Vehicle createVehicle(){
		return new Broom();
	}
	
	//Weapon: magic wand
	public Weapon createWeapon(){
		return new MagicStick();
	}
	//Food: poisonous mushroom
	public Food createFood(){
		return new MushRoom();
	}
}

DefaultFactory inherits Abstract Factory:

//Default factory
public class DefaultFactory extends AbstractFactory{

	@Override
	public Food createFood() {
		return new Apple();
	}

	@Override
	public Vehicle createVehicle() {
		return new Car();
	}

	@Override
	public Weapon createWeapon() {
		return new AK47();
	}
}

The resulting class structure is as follows:

Test procedure:

//If you change a factory, you only need to change this place. If you change a factory, you can change all the series of products produced
		AbstractFactory factory =  new MagicFactory(); //new DefaultFactory();
		//Change a factory
		Vehicle vehicle = factory.createVehicle();
		vehicle.run();
		Weapon weapon = factory.createWeapon();
		weapon.shoot();
		Food food = factory.createFood();
		food.printName();
	
DefaultFactory Print:
Running in smoke...
Dada dada....
apple

**Change the factory to * * * * MagicFactory

AbstractFactory factory = new DefaultFactory();

Print:

The broom wagged its tail

fire hu hu hu ...

mushroom

Abstract factory: produce a series of products. If you want to replace a series of products, or you want to expand on this series of products, and want to control the generation process of these series of products, use abstract factory.

Advantages and disadvantages of abstract factory and ordinary factory:

Ordinary factory:

It can be extended in the dimension of products to generate new products and factories that can generate new products. In other words, it can be extended in the dimension of the product.

If you want to produce a product series in an ordinary Factory, it will be particularly troublesome. When a product is generated, the Factory of a product will appear. There will be "Factory flooding".

Abstract factory:

Can change factories and produce new product series, but can not produce new product varieties. If you add a new product, you need to add createXXX() in the abstract factory; Method, all subclasses should implement this method. There are too many things to change.

Is there a kind of factory that combines ordinary factory and abstract factory?

Can you add product varieties at will and add product series conveniently? No,

Spring provides a solution, spring's Bean factory.

Spring said, don't do this. Moveable m = new Car(); Just new Car. You can configure it into the configuration file.

Test java to read the properties configuration file and reflect the generated object

spring.properties: 

VehicleType=com.lhy.springfactory.Car

Test code:

public static void main(String[] args) throws Exception{
	Properties props = new Properties();
		props.load(Test.class.getClassLoader().getResourceAsStream("com/lhy/springfactory/spring.properties"));

		String vehicleTypeName = props.getProperty("VehicleType");
		System.out.println(vehicleTypeName);
		//Reflection generation object
		Object o = Class.forName(vehicleTypeName).newInstance();
		Moveable m = (Moveable)o;
		m.run();
	}
Print results:
com.lhy.springfactory.Car
 Running in smoke...

Change the configuration file to or Trian:

VehicleType=com.lhy.springfactory.Train

Execute the test code and print:

com.lhy.springfactory.Train

The little train whined

You can see that you can dynamically control the generated classes just by changing the configuration file. The code doesn't move. Spring is such an idea.

The simplest way to use Spring:

Import the necessary jar packages,

Configuration files required by Spring:

<?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
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

  <bean id="v" class="com.bjsxt.spring.factory.Train">
  </bean>
  
  <!--  //v=com.bjsxt.spring.factory.Car  -->


</beans>

Test procedure:

package com.bjsxt.spring.factory;

import java.io.IOException;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args) throws Exception {
		BeanFactory f = new ClassPathXmlApplicationContext("applicationContext.xml");
		Object o = f.getBean("v");
		Moveable m = (Moveable)o;
		m.run();
	}

}
Train class configured in configuration file, print:
The little train whined...
change into Car´╝îPrint:
Running in smoke...

Let's simulate the Bean factory of Spring

Spring's BeanFactory is a container, which is implemented by a map. It reads the configuration of < Bean id = "V" class = "com. Bjsxt. Spring. Factory. Train" / > from the configuration file, traverses and parses the xml configuration, takes the ID as the key, and the fully qualified name of the class after the class takes the object generated by reflection as the value, and puts it into this map. When using, directly map get(id); Get the Bean object.

ClassPathXmlApplicationContext is an implementation of BeanFactory. This implementation is simulated here.

The Bean factory of Spring is simulated here:

public interface BeanFactory {

	Object getBean(String id);
}

ClassPathXmlApplicationContext:

public class ClassPathXmlApplicationContext implements BeanFactory{
	//A container for storing Bean objects,
	private Map<String, Object> container = new HashMap<String, Object>();
	
	// The constructor finds the configuration file and reads the xml configuration file
	public ClassPathXmlApplicationContext(String fileName) throws Exception{
		SAXBuilder sb = new SAXBuilder();
		Document doc = sb.build(this.getClass().getClassLoader()
				.getResourceAsStream(fileName));
		Element root = doc.getRootElement();
		List list = XPath.selectNodes(root, "/beans/bean");
		System.out.println(list.size());

		for (int i = 0; i < list.size(); i++) {
			Element bean = (Element) list.get(i);
			String id = bean.getAttributeValue("id");
			String clazz = bean.getAttributeValue("class");
			Object o = Class.forName(clazz).newInstance();
			container.put(id, o);
			System.out.println(id + " " + clazz);
		}
	}

	//Read the configuration file, read the Bean whose id is the passed in id, and instantiate it
	@Override
	public Object getBean(String id) {
		return container.get(id);
	}

	
}

Test procedure:

public static void main(String[] args) throws Exception{
		
		BeanFactory f = new ClassPathXmlApplicationContext("com/lhy/springfactory/applicationContext.xml");
		Object o = f.getBean("v");
		Moveable m = (Moveable)o;
		m.run();
		
		Train trian = (Train)f.getBean("trian");
		trian.run();
		
	}

applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans>
  <bean id="v" class="com.lhy.springfactory.Car"/>
	<bean id="trian" class="com.lhy.springfactory.Train"/>
  <!--  //v=com.bjsxt.spring.factory.Car  -->
</beans>

Print:

2

v com.lhy.springfactory.Car

trian com.lhy.springfactory.Train

Running with smoke

The little train whined

In this way, the class is configured in the configuration file, which is more flexible!

Tags: Design Pattern

Posted by fuzz01 on Sun, 15 May 2022 07:34:12 +0300