Patrón de diseño de Java --- Personalice SpringIOC en la práctica

1. Personaliza el IOC de Spring

1.1 Caso

Ahora es necesario analizar los siguientes archivos de configuración y personalizar el IOC del framework Spring para administrar los objetos involucrados.

<?xml version="1.0" encoding="UTF-8"?>
<beans>
	<bean id="userService" class="com.test.service.impl.UserServiceImpl">
		<property name="userDao" ref="userDao"></property>
	</bean>
	<bean id="userDao" class="com.test.dao.impl.UserDaoImpl"></bean>
</beans>

1.2 Definir clases pojo relacionadas con frijoles

1.2.1, clase PropertyValue

Se utiliza para encapsular las propiedades del bean, reflejadas en el archivo de configuración anterior son los datos de la etiqueta de propiedad de la subetiqueta de la etiqueta del bean del paquete.

/**
 * @version v1.0
 * @ClassName: PropertyValue
 * @Description: 用来封装bean标签下的property标签的属性
 *              name属性
 *              ref属性
 *              value属性 : 给基本数据类型及String类型数据赋的值
 */
public class PropertyValue {
    
    

    private String name;
    private String ref;
    private String value;

    public PropertyValue() {
    
    
    }

    public PropertyValue(String name, String ref, String value) {
    
    
        this.name = name;
        this.ref = ref;
        this.value = value;
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public String getRef() {
    
    
        return ref;
    }

    public void setRef(String ref) {
    
    
        this.ref = ref;
    }

    public String getValue() {
    
    
        return value;
    }

    public void setValue(String value) {
    
    
        this.value = value;
    }
}

1.2.2、Valores de propiedades mutables类

Una etiqueta de bean puede tener varias subetiquetas de propiedad, así que defina una clase MutablePropertyValues ​​​​para almacenar y administrar varios objetos PropertyValue.

/**
 * @version v1.0
 * @ClassName: MutablePropertyValues
 * @Description: 用户存储和管理多个PropertyValue对象
 */
public class MutablePropertyValues implements Iterable<PropertyValue> {
    
    

    //定义list集合对象,用来存储PropertyValue对象
    private final List<PropertyValue> propertyValueList;

    public MutablePropertyValues() {
    
    
        this.propertyValueList = new ArrayList<PropertyValue>();
    }

    public MutablePropertyValues(List<PropertyValue> propertyValueList) {
    
    
        if(propertyValueList == null) {
    
    
            this.propertyValueList = new ArrayList<PropertyValue>();
        } else {
    
    
            this.propertyValueList = propertyValueList;
        }
    }

    //获取所有的PropertyValue对象,返回以数组的形式
    public PropertyValue[] getPropertyValues() {
    
    
        //将集合转换为数组并返回
        return propertyValueList.toArray(new PropertyValue[0]);
    }

    //根据name属性值获取PropertyValue对象
    public PropertyValue getPropertyValue(String propertyName) {
    
    
        //遍历集合对象
        for (PropertyValue propertyValue : propertyValueList) {
    
    
            if (propertyValue.getName().equals(propertyName)) {
    
    
                return propertyValue;
            }
        }
        return null;
    }

    //判断集合是否为空
    public boolean isEmpty() {
    
    
        return propertyValueList.isEmpty();
    }

    //添加PropertyValue对象
    public MutablePropertyValues addPropertyValue(PropertyValue pv) {
    
    
        //判断集合中存储的PropertyValue对象是否和传递进行的重复了,如果重复了,进行覆盖
        for (int i = 0; i < propertyValueList.size(); i++) {
    
    
            //获取集合中每一个PropertyValue对象
            PropertyValue currentPv = propertyValueList.get(i);
            if(currentPv.getName().equals(pv.getName())) {
    
    
                propertyValueList.set(i,pv);
                return this; //目的就是实现链式编程
            }
        }
        this.propertyValueList.add(pv);
        return this;//目的就是实现链式编程
    }

    //判断是否有指定name属性值的对象
    public boolean contains(String propertyName) {
    
    
        return getPropertyValue(propertyName) != null;
    }

