一、设计模式

1、意义

运用设计模式,将代码中变化的部分和不变的部分分开,方便维护代码和拓展功能。

2、六大基本原则

(1)开闭原则

模块应尽量在不修改原代码(闭)的情况下展开。即在功能需要拓展的时候,不能取修改原有的代码,实现一个热插拔的效果。

(2)里氏替换原则

里氏替换原则:对实现抽象化的具体步骤的规范。
意义:子类可以扩展父类的功能,但不能改变父类原有的功能;
父类能出现的地方都可以用子类来代替,且换成子类也不会出现任何错误或异常,且使用者无需知道是父类还是子类。

(3)依赖导致原则

1)高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象;
2)抽象不应该依赖于具体实现,而具体实现应依赖于抽象。
要求:面向抽象或接口编程,不对实现类进行编程,降低客户于实现模块间的耦合。

(4)接口隔离原则

每一个接口应该是一种角色,只干与自己有关的事,降低类与类之间的耦合度。

(5)合成复用原则

尽量使用组合/聚合,尽量不要使用继承。

(6)迪米特原则

一个实体应当尽量少地与其他实体之间发生相互作用,使系统功能模块相对独立。

二、单例模式(唯一化实例对象)

1、定义

单例模式:即是一个类只有一个实例;

2、应用场景

一个对象就能很好的完成工作,就没有必要创建多个对象来完成,创建的对象越多,浪费资源越多。
应用场景:servlet、applicationContext、工具类(静态的)、日志对象等;
单例模式通常都是一些无状态的对象,如:controller、service、dao等。

3、作用及好处

节约资源。

4、分类及代码

单例模式基本思想:

a.私有化构造器;
b.在类的内部实例化一个静态的单例对象;
c.提供一个静态的get方法返回本类唯一的实例。

(1)饿汉式

1)对象创建时机:在类被加载的时候就创建了本类的唯一的实例;
2)特点:调用效率高,线程安全,但不能延时加载;
3)代码示例:

  1. public class SingleTon{
  2. //1、私有化构造器
  3. private SingleTon(){};
  4. //2、在类的内部实例化一个静态的单例对象
  5. private static SingleTonINSTANCE = new SingleTon();
  6. //3、提供静态的get方法返回本类唯一的实例
  7. public static SingleTon getInstance(){
  8. return INSTANCE
  9. }
  10. }

(2)懒汉式

1)对象创建时机:在外部调用时才创建;
2)特点:调用效率比饿汉式略低,线程不安全,可延时加载;
3)为保证线程安全,可加线程同步,但这样效率较低;
4)代码示例(含线程同步):

  1. public class Lazy{
  2. //1、私有化构造器
  3. private Lazy(){};
  4. //2、声明一个本类的实例
  5. private static volatile Lazy INSTANCE = null;
  6. //3、提供静态的get方法返回本类的唯一实例
  7. publicstatic Lazy getInstance(){
  8. if(INSTANCE==null){
  9. synchronized(Lazy.class){
  10. if(INSTANCE==null){
  11. INSTANCE=new Lazy();
  12. }
  13. }
  14. }
  15. return INSTANCE;
  16. }
  17. }

注意:
a.懒汉式如果不加锁,在多线程操作的时候并不能完全保证是单例模式,在判断为该对象为null和创建对象之间,如果有另一个线程进来,就会判断失误,不能保证单例。所以为保证单例,需加锁,进行两次判空。
b.创建对象的操作不是原子性的操作,Java虚拟机在运行时可能将创建对象的过程进行重排序(a.分配内存、b.创建对象、c.将对象的引用赋值给变量),如果发生重排序,可能成为了(),那么instance就指向一个地址,不为null,就只直接return一个未初始化的对象,所以需要加上volatile关键字,防止指令重排序,对于改变量的所有操作,所有线程可见。**

(3)静态内部类(推荐使用)

1)创建时机
外部显示调用getInstance方法时,加载静态内部类,从而实例化本类的唯一实例;
2)特点:线程安全,调用效率高,延迟加载。
3)代码示例

  1. public class InnerClass{
  2. //1、私有化构造器
  3. private InnerClass(){};
  4. //2、声明静态内部类
  5. private static class InnerClassHolder(){
  6. private static InnerClass INSTANCE = new InnerClass();
  7. }
  8. //3、声明get方法
  9. public static InnerClass getInstance(){
  10. return InnerClassHolder.INSTANCE;
  11. }
  12. }

三、工厂模式

工厂就是帮助我们创建对象的。

1、应用场景

(1)创建对象的过程比较复杂
需要其他类的辅助、大量的计算、大量配置信息来获取时,使用工厂模式创建对象;
(2)当对象的实例有可能发生变化
为了不修改原代码,提高代码的可维护性。

2、分类

(1)简单工厂(静态工厂)

