一、设计模式
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)代码示例:
public class SingleTon{
//1、私有化构造器
private SingleTon(){};
//2、在类的内部实例化一个静态的单例对象
private static SingleTonINSTANCE = new SingleTon();
//3、提供静态的get方法返回本类唯一的实例
public static SingleTon getInstance(){
return INSTANCE;
}
}
(2)懒汉式
1)对象创建时机:在外部调用时才创建;
2)特点:调用效率比饿汉式略低,线程不安全,可延时加载;
3)为保证线程安全,可加线程同步,但这样效率较低;
4)代码示例(含线程同步):
public class Lazy{
//1、私有化构造器
private Lazy(){};
//2、声明一个本类的实例
private static volatile Lazy INSTANCE = null;
//3、提供静态的get方法返回本类的唯一实例
publicstatic Lazy getInstance(){
if(INSTANCE==null){
synchronized(Lazy.class){
if(INSTANCE==null){
INSTANCE=new Lazy();
}
}
}
return INSTANCE;
}
}
注意:
a.懒汉式如果不加锁,在多线程操作的时候并不能完全保证是单例模式,在判断为该对象为null和创建对象之间,如果有另一个线程进来,就会判断失误,不能保证单例。所以为保证单例,需加锁,进行两次判空。
b.创建对象的操作不是原子性的操作,Java虚拟机在运行时可能将创建对象的过程进行重排序(a.分配内存、b.创建对象、c.将对象的引用赋值给变量),如果发生重排序,可能成为了(),那么instance就指向一个地址,不为null,就只直接return一个未初始化的对象,所以需要加上volatile关键字,防止指令重排序,对于改变量的所有操作,所有线程可见。**
(3)静态内部类(推荐使用)
1)创建时机
外部显示调用getInstance方法时,加载静态内部类,从而实例化本类的唯一实例;
2)特点:线程安全,调用效率高,延迟加载。
3)代码示例
public class InnerClass{
//1、私有化构造器
private InnerClass(){};
//2、声明静态内部类
private static class InnerClassHolder(){
private static InnerClass INSTANCE = new InnerClass();
}
//3、声明get方法
public static InnerClass getInstance(){
return InnerClassHolder.INSTANCE;
}
}
三、工厂模式
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配置文件
(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;
}
}