    //获取迭代器对象
    public Iterator<PropertyValue> iterator() {
    
    
        return propertyValueList.iterator();
    }
}

1.2.3, clase BeanDefinition

La clase BeanDefinition se utiliza para encapsular la información del bean, que incluye principalmente la identificación (es decir, el nombre del objeto del bean), la clase (el nombre completo de la clase que debe administrar Spring) y los datos de propiedad de la subetiqueta.

/**
 * @version v1.0
 * @ClassName: BeanDefinition
 * @Description: 用来封装bean标签数据
 *      id属性
 *      class属性
 *      property子标签的数据
 */
public class BeanDefinition {
    
    

    private String id;
    private String className;

    private MutablePropertyValues propertyValues;

    public BeanDefinition() {
    
    
        propertyValues = new MutablePropertyValues();
    }

    public String getId() {
    
    
        return id;
    }

    public void setId(String id) {
    
    
        this.id = id;
    }

    public String getClassName() {
    
    
        return className;
    }

    public void setClassName(String className) {
    
    
        this.className = className;
    }

    public MutablePropertyValues getPropertyValues() {
    
    
        return propertyValues;
    }

    public void setPropertyValues(MutablePropertyValues propertyValues) {
    
    
        this.propertyValues = propertyValues;
    }
}

1.3 Definir clases relacionadas con el registro

1.3.1, interfaz BeanDefinitionRegistry

La interfaz BeanDefinitionRegistry define las operaciones relacionadas del registro y define las siguientes funciones:

  • Registre el objeto BeanDefinition en el registro

  • Elimina el objeto BeanDefinition con el nombre especificado del registro

  • Obtenga el objeto BeanDefinition del registro por nombre

  • Determinar si el objeto BeanDefinition con el nombre especificado está contenido en el registro

  • Obtenga la cantidad de objetos BeanDefinition en el registro

  • Obtenga los nombres de todas las BeanDefinitions en el registro

/**
 * @version v1.0
 * @ClassName: BeanDefinitionRegistry
 * @Description: 注册表对象
 */
public interface BeanDefinitionRegistry {
    
    
   
    //注册BeanDefinition对象到注册表中
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);

    //从注册表中删除指定名称的BeanDefinition对象
    void removeBeanDefinition(String beanName) throws Exception;

    //根据名称从注册表中获取BeanDefinition对象
    BeanDefinition getBeanDefinition(String beanName) throws Exception;

    boolean containsBeanDefinition(String beanName);

    int getBeanDefinitionCount();

    String[] getBeanDefinitionNames();
}

1.3.2, clase SimpleBeanDefinitionRegistry

Esta clase implementa la interfaz BeanDefinitionRegistry y define la colección Map como contenedor de registro.

/**
 * @version v1.0
 * @ClassName: SimpleBeanDefinitionRegistry
 * @Description: 注册表接口的子实现类
 */
public class SimpleBeanDefinitionRegistry implements BeanDefinitionRegistry {
    
    

    //定义一个容器,用来存储BeanDefinition对象
    private Map<String,BeanDefinition> beanDefinitionMap = new HashMap<String, BeanDefinition>();

    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
    
    
        beanDefinitionMap.put(beanName,beanDefinition);
    }

    public void removeBeanDefinition(String beanName) throws Exception {
    
    
        beanDefinitionMap.remove(beanName);
    }

    public BeanDefinition getBeanDefinition(String beanName) throws Exception {
    
    
        return beanDefinitionMap.get(beanName);
    }

    public boolean containsBeanDefinition(String beanName) {
    
    
        return beanDefinitionMap.containsKey(beanName);
    }

    public int getBeanDefinitionCount() {
    
    
        return beanDefinitionMap.size();
    }

    public String[] getBeanDefinitionNames() {
    
    
        return beanDefinitionMap.keySet().toArray(new String[0]);
    }
}

1.4 Definir clases relacionadas con el analizador

1.4.1, interfaz BeanDefinitionReader

BeanDefinitionReader se utiliza para analizar archivos de configuración y registrar beans en el registro. Se definen dos especificaciones:

  • Obtenga la función del registro, para que el mundo exterior pueda obtener el objeto de registro a través de este objeto.

  • Cargue el archivo de configuración y registre los datos del bean.

/**
 * @version v1.0
 * @ClassName: BeanDefinitionReader
 * @Description:
 *      用来解析配置文件的,而该接口只是定义了规范
 */