1)用处:用于创建同一等级(同一接口不同实现类)结构中的任意产品(对象);
2)要求:
①产品必须要有接口;
②创建静态工厂,在工厂内部创建静态的get方法,根据参数的不同创建不同的对象;
③在调用者中通过工厂来获取目标对象,从而实现对象的调用者和创建者的分离;
3)步骤:
①创建properties文件;
②在静态工厂中创建一个properties对象加载配置文件;
③获取参数创建;
④根据参数创建对象;
4)缺点
①产品必须要有接口;
②简单工厂创建对象依赖参数,从而违背了开闭原则;
5)注意:在维护某个实现类的引用时,使用其接口;
6)代码示例

//定义创建UserDao对象的简单工厂
public class UserDaoFactory{

    public staticUserDao getUserDao(){
        //加载properties文件
        Properties properties = new Properties();
        properties.load(new FileInputStream(“src/dbType.properties”));
        String dbType = properties.getProperty(“dbType“);
        //初始化实例对象
        UserDao userDao = null;
        //根据参数创建对象
        if(“mysql”.equals(dbType)){
            uerDao= new UserDaoImplForMysql();
        }else if(“oracle”. equals(dbType)){
            uerDao= new UserDaoImplForOracle();
        }
            return userDao;
    }
}

(2)工厂方法

1)用处:当新增的模块和已有的模块所依赖的实现类不同时,使用工厂方法创建对象;
2)实现的要求
①定义产品的接口;
②定义工厂的接口;
③根据需要创建不同的产品的实现类;
④在客户端中获取目标对象,通过创建与之对应的工厂对象来获取目标对象;
3)代码示例:工厂中只负责创建相应类的实例

public UserDaoFactory{
   public UserDao getUserDao(){
       return new UserDaoImpl();
   }
}

(3)抽象工厂

1)特点:用来生产不同产品族的全部产品:
对于新增的产品无能为力;
支持新增产品族。
2)产品族:一系列有特定关系的产品,比如与汽车相关的发动机,轮胎和座椅等。

3、简单工厂和工厂方法比较

①简单工厂只有一个工厂类,工厂方法有一组实现了相同接口的工厂类;
②代码复杂度
静态工厂:较复杂,需要根据参数处理业务逻辑,需根据产品的增加删除修改业务逻辑;
工厂方法:较简单,只需要创建对象并返回,完全满足开闭原则;
③结构复杂度
静态工厂:较低,所有的业务逻辑都在一个静态工厂中;
工厂方法:较高,每增加一个产品,需要增加相应的工厂类;
④管理上的难度
静态工厂:某种程度来说,虽然不完全符合设计原则,但是在合理范围内也能具有良好的扩展性,项目中使用比较多;
工厂方法:完全满足开闭原则(OCP),具有良好的拓展性;

四、JAXBContext解析xml

1、基本步骤

(1)创建xml配置文件;
(2)创建对应的java bean,去映射的配置文件中的结点;
(3)在java bean的相应位置添加注解
1)类上:@XmlRootElement(name=“属性名(要映射的标签名)”);
2)属性的get方法上:@XmlAttribute(name=”属性名”);
3)子结点集合的属性上:@XmlElement(name=”子结点标签名”);
(4)解析xml
1)创建JAXBcontext对象;
2)通过context对象创建解析器;
3)解析器解析xml,并返回一个与之对应的java bean。

2、代码示例:详见IOC示例。

五、IOC控制反转

1、定义

将所依赖的对象交给第三方工厂或容器来创建,并主动赋值维护引用,叫做控制反转。

2、作用

解耦(耦合:不同类的对象之间建立相互联系),使代码符合高内聚,低耦合。

2、核心思想

(1)根据类的模板,通过反射创建对象;
(2)通过成员属性的set方法,注入依赖;
1)通过反射构造set方法:
需知道的参数:成员属性名,属性类型;
2)通过反射调用set方法,注入依赖;
需要知道的参数:方法所属对象,实际参数;

3、依赖技术要点

(1)工厂模式:创建对象;
(2)单例模式:每个类只创建唯一的实例;
(3)反射:根据类的模板创建对象;
(4)解析xml(JAXB解析xml);

4、流程分析

(1)创建xml配置文件,定义了项目中所有需要创建的对象;
(2)创建单例工厂,用于创建对象,工厂中维护一个map容器,用于维护创建好的对象;
(3)在工厂中解析xml;
(4)通过反射创建对象;
(5)将对象放到map中管理(每个对象对应唯一的id);

5、伪代码(逻辑流程)(重点)

流程:创建xml配置文件>>解析xml配置文件>>创建对象>>维护依赖。

I.定义项目的基本结构

II.定义xml配置文件

所有需要让工厂创建的对象都需要在xml文件中配置。

(1)bean结点相关属性

1)id:当前bean在工厂中唯一的标识符(接口名首字母小写);
2)class:当前bean所属类的全限定名(实现类);
在工厂中需要根据class的值,通过反射创建对象。

(2)property结点相关属性

1)name:当前bean的某个成员属性的属性名;
2)value:当前bean某个成员属性的值(基本数据类型
3)ref:当前bean所依赖的某个属性的实例的id(引用数据类型)。

III.创建相应的java bean与xml文件进行映射

(1)beans
(2)bean
(3)Property
注意:在相应的bean里添加注解
1)类上:@XmlRootElement(name=”映射节点名”);
2)子结点集合的属性上:@XmlElement(name=“映射作为子结点的bean”);
3)成员属性的get方法上:@XmlAttribute(name=”映射属性结点”);

