Spring中设计模式

 

Spring中常见的设计模式

Ⅰ、23种经典设计模式分类

通常来说,设计模式都是混合使用,不会独立应用。利用穷举法充分理解设计模式的应用场景。在平时的应用中,不是用设计模式去生搬硬套,而是根据具体业务问题需要时借鉴。

Ⅱ、设计模式在应用中遵循六大原则

开闭原则

里氏交换原则

依赖倒转原则

接口隔离原则

迪米特法则(最少知道原则)

合成复用原则

Ⅲ、设计模式之间的关系

       

Ⅳ、设计模式由来

   

1、简单工厂模式(Factory)

也叫静态工厂模式

解决:用户与产品之间的问题

小作坊式的生产模式

用户本身不关心生产的过程,只关心生产这个结果。

Spring应用

       <BeanFactory>  <bean scope=“singleton/prototype”> </bean>   </BeanFactory>

总结

2、工厂方法模式(Factory Method)

工厂方法模式:更私人的。

抽象工厂模式:

(子类继承抽象工厂类重写方法)

  是用户的主入口、对用户更简单。

在Spring中应用最为广泛。

3、单例模式(保证独一无二

 单例模式:一个类,在整个系统运行过程中,只允许产生一个实例。(有且只有一个 New)

 应用: 工厂本身、IOC容器、配置文件、日历、Listener本身是单例的。

 解决一个并发访问的时候线程安全问题,保证单例的技术方案

   饿汉式:线程安全

 

在实例使用之前,不管你用不用,我都先new出来再说,避免线程安全问题。

饿汉式天生线程安全,类加载的时候初始化一次对象,效率比懒汉式高。

缺点:类加载时就实例,占茅坑不拉屎。

public class SingletonEH {

//类加载的时候初始化一次

private static  final SingletonEH instance = new SingletonEH();

//私有的构造函数,保证外类不能实例化本类

private SingletonEH() {}

//功能描述:(对外暴露的方法)

public static SingletonEH getInstance(){

     return instance;
     }

}

   懒汉式:线程不安全(延时加载)

默认加载的时候不实例化,在需要这个实例的时候才实例化。

懒汉式线程不安全,需要加上 synchronize同步锁,同步锁影响了程序执行效率。

public class SingletonLHsyn {

private static SingletonLHsyn instance;

private SingletonLHsyn() {}

//必须加锁 synchronized 才能保证单例,但加锁会影响效率。

public static synchronized SingletonLHsyn getInstance() {

        if (instance == null) {

            instance = new SingletonLHsyn();

        }
        return instance;
        }
}

   注册登记式:Spring最常用的

      每使用一次,都往一个固定的容器中注册并且将使用过的对象进行缓存,下次取对象,直接从缓存中取,以保持每次获取的对象都是同一个。

IOC中的单例模式就是经典的登录注册式单例。

   枚举式:也是注册登记的一种,不过使用枚举容器。

   序列化与反序列化:重写JVM的readResolve()。

总结

4、原型模式(孙悟空吹毫毛

原型模式:首先有个原型,数据内容相同,但对象实例不同(完全两个个体)。

应用:DTO VO  POJO entry

DTO与VO之间存在一些属性名称、类型都相同。

数据库中表查询出来的对象会赋值给DTO,不会再赋值给MVC中Model,而是把DTO的值赋值给VO,再VO的值传到View中去。

复制

Scope=”prototype”

Apache 反射去实现(原型模式)

实现cloneable接口

Clone()克隆

浅拷贝(成员属性)

深拷贝(连成员对象List<>()复制)

总结

5、代理模式(Proxy

定义:给某一个对象提供一个代理,并由代理对象控制对原对象的引用。

应用:AOP实现、拦截器、中介、黄牛、媒婆、解耦、专人做专事、增强、自己不想做的是又不得不做。

角色:代理类、目标类(被代理的类)

例如:找对象但决策权在自己

 

Controller动态代理

jdk和Cglib代理模式底层实现:字节码重组

 

JDK动态代理:接口实现

CGLib代理:继承

 

分类:

静态代理

静态代理,在代理之前,所有东西都已知(人工)。

动态代理动态代理,在代

理之前,所有东西都无知(智能化、自动化)

JDK动态代理

UserManager.java

package www.huihex.pattern.proxy.jdkcglibproxy;
/** 
 * Created by llx on 2019年11月20日
 * 用户管理接口
 */
public interface UserManager {
  //新增用户抽象方法
  void addUser(String userName,String password);
  //删除用户抽象方法
  void delUser(String userName);
}

  UserManagerIMpl.java

package www.huihex.pattern.proxy.jdkcglibproxy;
/** 
 * Created by llx on 2019年11月20日
 * 用户管理实现类,实现用户管理接口
 */
public class UserManagerImpl implements UserManager{
  //重写新增用户方法
  @Override
  public void addUser(String userName, String password) {
      System.out.println("调用了新增的方法!");
      System.out.println("传入参数为 userName: "+userName+" password: "+password);
  }
  //重写删除用户方法
  @Override
  public void delUser(String userName) {
      System.out.println("调用了删除的方法!");
      System.out.println("传入参数为 userName: "+userName);
  }
}

 JdkProxy.java

package www.huihex.pattern.proxy.jdkcglibproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/** 
 * Created by llx on 2019年11月20日
 * JDK动态代理实现InvocationHandler接口
 */
public class JdkProxy implements InvocationHandler {
  //需要代理的目标对象
  private Object target ;
  
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      System.out.println("JDK动态代理,监听开始!");
      Object result = method.invoke(target, args);
      System.out.println("JDK动态代理,监听结束!");
      return result;
  }
  //定义获取代理对象方法
  private Object getJDKProxy(Object targetObject){
      //为目标对象target赋值
      this.target = targetObject;
      //JDK动态代理只能针对实现了接口的类进行代理,newProxyInstance 函数所需参数就可看出
      //底层用 字节码重组实现的
      return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
  }
  
  public static void main(String[] args) {
      JdkProxy jdkProxy = new JdkProxy();//实例化JDKProxy对象
      UserManager user = (UserManager) jdkProxy.getJDKProxy(new UserManagerImpl());//获取代理对象
      user.addUser("admin", "123123");//执行新增方法
  }
}

CGlib动态代理

Cglib动态代理(需要导入两个jar包,asm-5.2.jar,cglib-3.2.5.jar。版本自行选择)

   CglibProxy.java

package www.huihex.pattern.proxy.jdkcglibproxy;

import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/** 
 * Created by llx on 2019年11月20日
 * Cglib动态代理,实现MethodInterceptor接口
 * Cglib动态代理(需要导入两个jar包,asm-5.2.jar,cglib-3.2.5.jar。版本自行选择)
 */
public class CglibProxy implements MethodInterceptor {
  //需要代理的目标对象
  private Object target;
  //重写拦截方法
  @Override
  public Object intercept(Object obj, Method method, Object[] arr, MethodProxy proxy) throws Throwable {
      System.out.println("Cglib动态代理,监听开始!");
      //方法执行,参数:target目标对象、 arr参数数组
      Object invoke = method.invoke(target, arr);
      
      System.out.println("Cglib动态代理,监听结束!");
      return invoke;
  }
  //定义获取代理对象方法
  public Object getCglibProxy(Object objectTarget){
      //为目标对象target赋值
      this.target = objectTarget;
      Enhancer enhancer = new Enhancer();
      //设置父类,因为Cglib是针对指定的类生成一个子类,所以需要指定父类
      enhancer.setSuperclass(objectTarget.getClass());
      enhancer.setCallback(this);// 设置回调 
      Object result = enhancer.create();//创建并返回代理对象
      return result;
  }
  
  public static void main(String[] args) {
	  //实例化CglibProxy对象
      CglibProxy cglib = new CglibProxy();
      //获取代理对象
      UserManager user =  (UserManager) cglib.getCglibProxy(new UserManagerImpl());
      //执行删除方法
      user.delUser("admin");
  }
}

 
总结

1、原理区别:

利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,

在调用具体方法前调用InvokeHandler来处理。

而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

 2、何时使用JDK还是CGLIB?

1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP 。

2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP 

3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

 3、如何强制使用CGLIB实现AOP?

(1)添加CGLIB库,SPRING_HOME/cglib/*.jar

(2)在spring配置文件中加入

<aop:aspectj-autoproxy proxy-target-class="true"/>

4、JDK动态代理和CGLIB字节码生成的区别?

(1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类

(2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法 因为是继承,所以该类或方法最好不要声明成final 

5、CGlib比JDK快?

1)使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,

在jdk6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,

因为CGLib原理是动态生成被代理类的子类。

2)在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点,但是到jdk8的时候,jdk代理效率高于CGLIB代理,

总之,每一次jdk版本升级,jdk代理效率都得到提升,而CGLIB代理消息确有点跟不上步伐。

6、Spring如何选择用JDK还是CGLIB?

1)当Bean实现接口时,Spring就会用JDK的动态代理。

2)当Bean没有实现接口时,Spring使用CGlib是实现。

3)可以强制使用CGlib

(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)。

6、策略模式(Strategy

最终执行结果是固定的。执行过程和执行逻辑不一样。

Spring 中在实例化对象的时候用到 Strategy 模式,在 SimpleInstantiationStrategy 有使用。

应用:

比较器、固定算法、商城的支付方式、登录方式选择、旅行路线的选择。

优点:

符合开闭原则

避免使用多重条件转移语句
比如省去大量的 if/else 和 switch 语句,降低代码的耦合

提高算法的保密性和安全性
只需知道策略的作用,而不关心内部实现

缺点:

客户端必须知道所有的策略类,并自行决定使用哪一个策略类

产生很多策略类

总结

7、模板方法模式(Template 流程标准化,原料自己加。)

JdbcTemplate  完美

总结

8、委派模式(干活是你的(普通员工),功劳是我的(项目经理))

DispatcherServlet

总结

9、适配器模式((兼容)Adapter 需要一个转换头)

不改变原来的代码,也要兼容新的需求。

总结

10、装饰者模式(Decorator 也叫包装器模式

需要包装,但不改变本质(同宗同源)

特点

  1. 注重覆盖、扩展。
  2. 装饰器和被装饰器都实现同一个接口,主要目的是为了扩展之后依旧保留 OOP 关系(同宗同源)。
  3. 满足 is-a 的关系。

应用

IO 流包装、数据源包装、简历包装

总结

11、观察者模式(完成时通知我)

发布者(Publish)和订阅者(Subscribe)

但观察者模式只是订阅者来解耦 然后做出相应

应用

监听器 鼠标事件 短信邮箱通知

Java回调机制

用一句话讲明回调机制就是: 在A类里面拥有一个类B的对象,调用B类的个方法并把自身引用传入,在B类的这个方法里面又通过传进来的A的引用来调用A类的某个方法(这个最后调用的A类的方法就叫做回调方法)。

总结

各设计模式对比及编程思想总结Spring AOP OOP BOP IOC DI/DL

AOP(面向切面编程)

AOP:面向切面编程思想

动态代理只是AOP的一种实现技术的手段。

Aspect(切面)

Expretion(切面表达式)

作用:

解耦、专人干专事、增强、规则。

OOP(面向对象编程)

BOP(面向Bean编程)

一切从Bean 开始。

IOC(控制反转)

IOC容器是一个Map。

DI/DL(依赖注入)

依赖注入、依赖查找,Spring 不仅保存自己创建的对象,而且保存对象与对象之间的关系。

 

注入即赋值,主要三种方式构造方法、set 方法、直接赋值。

 

Spring 的加载步骤

 

     定位、载入、注册:再确定要不要初始化Spring

发布了14 篇原创文章 · 获赞 0 · 访问量 445

猜你喜欢

转载自blog.csdn.net/qq_38118080/article/details/103262391
今日推荐