public interface BeanDefinitionReader {
    
    
    //获取注册表对象
    BeanDefinitionRegistry getRegistry();
    //加载配置文件并在注册表中进行注册
    void loadBeanDefinitions(String configLocation) throws Exception;
}

1.4.2, clase XmlBeanDefinitionReader

La clase XmlBeanDefinitionReader está dedicada a analizar archivos de configuración xml. Esta clase implementa la interfaz BeanDefinitionReader e implementa dos funciones en la interfaz.

/**
 * @version v1.0
 * @ClassName: XmlBeanDefinitionReader
 * @Description: 针对xml配置文件进行解析的类
 */
public class XmlBeanDefinitionReader implements BeanDefinitionReader {
    
    

    //声明注册表对象
    private BeanDefinitionRegistry registry;

    public XmlBeanDefinitionReader() {
    
    
        registry = new SimpleBeanDefinitionRegistry();
    }

    public BeanDefinitionRegistry getRegistry() {
    
    
        return registry;
    }

    public void loadBeanDefinitions(String configLocation) throws Exception {
    
    
        //使用dom4j进行xml配置文件的解析
        SAXReader reader = new SAXReader();
        //获取类路径下的配置文件
        InputStream is = XmlBeanDefinitionReader.class.getClassLoader().getResourceAsStream(configLocation);
        Document document = reader.read(is);
        //根据Document对象获取根标签对象 (beans)
        Element rootElement = document.getRootElement();
        //获取根标签下所有的bean标签对象
        List<Element> beanElements = rootElement.elements("bean");
        //遍历集合
        for (Element beanElement : beanElements) {
    
    
            //获取id属性
            String id = beanElement.attributeValue("id");
            //获取class属性
            String className = beanElement.attributeValue("class");

            //将id属性和class属性封装到BeanDefinition对象中
            //1,创建BeanDefinition
            BeanDefinition beanDefinition = new BeanDefinition();
            beanDefinition.setId(id);
            beanDefinition.setClassName(className);

            //创建MutablePropertyValues对象
            MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();

            //获取bean标签下所有的property标签对象
            List<Element> propertyElements = beanElement.elements("property");
            for (Element propertyElement : propertyElements) {
    
    
                String name = propertyElement.attributeValue("name");
                String ref = propertyElement.attributeValue("ref");
                String value = propertyElement.attributeValue("value");
                PropertyValue propertyValue = new PropertyValue(name,ref,value);
                mutablePropertyValues.addPropertyValue(propertyValue);
            }
            //将mutablePropertyValues对象封装到BeanDefinition对象中
            beanDefinition.setPropertyValues(mutablePropertyValues);

            //将beanDefinition对象注册到注册表中
            registry.registerBeanDefinition(id,beanDefinition);
        }
        
    }
}

1.5, clases relacionadas con contenedores IOC

1.5.1, interfaz BeanFactory

La especificación uniforme para definir el contenedor IOC en esta interfaz es obtener el objeto bean.

/**
 * @version v1.0
 * @ClassName: BeanFactory
 * @Description: IOC容器父接口
 */
public interface BeanFactory {
    
    

    Object getBean(String name) throws Exception;

    <T> T getBean(String name, Class<? extends T> clazz) throws Exception;
}

1.5.2, interfaz ApplicationContext

Todas las clases de subimplementación de esta interfaz no se retrasan en la creación de objetos de bean, por lo que el método refresh() se define en esta interfaz, que principalmente completa las dos funciones siguientes:

  • Cargue el archivo de configuración.

  • El objeto bean se crea de acuerdo con los datos encapsulados por el objeto BeanDefinition en el registro.

/**
 * @version v1.0
 * @ClassName: ApplicationContext
 * @Description: 定义非延时加载功能
 */
public interface ApplicationContext extends BeanFactory {
    
    

    void refresh() throws Exception;
}

1.5.3、Contexto de aplicación abstracta类

  • Como una subclase de la interfaz ApplicationContext, esta clase tampoco se carga con retraso, por lo que debe definir una colección de mapas en esta clase como un contenedor para el almacenamiento de objetos de bean.

  • Declare una variable de tipo BeanDefinitionReader para analizar el archivo de configuración xml, de acuerdo con el principio de responsabilidad única. La creación de objetos de tipo BeanDefinitionReader se implementa mediante subclases, porque solo las subclases definen claramente qué objeto de subclase de BeanDefinitionReader crear.

