java常用设计模式小结

来源:互联网 发布:手机不能连接数据网络 编辑:程序博客网 时间:2024/05/29 06:41

目录

一、概述...1

二、        六大设计原则... 1

1、单一职责原则...1

2、里氏替换原则...2

3、依赖倒置原则...2

4、接口隔离原则...2

5、迪米特法则...2

6、开闭原则...2

三、常用设计模式简介...2

1、      单例模式... 2

1.1单例模式定义:...2

1.2单例模式优缺点...3

1.3单例模式应用场景...3

1.4 单例的扩展(线程安全问题)3

2、工厂模式...5

2.1 工厂方法模式的定义...5

2.2 抽象工厂的定义...7

2.3三种工厂模式的异同(优缺点)8

2.4 工厂模式的使用场景...9

2.5 工厂模式扩展...10

3、模板方法模式...12

3.1 模板方法模式的定义...12

3.2 模板方法模式的优缺点...14

3.3 模板方法模式的使用场景...14

4、代理模式...14

4.1 代理模式定义...14

4.2 静态代理...15

4.3 动态代理...16

4.4 cglib代理...22

5.门面模式...24

5.1 门面模式的定义...24

5.2 门面模式的优缺点...25

5.3 门面模式的使用场景...25

 

 

一、概述

设计模式是前人在大量的实验和错误中总结出来的一套高效、可复用的软件开发解决方案。在开发中设计模式往往会带来性能的提升以及工作量减少,但其理解起来还是比较抽象的,在这里结合一些资料和自己的理解对java中比较常用的设计模式做一个小结。

设计模式一般有两条准则对其进行分类,第一是目的准则(是完成什么工作的),可以将模式分为:创建型、结构型、行为型。创建型模式和对象的创建有关,结构型来处理类和对象的组合,行为型模式处理类和对象怎么交互和怎么分配职责。第二是范围准则,指定模式是作用于类还是对象的。类模式是处理类和子类的关系,一般通过继承,在编译的时候已经确定,是静态的。对象模式一般在运行时候才能确定,更具动态性。


自己在web系统开发中,使用到的设计模式有:单例模式、工厂模式、抽象工厂模式、模板方法模式、代理(委托)模式、门面(外观)模式;还有一些常见的比如:建造者模式、策略模式、适配器模式、观察者模式等,下面就通过各个模式的简介,使用场景,优缺点方面来逐一介绍:

设计模式一般要遵循六大设计原则,在开始介绍设计模式之前会对这些设计原则的概念做一个简要的说明。

 

二、六大设计原则

1、单一职责原则

一个方法尽可能只做一件事,减少耦合

2、里氏替换原则

父类出现的地方子类也可以出现

3、依赖倒置原则

高层模块不应该依赖低层模块,两者都应该依赖其抽象;

抽象不应该依赖细节;

细节应该依赖抽象。

模块间的依赖通过抽象发生,实现类之间不直接发生依赖的关系;

接口或抽象类不依赖于实现类;

实现依赖于接口和抽象类。

4、接口隔离原则

实例接口

类接口

客户端不应该依赖它不需要的接口

类间的依赖关系应该建立在最小接口上

5、迪米特法则

一个对象应该对其他对象有最少的了解

6、开闭原则

一个软件实体如类、模块和函数应该对扩展开放,对修改关闭

 

三、常用设计模式简介

1、单例模式

1.1单例模式定义:

一个系统只存在一份该类的实例,自行实例化并向整个系统提供这个实例。通用类图如下:

 

 

单例模式分为懒汉(获取对象的时候实例化)和饿汉(初始实例化成员变量)两种

1.2单例模式优缺点

优点:系统中只存在一份实例,占用资源少,效率提高

            提供对唯一实例的受控访问

            允许可变数目的实例

缺点:扩展困难(没有接口),除非修改类

1.3单例模式应用场景

        a)生成唯一的序列号

              b)web页面的计数器,可以用单例来保存这个值,避免每次刷新都更新数据库带来的性能开销

              c)创建一个对象需要消耗的资源过多,如访问IO和数据库等资源

 

1.4 单例的扩展(线程安全问题)

              饿汉模式本身就是线程安全的,类加载的时候静态成员变量实例被创建,每次通过公有接口访问该实例,缺点是不是Lazy loading的。

       懒汉模式可以通过synchronized来实现线程安全,效率低。

              也可以创建静态内部类,在静态内部类包含静态成员实例,再通过公有接口返回该实例(不但线程安全,而且是Lazy Loading的)

 