IV.定义静态内部类的单例工厂

(1)定义静态内部类的单例工厂;
(2)在工厂中声明一个map容器,用于存放创建好的对象bean;
(3)在构造器中处理xml配置文件
1)解析xml文件,获得Beans(Beans对象代理了xml文本);
2)创建所有配置在xml中的bean(第一次遍历beans)
①获取bean的id(String);
②获取bean的class值(String);
③根据字符串的class值获取类的模板clazz(类的类对象);
④根据模板clazz创建对象bean:instance;
⑤将id作为键,创建的对象instance作为值,存入map容器;
3)维护bean与bean的依赖关系(第二次遍历)
注意:通过set方法维护依赖关系。
①获取bean的id;
②通过id,从map容器中获取当前bean的实例currentInstance;
③ 通过实例获取当前实例的模板clazz;
④ 通过模板获取bean的property集合;
⑤ 遍历property集合,获取property结点上的name和ref值;
name值:当前bean的某个成员变量的属性名;
通过字符串处理,属性名前加set,属性名大写,即为set方法的方法名;
通过name值,获取形参类型:name值.getTpye();
ref值:当前bean的某个成员变量的实例的id,通过这个id,从map中获取
该实例,为set方法的实参paramBean;
⑥获取set方法setMethod:通过模板、方法名和形参类型获取;
⑦执行set方法:通过反射执行
*setMethod.invoke(方法调用者currentInstance,实参paramBean);

V.在工厂类声明一个公开的get方法,用于外部从map中获取创建好的对象bean;

6、代码示例

package com.chen.core;

import java.io.FileInputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import com.chen.config.BeanDefinition;
import com.chen.config.BeansDefinition;
import com.chen.config.Property;

public class BeanFactory {

    private Map<String, Object> cache = new HashMap<>();

    private BeanFactory() {

        //1、解析xml配置文件
        BeansDefinition beans = parseXML();


        //2、通过beans获取beanList,进行遍历,创建对象
        createInstance(beans);

        //3、第二次遍历beanList,维护依赖关系
        maintain(beans);

    };

    //维护依赖关系
    private void maintain(BeansDefinition beans) {
        List<BeanDefinition> beanList = beans.getBeans();

        if(beanList!=null && !beanList.isEmpty()) {
            for (BeanDefinition bean : beanList) {

                List<Property> properties = bean.getProperties();
                /*
                 * 调用set方法:
                 * 1、获取方法对象
                 *      1)反射模板
                 *      2)方法名、形参类型
                 *         1)方法名>>name值
                 *         2)形参类型>>name值    
                 *         
                 * 2、调用方法
                 *      方法对象、方法的调用者、实参
                 *         1)方法的调用者:map中获取
                 *         2)实参:通过ref在容器中获取
                 */
                if(properties!=null && !properties.isEmpty()) {
                    String id = bean.getId();

                    Object currentInstance = cache.get(id);

                    Class<? extends Object> clazz = currentInstance.getClass();
                    for (Property property : properties) {
                        String nameValue = property.getName();
                        String refValue = property.getRef();

                        //构建方法名
                        String setMenthodName = 
                                "set"
                                +nameValue.substring(0, 1).toUpperCase()
                                +nameValue.substring(1);

                        //形参类型
                        try {
                            Field field = clazz.getDeclaredField(nameValue);
                            Class<?> type = field.getType();
                            Method method = clazz.getDeclaredMethod(setMenthodName, type);
                            //获取实参
                            Object actulParamter = cache.get(refValue);
                            //执行set方法
                            method.invoke(currentInstance, actulParamter);

                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }

                }

            }
        }

    }

    //通过beans获得beanList集合,遍历创建对象
    private void createInstance(BeansDefinition beans) {
        List<BeanDefinition> beanList = beans.getBeans();

        if(beanList!=null && !beanList.isEmpty()) {
            for (BeanDefinition bean : beanList) {
                String idValue = bean.getId();
                String classValue = bean.getClazz();
                try {
                    Class<?> clazz = Class.forName(classValue);

                    Object instance = clazz.newInstance();

                    cache.put(idValue, instance);

                } catch (Exception e) {
                    e.printStackTrace();
                }    
            }
        }
    }


    //解析xml配置文件
    private BeansDefinition parseXML() {

        BeansDefinition beans =null;

        try {
            JAXBContext context = JAXBContext.newInstance(BeansDefinition.class);

            Unmarshaller unmarshaller = context.createUnmarshaller();

            beans = (BeansDefinition) unmarshaller.unmarshal(new FileInputStream("src/applicationContext.xml"));

        } catch (Exception e) {
            e.printStackTrace();
        }


        return beans;

    }

    private static class InnerClass{
        private static BeanFactory INSTANCE = new BeanFactory();
    }


    public static BeanFactory getInstance() {
        return InnerClass.INSTANCE;
    }

    //设置方法获得对象
    public Object getBean(String beanName) {
        Object object = cache.get(beanName);
        return object;
    }

}