/**
 * @version v1.0
 * @ClassName: AbstractApplicationContext
 * @Description: ApplicationContext接口的子实现类,用于立即加载
 */
public abstract class AbstractApplicationContext implements ApplicationContext {
    
    

    //声明解析器变量
    protected BeanDefinitionReader beanDefinitionReader;

    //定义用于存储bean对象的map容器
    protected Map<String, Object> singletonObjects = new HashMap<String, Object>();

    //声明配置文件路径的变量
    protected String configLocation;

    public void refresh() throws Exception {
    
    
        //加载BeanDefinition对象
        beanDefinitionReader.loadBeanDefinitions(configLocation);
        //初始化bean
        finishBeanInitialization();
    }

    //bean的初始化
    private void finishBeanInitialization() throws Exception {
    
    
        //获取注册表对象
        BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();

        //获取BeanDefinition对象
        String[] beanNames = registry.getBeanDefinitionNames();
        for (String beanName : beanNames) {
    
    
            //进行bean的初始化
            getBean(beanName);
        }
    }
}

1.5.4, ClassPathXmlApplicationContext类

Esta clase carga principalmente archivos de configuración bajo la ruta de clase y crea objetos de bean. Principalmente realiza las siguientes funciones:

  • En el constructor, cree un objeto BeanDefinitionReader.

  • En el método de construcción, llame al método refresh() para cargar el archivo de configuración, cree un objeto bean y guárdelo en el contenedor.

  • Vuelva a escribir el método getBean() en la interfaz principal e implemente operaciones de inyección de dependencia.

/**
 * @version v1.0
 * @ClassName: ClassPathXmlApplicationContext
 * @Description: IOC容器具体的子实现类
 *          用于加载类路径下的xml格式的配置文件
 */
public class ClassPathXmlApplicationContext extends AbstractApplicationContext {
    
    

    public ClassPathXmlApplicationContext(String configLocation) {
    
    
        this.configLocation = configLocation;
        //构建解析器对象
        beanDefinitionReader = new XmlBeanDefinitionReader();
        try{
    
    
            this.refresh();
        } catch (Exception e) {
    
    

        }
    }

    //根据bean对象的名称获取bean对象
    public Object getBean(String name) throws Exception {
    
    
        //判断对象容器中是否包含指定名称的bean对象,如果包含,直接返回即可,如果不包含,需要自行创建
        Object obj = singletonObjects.get(name);
        if (obj != null) {
    
    
            return obj;
        }

        //获取BeanDefinition对象
        BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();
        BeanDefinition beanDefinition = registry.getBeanDefinition(name);
        //获取bean信息中的className
        String className = beanDefinition.getClassName();
        //通过反射创建对象
        Class<?> clazz = Class.forName(className);
        Object beanObj = clazz.newInstance();

        //进行依赖注入操作
        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
        for (PropertyValue propertyValue : propertyValues) {
    
    
            //获取name属性值
            String propertyName = propertyValue.getName();
            //获取value属性
            String value = propertyValue.getValue();
            //获取ref属性
            String ref = propertyValue.getRef();
            if(ref != null && !"".equals(ref)) {
    
    
                //获取依赖的bean对象
                Object bean = getBean(ref);
                //拼接方法名
                String methodName = StringUtils.getSetterMethodByFieldName(propertyName);
                //获取所有的方法对象
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
    
    
                    if (methodName.equals(method.getName())) {
    
    
                        //执行该setter方法
                        method.invoke(beanObj,bean);
                    }
                }
            }

            if(value != null && !"".equals(value)) {
    
    
                //拼接方法名
                String methodName = StringUtils.getSetterMethodByFieldName(propertyName);
                //获取method对象
                Method method = clazz.getMethod(methodName, String.class);
                method.invoke(beanObj,value);
            }
        }

        //在返回beanObj对象之前,将该对象存储到map容器中
        singletonObjects.put(name,beanObj);
        return beanObj;
    }

    public <T> T getBean(String name, Class<? extends T> clazz) throws Exception {
    
    
        Object bean = getBean(name);
        if(bean == null) {
    
    
            return null;
        }
        return clazz.cast(bean);
    }
}