public classSingletonLazy {

   
private static SingletonLazysingleton =null;

   
//防止创建多个实例(构造函数私有化,封死了从外部创建实例的可能)
   
private SingletonLazy() {

    }

   
/**
     *
线程安全,效率不高
    
* @return
    
*/
   
public static synchronized SingletonLazygetInstance() {
       
if (singleton== null) {
           
singleton =new SingletonLazy();
       
}
       
return singleton;
   
}

   
/**
     *
还是线程不安全 AB线程同时进入if
     * @return
    
*/
   
public static SingletonLazygetInstance1(){
       
if(singleton== null){
           
synchronized (SingletonLazy.class){
               
singleton =new SingletonLazy();
           
}
        }
       
return singleton;
   
}

   
/**
     *
双重if,看似安全,某种情况下还是线程不安全
    
* 内存模型允许无序写入
     * @return
    
*/
   
public static SingletonLazygetInstance2(){
       
if(singleton== null){
           
synchronized (SingletonLazy.class){
               
if(singleton== null) {
                   
singleton =new SingletonLazy();
               
}
            }
        }
       
return singleton;
   
}

   
/**
     *
安全的
    
* 和饿汉不同的是:SingletonLazySingletonHungry被加载了,
    
* instance也不一定被初始化,只有SingletonHolder被主动使用
    
* 才会实例化,达到了lazy loading的效果
    
*/
   
private static class SingletonHolder{
       
public static SingletonLazysingletonLazy =new SingletonLazy();
   
}

   
public static SingletonLazygetSingleton(){
       
return SingletonHolder.singletonLazy;
   
}

}

 

单例模式属于对象创建型模式

 

2、工厂模式

2.1 工厂方法模式的定义

        定义一个创建对象的接口,让子类决定实例化哪一个类,将对象的实例化延迟到子类。通用类图如下:


             

        //抽象产品public abstract class PenCore{    String color;    public abstract void writeWord(String s);}

//抽象构造者
publicabstract class BallPen {
    BallPen(){
        System.
out.println("生产一只装有"+getPenCore().color+"笔芯的圆珠笔");
   
}
   
public abstract PenCoregetPenCore();
}

//工厂
publicclass BlueBallPenextendsBallPen {
   
public PenCoregetPenCore() {
       
return new BluePenCore();
   
}
}

//具体构造者public class RedBullPen extends BallPen {    public PenCore getPenCore() {        return new RedPenCore();    }}

//具体产品

public classBluePenCore extendsPenCore {
    BluePenCore() {
       
color ="蓝色";
   
}
   
public void writeWord(String s) {
        System.
out.println("写出"+ color + "的字"+ s);
   
}
}

//具体产品public class RedPenCore extends PenCore {    RedPenCore() {        color = "红色";    }    public void writeWord(String s) {        System.out.println("写出" + color + "的字" + s);    }}

 

 

2.2 抽象工厂的定义

为创建一组相关或相互依赖的对象(产品族)提供一个接口,而且无需指定他们的具体类

封装性:每个产品的实现类不是高层模块要关心的


 

//抽象产品类public abstract class AbstractProductA {    //每个产品共有的方法    public void shareMethod(){    }    //每个产品相同方法,不同实现    public abstract void doSomething();}//产品A1的实现类public class ProductA1 extends AbstractProductA {    public void doSomething() {        System.out.println("产品A1的实现方法");    }}//产品A2的实现类public class ProductA2 extends AbstractProductA {    public void doSomething() {        System.out.println("产品A2的实现方法");    }}//抽象工厂类public abstract class AbstractCreator {    //创建A产品家族    public abstract AbstractProductA createProductA();    //创建B产品家族    public abstract AbstractProductB createProductB();}注意 有N个产品族,在抽象工厂类中就应该有N个创建方法。产品等级1的实现类public class Creator1 extends AbstractCreator {    //只生产产品等级为1A产品    public AbstractProductA createProductA() {        return new ProductA1();    }    //只生产产品等级为1B产品    public AbstractProductB createProductB() {        return new ProductB1();    }}//场景类public class Client {    public static void main(String[] args) {//定义出两个工厂        AbstractCreator creator1 = new Creator1();        AbstractCreator creator2 = new Creator2();//产生A1对象        AbstractProductA a1 = creator1.createProductA();//产生A2对象        AbstractProductA a2 = creator2.createProductA();//产生B1对象        AbstractProductB b1 = creator1.createProductB();//产生B2对象        AbstractProductB b2 = creator2.createProductB();/** 然后在这里就可以为所欲为了...*/    }}

 

2.3三种工厂模式的异同(优缺点)

1)  相同点

都属于创建型模式主要功能都是把对象的实例化部分抽取出来,优化了系统架构,增强了系统扩展性。

2)  不同点

工厂模式分为:简单工厂模式,工厂方法模式,抽象工厂模式

简单工厂模式工厂方法是静态的,不易扩展,不符合开闭原则;

工厂方法模式去掉了简单工厂方法的静态属性,使得子类得以继承,这样可以减小父类工厂的”压力”,创建更多的分厂,符合开闭原则,但是只要产品增加会增加更多的分工厂类,使得维护变得困难。简单工厂和工厂方法模式并没有解决代码改动时的问题,简单工厂需要修改工厂类,工厂方法模式需要创建新的对象(工厂)。

抽象工厂模式可以向客户端提供一个接口,使得客户端在不必指定产品的具体情况下,创建多个产品族中的产品对象(具体的产品类和创建类分别继承了抽象的产品类和抽象的创建类)。

 

工厂方法模式只有一个抽象产品,抽象工厂有多个抽象产品

工厂方法模式工厂只能生产特定一个具体产品实例,抽象工厂可以生产多个产品实例。

 

工厂方法模式:

一个抽象的产品类,可以派生多个具体的产品类

一个抽象的工厂类,可以派生多个具体的工程类

每个工厂类,只能生产一个具体的产品实例

         抽象工厂模式:

                            多个抽象产品类,每个抽象的产品类可以派生多个具体的产品类

                            一个抽象的工厂类,可以派生多个具体的工厂类

                            每个工厂类,可以创建多个具体以的产品实例

 

              抽象工厂模式的优点:封装,不用关心具体的产品类如何创建,只需要关心抽象的接口

       抽象工厂模式的缺点:扩展困难,要增加产品,抽象的创建工厂需要增加接口,实现类相应的也必须修改,严重违反开闭原则。

  2.4 工厂模式的使用场景

工厂模式是new一个对象的替代品,所以在需要生成对象的地方都可以使用,但会增加代码维护复杂度。

万物皆对象,那万物就都是产品类,比如需要设计一个邮件服务框架,有三种网络协议可供选择,IMAP、HTTP、POP3,可以有一个统一的抽象接口,然后又三个具体的产品类进行不同的实现,再定义一个工厂方法,根据传入的条件,选择不同的方式。

      2.5 工厂模式扩展

           2.5.1缩小为简单工厂类

                            工厂方法改为静态的

           2.5.2升级为多个工厂类

(每个工厂继承抽象工厂,自行创建对应的产品类)

           2.5.3替代单例模式

              

                     

                    

  2.5.4 延迟初始化

             

 

**************************************

抽象类和接口:

接口可以说成抽象类的一种特例,接口中的所有方法默认是public abstract的,接口中的所有变量默认是public staticfinal的。

抽象类可以有构造方法,接口不能有构造方法。

抽象类可以有普通成员变量,接口没有普通的成员变量(static的)

一个类可以实现多个接口(多继承),但只能继承一个类

抽象类是用来捕捉子类通用特性的。不能被实例化,只能被用作子类的超类

子类必须实现抽象类的抽象方法。

接口是抽象方法的集合,如果一个类实现了某个接口,就继承了这个接口的抽象方法,如果实现这个接口,就必须确保使用这些方法,接口只是一种形式,接口自身不能做任何事情。

 

接口的速度相对抽象类比较忙,因为它花时间寻找在类中的实现方法。

抽象类添加新方法时,可以提供默认实现,接口添加方法,必须修改实现该接口的类

 

使用场景:

如果拥有一些方法,并且有默认实现,那么使用抽象类;

如果想要多继承,必须使用接口,一个类可以实现多个接口;

如果基本功能在不断改变,就需要使用抽象类。

 

 

3、模板方法模式

      3.1 模板方法模式的定义

           定义一个操作中的算法框架,将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤

   