1.6 Resumen de IOC de resorte personalizado

1.6.1 Patrones de diseño utilizados

  • patrón de fábrica. Esto utiliza el modo de fábrica + archivo de configuración.

  • Patrón de singleton. Los objetos de bean administrados por Spring IOC son todos singletons. El singleton aquí no está controlado por el constructor, pero Spring Framework crea solo un objeto para cada bean.

  • Patrón de método de plantilla. El método finishBeanInitialization() de la clase AbstractApplicationContext llama al método getBean() de la subclase, porque la implementación de getBean() está estrechamente relacionada con el entorno.

  • patrón de iterador. El modo iterador se usa para la definición de la clase MutablePropertyValues, porque esta clase almacena y administra objetos PropertyValue y también pertenece a un contenedor, por lo que proporciona un método transversal para el contenedor.

Spring Framework en realidad usa muchos patrones de diseño, como AOP usa el modo proxy, elige el proxy JDK o el proxy CGLIB para usar el modo de estrategia, así como el modo adaptador, el modo decorador, el modo observador, etc.

1.7 Prueba

1.7.1 Empaquete el código anterior, colóquelo en el almacén local e introduzca las dependencias

<dependency>
 	<groupId>com.test</groupId>
  	<artifactId>test_spring</artifactId>
  	<version>1.0-SNAPSHOT</version>
</dependency>

1.7.2, capa de acceso a datos. Definir la interfaz UserDao y sus clases de implementación secundaria

/**
 * @version v1.0
 * @ClassName: UserDao
 * @Description: 数据访问层接口
 */
public interface UserDao {
    
    

    public void add();
}


/**
 * @version v1.0
 * @ClassName: UserDaoImpl
 * @Description: 数据访问层实现类
 */
public class UserDaoImpl implements UserDao {
    
    

    private String username;
    private String password;

    public void setUsername(String username) {
    
    
        this.username = username;
    }

    public void setPassword(String password) {
    
    
        this.password = password;
    }

    public UserDaoImpl() {
    
    
        System.out.println("userDao被创建了");
    }

    public void add() {
    
    
        System.out.println("UserDao ..." + username + "==" + password);
    }
}

1.7.3, capa de lógica de negocios. Definir la interfaz UserService y sus clases de implementación secundaria

/**
 * @version v1.0
 * @ClassName: UserService
 * @Description: 业务逻辑层接口
 */
public interface UserService {
    
    

    public void add();
}


/**
 * @version v1.0
 * @ClassName: UserServiceImpl
 * @Description: 业务逻辑层实现类
 */
public class UserServiceImpl implements UserService {
    
    

    //声明一个UserDao类型的变量
    private UserDao userDao;

    public UserServiceImpl() {
    
    
        System.out.println("userService被创建了");
    }

    public void setUserDao(UserDao userDao) {
    
    
        this.userDao = userDao;
    }

    public void add() {
    
    
        System.out.println("UserService ...");
        userDao.add();
    }
}

1.7.4 Defina la clase UserController y use el método principal para simular la capa del controlador

/**
 * @version v1.0
 * @ClassName: UserController
 * @Description: 入口
 */
public class UserController {
    
    
    public static void main(String[] args) throws Exception {
    
    
        //1,创建spring的容器对象
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
        //2,从容器对象中获取userService对象
        UserService userService = applicationContext.getBean("userService", UserService.class);
        //3,调用userService方法进行业务逻辑处理
        userService.add();
    }
}

1.7.5 Escribir archivos de configuración. Escriba un archivo de configuración denominado ApplicationContext.xml en la ruta de clases

<?xml version="1.0" encoding="UTF-8"?>
<beans>

    <bean id="userDao" class="com.test.dao.impl.UserDaoImpl">
        <property name="username" value="zhangsan"></property>
        <property name="password" value="123456"></property>
    </bean>

    <bean id="userService" class="com.test.service.impl.UserServiceImpl">
        <property name="userDao" ref="userDao"></property>
    </bean>

</beans>

Supongo que te gusta

Origin blog.csdn.net/shuai_h/article/details/129771571
Recomendado
Clasificación