抽象模板类定义多个基本抽象方法和一个模板方法,象模板类并实现抽象方法,使用时直接调用模板方法。

       注意:抽象模板方法尽量声明为protected的,符合迪米特法则(一个对象应该对其他对象有最少的了解),防止子类会扩大父类的访问权限。

      

       抽象类中的方法分为三种:

           抽象方法:在抽象类中声明,具体的实现在子中

           模板方法:抽象类中声明和实现,模板方法一般调用抽象方法来实现逻辑功能,模板方法最好是final的防止被子类重写

           钩子方法:抽象类中声明和实现,但是一般可以被子类重写扩展,子类通过扩展钩子方法来影响模板方法的逻辑。(钩子方法也在模板方法调用)

public abstract classabstractSort {

   
protected abstract void sortArray(int[] array);

    public void
showSortResult(int[] array){
       
this.sortArray(array);
       
System.out.println("排序结果:");
        for
(inti=0;i<array.length;i++){
            System.
out.printf("%3s",array[i]);
       
}
    }
}

 

public classConcreteSort extendsabstractSort {
   
@Override
   
protected void sortArray(int[] array) {
       
for(inti=0;i<array.length-1;i++){
            selectSort(array
, i);
       
}
    }

   
private void selectSort(int[] array, intindex) {
       
int MinValue =32767; // 最小值变量
       
int indexMin =0; //最小值索引变量
       
int Temp;// 暂存变量
       
for (inti = index; i < array.length;i++) {
           
if (array[i] < MinValue){// 找到最小值
               
MinValue = array[i];// 储存最小值
               
indexMin = i;
           
}
        }
        Temp = array[index]
; //交换两数值
       
array[index] = array[indexMin];
       
array[indexMin] = Temp;
   
}

 

public classClient {
   
/**
     *
大部分刚入职场的菜鸟们可能深有体会,大牛们把架构搭好,
    
* 比较简单的方法一般写成抽象,让菜鸟们来开发。
    
* @param args
    
*/
   
public static void main(String[] args) {
       
int[] arr = {2,3,1,4,523,11,23};
       
abstractSort a = newConcreteSort();
       
a.showSortResult(arr);
   
}

}

 

3.2 模板方法模式的优缺点

    优点:模板方法可以有一种反向控制的结构,父类调用子类的操作,通过对子类的扩展增加新的行为,符合“开闭原则

       代码复用,在类库中尤为重要,它提取了类库中的公共行为

       容易扩展,增加实现类很容易实现功能扩展。

       灵活:钩子方法,子类的实现也可以影响父类中的逻辑。(违反里氏替换原则)

    缺点:每个不同的实现类都要定义一个子类,系统更加庞大

 

3.3 模板方法模式的使用场景

       多个子类有公共方法,并且逻辑基本相同

       重要、复杂的算法,可以把核心算法设计为模板方法,周边相关细节由子类实现。

       重构时,把相同代码抽取到父类,然后通过钩子方法约束其行为。

 

 

4、代理模式

      4.1 代理模式定义

    为其他对象提供一种代理,以控制对这个对象的访问(可以在目标对象的实现基础上,增加额外的功能,即对目标对象的扩展。)

编程思想:不要修改已经写好的一个方法,如果非要修改,可以通过代理的方法进行扩展。

代理理模式的使用场景非常多,大家可以看看Spring AOP,这是一个非常典型的动态代理。

 

   

4.2 静态代理

被代理的对象和代理接口一般要实现或继承一个共同的代理接口或父类

 

public interface IUserDao {    void save();}
public class UserDao implements IUserDao {    public void save() {        System.out.println("开始保存。。。。");    }}
/** * 代理对象,静态代理 */public class UserDaoProxy implements IUserDao {    private IUserDao target;    public UserDaoProxy(IUserDao target) {        this.target=target;    }    public void save() {        System.out.println("前置处理。。。");        target.save();        System.out.println("后置处理。。。");    }}
public class Client {    public static void main(String[] args) {        UserDao dao = new UserDao();        UserDaoProxy proxy = new UserDaoProxy(dao);        proxy.save();    }}

 

·        缺点:由于共同继承或实现了统一的接口和类,代理类太多,一旦接口中增加了新的方法,代理对象和目标对象都要进行修改维护。

·        优点:可以做到不修改目标对象的前提下,对目标对象进行修改。

可以使用动态代理的方式来解决静态代理中的缺点。

4.3 动态代理

      

4.3.1 动态代理的特点

   a.代理对象,不需要实现接口

   b.代理对象的生成是利用JDK的API,动态的在内存中构建代理(需要我们指定创建代理对象/目标对象实现的接口类型)

   c.动态代理也叫做JDK代理,接口代理

4.3.2 JDK中生成代理对象的API

   代理类所在包:java.lang.reflect.Proxy

   newProxyInstance方法

   static Object newProxyInstance(ClassLoader loader,Class<?>[]interface,InvocationHandler h)

    注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:

·        ClassLoader loader,:指定当前目标对象使用类加载器,获取加载器的方法是固定的

·        Class<?>[]interfaces,:目标对象实现的接口的类型,使用泛型方式确认类型

·        InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入

 

public class ProxyFactory {    private Objecttarget;    public ProxyFactory(Object target){        this.target=target;    }    public  ObjectgetProxyInstance(){
        /** * param[1] 类加载器 * param[2] 被代理对象实现的所有接口 * (在生成代理对象之前,会先判断缓存中是否存在代理对象,如果存在就直接返回, * 判断缓存中是否存在代理对象的时候就会用到类加载器和接口) * param[3] newProxyInstance内部就是根据反射找到代理类的构造函数, * 然后把InvocationHandler这个类掺入代理类中,然后利用反射再次生成代理类对象 **/
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),                target.getClass().getInterfaces(),                new InvocationHandler() {                    public Objectinvoke(Object proxy, Method method, Object[] args) throws Throwable {                        System.out.println("开始事务2");                        //执行目标对象方法                        Object returnValue = method.invoke(target, args);                        System.out.println("提交事务2");                        return returnValue;                    }                });    }}
 
 
 
public class Client {    public static void main(String[] args) {        // 目标对象        IUserDao target = new UserDao();        // 【原始的类型 class cn.itcast.b_dynamic.UserDao】        System.out.println(target.getClass());        // 给目标对象,创建代理对象        IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();        // class $Proxy0   内存中动态生成的代理对象        System.out.println(proxy.getClass());        // 执行方法   【代理对象】        proxy.save();    }}

 

代理对象不用实现接口,但是目标对象一定要实现接口,否则不能使用动态代理。

 

 

 

 

4.3.3 动态代理实现细节

public interfaceSubject {

   
void rent();

    void
hello(String str);
}

 

public classRealSubject implementsSubject {
   
public void rent() {
        System.
out.println("I want to rent my house");
   
}

   
public void hello(String str) {
        System.
out.println("hello "+str);
   
}
}

 

public classDynamicProxy implementsInvocationHandler{
   
// 这个就是我们要代理的真实对象
   
private Objectsubject;

   
//   构造方法,给我们要代理的真实对象赋初值
   
public DynamicProxy(Object subject){
       
this.subject= subject;
   
}

   
public Objectinvoke(Object object,Method method,Object[] args)throwsThrowable{
        
//  在代理真实对象前我们可以添加一些自己的操作
       
System.out.println("before rent house");

       
System.out.println("Method:"+ method);
       
//   当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
       
method.invoke(subject,args);
       
//  在代理真实对象后我们也可以添加一些自己的操作
       
System.out.println("after rent house");

        return null;
   
}

}

 

 

public classClient
{
   
public static void main(String[] args)
    {
       
//   我们要代理的真实对象
       
Subject realSubject = newRealSubject();

        
//    我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
       
InvocationHandler handler = newDynamicProxy(realSubject);

       
/*
         *
通过ProxynewProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
         *
第一个参数handler.getClass().getClassLoader(),我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
         *
第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
         *
第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler这个对象上
         */
       
Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(),realSubject
               .getClass().getInterfaces()
, handler);

       
System.out.println(subject.getClass().getName());
       
subject.rent();
       
subject.hello("world");
   
}
}

 

 

   每一个动态代理都必须要实现Invocationdler这个接口,并且每个代理的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法就会被转发为由InvocationHandler这个接口的invoke方法来进行调用

  Object invoke(Object proxy,Methodmethod,Object[] args) throw Throwable

proxy:代理的真实对象

method:调用真实对象的某个方法的method对象

args:调用真实对象某个方法时接受的参数

 

Proxy类:

Proxy类的作用就是动态创建一个代理对象,用的最多的就是newProxyInstance方法:

Public Object staticnewProxyInstance(Classloader loader,Class<?>[]interfaces,InvocationHandler h)throw IllegalArgumentException

loader: 定义了由哪一个ClassLoader对象来对生成的代理对象进行加载

interfaces:interface数组,表示的是将要给代理的对象提供一组什么接口,如果提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用接口中的方法了

h:InvocationHandler对象,当动态代理再调用方法的时候,会关联到哪一个InvocationHandler对象上

 

我们首先来看看 $Proxy0 这东西,我们看到,这个东西是由 System.out.println(subject.getClass().getName());这条语句打印出来的,那么为什么我们返回的这个代理对象的类名是这样的呢?

Subject subject =(Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(),realSubject

                .getClass().getInterfaces(),handler);

可能我以为返回的这个代理对象会是Subject类型的对象,或者是InvocationHandler的对象,结果却不是,首先我们解释一下为什么我们这里可以将其转化为Subject类型的对象?原因就是在newProxyInstance这个方法的第二个参数上,我们给这个代理对象提供了一组什么接口,那么我这个代理对象就会实现了这组接口,这个时候我们当然可以将这个代理对象强制类型转化为这组接口中的任意一个,因为这里的接口是Subject类型,所以就可以将其转化为Subject类型了

同时我们一定要记住,通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号

接着我们来看看这两句 

subject.rent();
subject.hello("world");

这里是通过代理对象来调用实现的那种接口中的方法,这个时候程序就会跳转到由这个代理对象关联到的 handler 中的invoke方法去执行,而我们的这个 handler 对象又接受了一个 RealSubject类型的参数,表示我要代理的就是这个真实对象,所以此时就会调用 handler 中的invoke方法去执行:

 

4.4 cglib代理

静态代理和动态代理都需要目标对象实现一个接口,但有时候目标对象只是一个单独的对象并没有实现任何一个接口,这个时候可以通过目标对象的子类实现代理,就叫做cglib代理。

Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.

·        JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.

·        Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)

·        Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.

Cglib子类代理实现方法:
1.需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,所以直接引入pring-core-3.2.5.jar即可.
2.引入功能包后,就可以在内存中动态构建子类
3.代理的类不能为final,否则报错
4.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.

 

 

/** * 目标对象,没有实现任何接口 */public class UserDao {    public void save() {        System.out.println("----已经保存数据!----");    }}
/** * Cglib子类代理工厂 * UserDao在内存中动态构建一个子类对象 */public class ProxyFactory implements MethodInterceptor {    //维护目标对象    private Object target;    public ProxyFactory(Object target) {        this.target = target;    }    //给目标对象创建一个代理对象    public Object getProxyInstance(){        //1.工具类        Enhancer en = new Enhancer();        //2.设置父类        en.setSuperclass(target.getClass());        //3.设置回调函数        en.setCallback(this);        //4.创建子类(代理对象)        return en.create();    }    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {        System.out.println("开始事务...");        //执行目标对象的方法        Object returnValue = method.invoke(target, args);        System.out.println("提交事务...");        return returnValue;    }}
public class Client {    public static void main(String[] args) {        //目标对象        UserDao target = new UserDao();        //代理对象        UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();        //执行代理对象的方法        proxy.save();    }}

 

在Spring的AOP编程中:
如果加入容器的目标对象有实现接口,用JDK代理
如果目标对象没有实现接口,用Cglib代理

 

 

5.门面模式 http://blog.csdn.net/xingjiarong/article/details/50066133

      5.1 门面模式的定义

要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用

 

     

子系统

public classClassA {

public voiddoSomethingA(){

//业务逻辑

}

}

public classClassB {

public voiddoSomethingB(){

//业务逻辑

}

}

public classClassC {

public voiddoSomethingC(){

//业务逻辑

}

}

 

门面对象

public classFacade {

//被委托的对象

private ClassA a =new ClassA();

private ClassB b =new ClassB();

private ClassC c =new ClassC();

//提供给外部访问的方法

public voidmethodA(){

this.a.doSomethingA();

}

public voidmethodB(){

this.b.doSomethingB();

}

public voidmethodC(){

this.c.doSomethingC();

}

}

 

5.2 门面模式的优缺点

    优点:减少系统的相互依赖;提高灵活性;提高安全性(不在门面上开通的方法,休想访问到)

    缺点:不符合开闭原则,对修改关闭,对扩展开放

5.3 门面模式的使用场景

    a.为一个复杂的模块或子系统提供一个供外界访问的接口

    b.子系统相对独立,外界对子系统的访问只要黑箱操作即可。

原创粉丝点击