常用设计模式总结

Posted by Jaimin on October 1, 2019

设计模式

工厂模式

  • 简单工厂模式(小作坊)

    见demo,以生产牛奶为例,new的过程比较复杂,所以使用工厂模式来new

    缺点:不易于扩展,增加产品需要修改工厂类的判断逻辑,违背了开闭原则。

    代码实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    
    public interface ICourse {
    /** 录制视频*/
    public void record();
    }
      
    public class JavaCourse implements ICourse {
    public void record() {
    System.out.println("录制Java 课程");
    }
        
    // 创建CourseFactory 工厂类    
    public class CourseFactory {
        public ICourse create(String name){
            if("java".equals(name)){
            	return new JavaCourse();
            }else if("python".equals(name)){
            	return new PythonCourse();
            }else {
            	return null;
            }
        }
    } 
          
    //调用
    public class SimpleFactoryTest {
        public static void main(String[] args) {
            CourseFactory factory = new CourseFactory();
            factory.create("java");
        }
    }    
    
  • 工厂模式(标准化、单一品牌)

    工厂方法模式(Fatory Method Pattern)是指定义一个创建对象的接口,但让实现这个 接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行。

    例子:某一品牌的工厂,专门负责生产的工厂

    代码实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    
    // 工厂模型 hie
    public interface ICourseFactory {
    	ICourse create();
    }
      
    //某一品牌的工厂,专门负责生产的工厂
    public class JavaCourseFactory implements ICourseFactory {
        public ICourse create() {
        	return new JavaCourse();
    	}
    }
    public class PythonCourseFactory implements ICourseFactory {
        public ICourse create() {
        return new PythonCourse();
        }
    }
    // 测试
    public class FactoryMethodTest {
      
        public static void main(String[] args) {
      
            ICourseFactory factory = new PythonCourseFactory();
            ICourse course = factory.create();
            course.record();
      
            factory = new JavaCourseFactory();
            course = factory.create();
            course.record();
      
        }
      
    }
    

    工厂方法适用于以下场景: 1、创建对象需要大量重复的代码。 2、客户端(应用层)不依赖于产品类实例如何被创建、实现等细节。 3、一个类通过其子类来指定创建哪个对象。 工厂方法也有缺点: 1、类的个数容易过多,增加复杂度。 2、增加了系统的抽象性和理解难度。

  • 抽象工厂模式(公共逻辑、便于统一管理、sping应用最为广泛的一种、易扩展)

    抽象工厂模式(Abastract Factory Pattern)是指提供一个创建一系列相关或相互依赖对象的接口,无须指定他们具体的类。

    抽象工厂是用户的入口,对于用户更加简单,用户只有选择的权利(不用输入)不容易出错;扩展时只需在抽象类中新增一个api即可。

    代码实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    
    // 产品族
    public interface IVideo {
    	void record();
    }
    public interface INote {
    	void edit();
    }
    /**
     * 抽象工厂是用户的主入口
     * 在Spring中应用得最为广泛的一种设计模式
     * 易于扩展
     */
    public interface CourseFactory {
        INote createNote();
        IVideo createVideo();
    }
      
    //java 产品族
    public class JavaVideo implements IVideo {
        public void record() {
        	System.out.println("录制Java 视频");
        }
    }
    public class JavaNote implements INote {
        public void edit() {
        	System.out.println("编写Java 笔记");
        }
    }
      
    // Java 产品族的具体工厂JavaCourseFactory
    public class JavaCourseFactory implements CourseFactory {
        public INote createNote() {
       		return new JavaNote();
        }
        public IVideo createVideo() {
        	return new JavaVideo();
        }
    }
    // 测试
    public static void main(String[] args) {
        JavaCourseFactory factory = new JavaCourseFactory();
        factory.createNote().edit();
        factory.createVideo().record();
    }
    

    抽象工厂也是有缺点的: 1、规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂 的接口。 2、增加了系统的抽象性和理解难度。

归纳:把细节隐藏,把结果对外公布

单例模式

(spring中bean默认的模式)

一个类模板,在整个系统运行过程只允许产生一个实例(有且只有一个),例如分布式锁;

目的:为了解决并发访问时的线程安全问题

保证单例的实现方案:饿汉式、懒汉式、注册登记式、枚举式、序列化与反序列化

  • 饿汉式:在类加载的时候就立即初始化 并创建单例对象,绝对线程安全,在线程出现以前已经实例化,不可能存在访问安全问题

    • 优点:没有锁,执行效率高

    • 缺点:类加载的时候就初始化 浪费内存

    代码实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    public class HungrySingleton {
        //先静态、后动态
        //先属性、后方法
        //先上后下
        private static final HungrySingleton hungrySingleton = new HungrySingleton();
      
        private HungrySingleton(){}
      
        public static HungrySingleton getInstance(){
            return  hungrySingleton;
        }
    }
    
  • 懒汉式:默认加载不实例化,在需要用到实例的时候才实例化,例如延时加载

    代码实现(简单版)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    //懒汉式单例
    //在外部需要使用的时候才进行实例化
    //加锁来保证线程安全问题,存在性能问题
    public class LazySimpleSingleton {
        private LazySimpleSingleton(){}
        //静态块,公共内存区域
        private static LazySimpleSingleton lazy = null;
        public synchronized static LazySimpleSingleton getInstance(){
            if(lazy == null){
                lazy = new LazySimpleSingleton();
            }
            return lazy;
        }
    }
    

    代码实现(双重检查)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    // 在对象为空时在进行加锁
    public class LazyDoubleCheckSingleton {
        private volatile static LazyDoubleCheckSingleton lazy = null;
      
        private LazyDoubleCheckSingleton(){}
        public static LazyDoubleCheckSingleton getInstance(){
            if(lazy == null){
                synchronized (LazyDoubleCheckSingleton.class){
                    if(lazy == null){
                        lazy = new LazyDoubleCheckSingleton();
                        //1.分配内存给这个对象
                        //2.初始化对象
                        //3.设置lazy指向刚分配的内存地址
                        //4.初次访问对象
                    }
                }
            }
            return lazy;
        }
    }
    

    反射破坏单例,上面介绍的单例模式的构造方法除了加上private 以外,没有做任何处理。如果我们使用反射来调用其构造方法,然后,再调用getInstance()方法,应该就会两个不同的实例。在其构造方法中做一些限制,一旦出现多次重复创建,则直接抛出异常。

    代码实现(静态内部类)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
    //这种形式兼顾饿汉式的内存浪费,也兼顾synchronized性能问题
    //完美地屏蔽了这两个缺点
    //史上最牛B的单例模式的实现方式
    public class LazyInnerClassSingleton {
        //默认使用LazyInnerClassGeneral的时候,会先初始化内部类
        //如果没使用的话,内部类是不加载的
        private LazyInnerClassSingleton(){
            if(LazyHolder.LAZY != null){
                throw new RuntimeException("不允许创建多个实例");
            }
        }
      
        //每一个关键字都不是多余的
        //static 是为了使单例的空间共享
        //保证这个方法不会被重写,重载
        public static final LazyInnerClassSingleton getInstance(){
            //在返回结果以前,一定会先加载内部类
            return LazyHolder.LAZY;
        }
      
        //默认不加载
        private static class LazyHolder{
            private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
        }
    }
    
  • 注册式单例

    代码实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
    //Spring中的做法,就是用这种注册式单例(比如BeanFactory)
    public class ContainerSingleton {
        private ContainerSingleton(){}
        private static Map<String,Object> ioc = new ConcurrentHashMap<String,Object>();
        public static Object getInstance(String className){
            synchronized (ioc) {
                if (!ioc.containsKey(className)) {
                    Object obj = null;
                    try {
                        obj = Class.forName(className).newInstance();
                        ioc.put(className, obj);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    return obj;
                } else {
                    return ioc.get(className);
                }
            }
        }
    }
    
  • 枚举式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//常量中去使用,常量不就是用来大家都能够共用吗?
//通常在通用API中使用
public enum EnumSingleton {
    INSTANCE;
    private Object data;
    public Object getData() {
        return data;
    }
    public void setData(Object data) {
        this.data = data;
    }
    public static EnumSingleton getInstance(){
        return INSTANCE;
    }
}
  • 序列化:就是把内存中的状态转换为字节码的形式,从而转换为一个IO流,写入到其他地方(磁盘,网络IO),内存中的状态永久保存下来

  • 反序列化:将已经持久化的字节码内容转换为io流 ,通过IO流的读取,进而将读取的内容转换为Java对象,在转换过程中会重新创建对象(new)

    代码实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    //反序列化时导致单例破坏
    public class SeriableSingleton implements Serializable {
      
        //序列化就是说把内存中的状态通过转换成字节码的形式
        //从而转换一个IO流,写入到其他地方(可以是磁盘、网络IO)
        //内存中状态给永久保存下来了
      
        //反序列化
        //讲已经持久化的字节码内容,转换为IO流
        //通过IO流的读取,进而将读取的内容转换为Java对象
        //在转换过程中会重新创建对象new
      
        public  final static SeriableSingleton INSTANCE = new SeriableSingleton();
        private SeriableSingleton(){}
      
        public static SeriableSingleton getInstance(){
            return INSTANCE;
        }
      
        private  Object readResolve(){
            return  INSTANCE;
        }
      
    }
    
  • ThreadLocal 线程单例

    ThreadLocal 不能保证其创建的对象是全局唯一,但是能保证在单个线程中是唯一的,天生的线程安全。

    ThreadLocal将所有的对象全部放在ThreadLocalMap 中,为每个线程都提供一个对象,实际上是以空间换时间来实现线程间隔离的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    public class ThreadLocalSingleton {
        private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance =
                new ThreadLocal<ThreadLocalSingleton>(){
                    @Override
                    protected ThreadLocalSingleton initialValue() {
                        return new ThreadLocalSingleton();
                    }
                };
      
        private ThreadLocalSingleton(){}
      
        public static ThreadLocalSingleton getInstance(){
            return threadLocalInstance.get();
        }
    }
    

原型模式

原型模式(Prototype Pattern)是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

在每次使用对象前,都会创建一个新的对象,并且会将依赖关系完整的赋值给这个新的对象。

特点:数据内容相同,但对象实例不同(完全两个个体),例如:孙悟空吹毫毛

应用:反射、克隆(复制)

适用场景:

1
2
3
4
5
1、类初始化消耗资源较多。
2、new 产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)

3、构造函数比较复杂。
4、循环体中生产大量对象时。
  • 浅克隆 (引用地址相同)

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//先创建原型Prototype 接口
public interface Prototype{
	Prototype clone();
}

public class ConcretePrototypeA implements Prototype {
    private int age;
    private String name;
    private List hobbies;

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public List getHobbies() {
        return hobbies;
    }
    public void setHobbies(List hobbies) {
        this.hobbies = hobbies;
    }

    @Override
    public ConcretePrototypeA clone() {
        ConcretePrototypeA concretePrototype = new ConcretePrototypeA();
        concretePrototype.setAge(this.age);
        concretePrototype.setName(this.name);
        concretePrototype.setHobbies(this.hobbies);
        return concretePrototype;
    }
}

// client对象
public class Client {
    private Prototype prototype;
    public Client(Prototype prototype){
    	this.prototype = prototype;
    }
    public Prototype startClone(Prototype concretePrototype){
    	return (Prototype)concretePrototype.clone();
    }
}

// 测试代码
public class PrototypeTest {
    public static void main(String[] args) {
        // 创建一个具体的需要克隆的对象
        ConcretePrototypeA concretePrototype = new ConcretePrototypeA();
        // 填充属性,方便测试
        concretePrototype.setAge(18);
        concretePrototype.setName("prototype");
        List hobbies = new ArrayList<String>();
        concretePrototype.setHobbies(hobbies);
        System.out.println(concretePrototype);

        // 创建Client对象,准备开始克隆
        Client client = new Client(concretePrototype);
        ConcretePrototypeA concretePrototypeClone = (ConcretePrototypeA) client.startClone(concretePrototype);
        System.out.println(concretePrototypeClone);

        System.out.println("克隆对象中的引用类型地址值:" + concretePrototypeClone.getHobbies());
        System.out.println("原对象中的引用类型地址值:" + concretePrototype.getHobbies());
        System.out.println("对象地址比较:"+(concretePrototypeClone.getHobbies() == concretePrototype.getHobbies()));
    }
}

运行结果
com.gupao.prototype.simple.ConcretePrototypeA@1540e19d
com.gupao.prototype.simple.ConcretePrototypeA@677327b6
克隆对象中的引用类型地址值[]
原对象中的引用类型地址值[]
对象地址比较true

从测试结果看出hobbies 的引用地址是相同的,意味着复制的不是值,而是引用的地址。这样的话, 如果我们修改任意一个对象中的属性值, concretePrototype 和concretePrototypeCone 的hobbies 值都会改变。这就是我们常说的浅克隆。只是完整复制了值类型数据,没有赋值引用对象。换言之,所有的引用对象仍然指向原来的对象,显然不是我们想要的结果。

  • 深克隆(地址不一样,一个新的对象)

    代码实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    
    // 原型类
    public class Monkey {
        public int height;
        public int weight;
        public Date birthday;
    }
      
    //引用对象
    public class JinGuBang implements Serializable {
        public float h = 100;
        public float d = 10;
        public void big(){
        this.d *= 2;
        this.h *= 2;
        }
        public void small(){
        this.d /= 2;
        this.h /= 2;
        }
    }
    //具体的对象
    public class QiTianDaSheng extends Monkey implements Cloneable,Serializable {
        public JinGuBang jinGuBang;
        public  QiTianDaSheng(){
            //只是初始化
            this.birthday = new Date();
            this.jinGuBang = new JinGuBang();
        }
      
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return this.deepClone();
        }
        public Object deepClone(){
            try{
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(bos);
                oos.writeObject(this);
      
                ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
                ObjectInputStream ois = new ObjectInputStream(bis);
      
                QiTianDaSheng copy = (QiTianDaSheng)ois.readObject();
                copy.birthday = new Date();
                return copy;
            }catch (Exception e){
                e.printStackTrace();
                return null;
            }
        }
          
        public QiTianDaSheng shallowClone(QiTianDaSheng target){
            QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();
            qiTianDaSheng.height = target.height;
            qiTianDaSheng.weight = target.height;
      
            qiTianDaSheng.jinGuBang = target.jinGuBang;
            qiTianDaSheng.birthday = new Date();
            return  qiTianDaSheng;
        }
          
    }
    // 测试类
    public class DeepCloneTest {
        public static void main(String[] args) {
            QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();
            try {
                QiTianDaSheng clone = (QiTianDaSheng)qiTianDaSheng.clone();
                System.out.println("深克隆:" + (qiTianDaSheng.jinGuBang == clone.jinGuBang));
            } catch (Exception e) {
                e.printStackTrace();
            }
      
            QiTianDaSheng q = new QiTianDaSheng();
            QiTianDaSheng n = q.shallowClone(q);
            System.out.println("浅克隆:" + (q.jinGuBang == n.jinGuBang));
              
        }
    }
    运行结果
    深克隆false
    浅克隆true
    

    克隆破坏单例模式

    如果我们克隆的目标的对象是单例对象,那意味着,深克隆就会破坏单例。实际上防止 克隆破坏单例解决思路非常简单,禁止深克隆便可。要么你我们的单例类不实现 Cloneable 接口;要么我们重写clone()方法,在clone 方法中返回单例对象即可,具体 代码如下:

    1
    2
    3
    4
    
    @Override
    protected Object clone() throws CloneNotSupportedException {
    	return INSTANCE;
    }
    

代理模式

代理模式(ProxyPattern)是指为其他对象提供一种代理,以控制对这个对象的访问。代理对象在客服端和目标对象之间起到中介作用,代理模式属于结构型设计模式。

使用代理模式主要有两个目的:一保护目标对象,二增强目标对象。

作用:字节码增强、非侵入式

字节码增强:是在Java字节码生成之后,运行期对其进行修改,增强其功能,这种方式相当于对应用程序的二进制文件进行修改。Java字节码增强主要是为了减少冗余代码,提高性能等。

代理角色,被代理角色(目标对象),由被代理角色来做最终的决定

静态代理和动态代理区别

  • 静态代理:在代理之前,所有东西都是已知的(人工)

  • 动态代理:在代理之前,所有东西都是未知的(自动化、智能化)

1、静态代理只能通过手动完成代理操作,如果被代理类增加新的方法,代理类需要同步 新增,违背开闭原则。 2、动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开 闭原则。 3、若动态代理要对目标类的增强逻辑扩展,结合策略模式,只需要新增策略类便可完成, 无需修改代理类的代码。

静态代理代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 被代理对象
public class Son implements Person {
    public void findLove(){
        System.out.println("儿子要求:肤白貌美大长腿");
    }
}
// 代理对象
public class Father implements Person {
    private Son person;

    public Father(Son person){
        this.person = person;
    }

    public void findLove(){
        System.out.println("父亲物色对象");
        this.person.findLove();
        System.out.println("双方父母同意,确立关系");
    }
}
// 测试类
public static void main(String[] args) {
    //只能代理指定的人
    Father father = new Father(new Son());
    father.findLove();
}

动态代理代码实现(JDK)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
 * 代理类
 */
public class JDKMeipo implements InvocationHandler {

    private Object target;
    public Object getInstance(Object target) throws Exception{
        this.target = target;
        Class<?> clazz = target.getClass();
        return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object obj = method.invoke(this.target,args);
        after();
       return obj;
    }

    private void before(){
        System.out.println("我是媒婆,我要给你找对象,现在已经确认你的需求");
        System.out.println("开始物色");
    }

    private void after(){
        System.out.println("OK的话,准备办事");
    }
}

/**
 *被代理对象
 */
public class Customer implements Person {
    public void findLove() {
        System.out.println("高富帅");
        System.out.println("身高180cm");
    }
}
// 测试
    public static void main(String[] args) {
        try {
            Object obj = new JDKMeipo().getInstance(new Customer());
            Method method = obj.getClass().getMethod("findLove",null);
            method.invoke(obj);
        }catch (Exception e){
            e.printStackTrace();
        }

JDK Proxy 生成对象的步骤如下: 1、拿到被代理对象的引用,并且获取到它的所有的接口,反射获取。 2、JDK Proxy 类重新生成一个新的类、同时新的类要实现被代理类所有实现的所有的接 口。 3、动态生成Java 代码,把新加的业务逻辑方法由一定的逻辑代码去调用(在代码中体 现)。 4、编译新生成的Java 代码.class。 5、再重新加载到JVM 中运行。 以上这个过程就叫字节码重组。JDK 中有一个规范,在ClassPath 下只要是$开头的class文件一般都是自动生成的

为什么JDK动态代理中要求目标类实现的接口数量不能超过65535个?

JVM在class文件定义时,规定interface的类型是u2,2个字节,16位,那么最多支持2的16次方-1,也就是65535

动态代理代码实现(Cglib)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class CglibMeipo implements MethodInterceptor {

    public Object getInstance(Class<?> clazz) throws Exception{
        //相当于Proxy,代理的工具类
        Enhancer enhancer = new Enhancer();
        //要把哪个设置为即将生成的新类父类
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //业务的增强
        before();
        Object obj = methodProxy.invokeSuper(o,objects);
        after();
        return obj;
    }

    private void before(){
        System.out.println("我是媒婆,我要给你找对象,现在已经确认你的需求");
        System.out.println("开始物色");
    }

    private void after(){
        System.out.println("OK的话,准备办事");
    }
}

// 被代理对象
public class Customer {
    public void findLove(){
        System.out.println("儿子要求:肤白貌美大长腿");
    }
}
// CGLib 代理的目标对象不需要实现任何接口,它是通过动态继承目标对象实现的动态代
public class CglibTest {
    public static void main(String[] args) {
        try {
            Customer obj = (Customer)new CglibMeipo().getInstance(Customer.class);
            obj.findLove();
        } catch (Exception e) {
        	e.printStackTrace();
        }
    }
}

CGLib 和JDK 动态代理对比 1.JDK 动态代理是实现了被代理对象的接口(已经继承了Proxy,所以只能实现接口了),CGLib 是继承了被代理对象。 2.JDK 和CGLib 都是在运行期生成字节码,JDK 是直接写Class 字节码,CGLib 使用ASM 框架写Class 字节码,Cglib 代理实现更复杂,生成代理类比JDK 效率低。 3.JDK 调用代理方法,是通过反射机制调用,CGLib 是通过FastClass 机制直接调用方法, CGLib 执行效率更高。

优点:

1、代理模式能将代理对象与真实被调用的目标对象分离。 2、一定程度上降低了系统的耦合度,扩展性好。 3、可以起到保护目标对象的作用。 4、可以对目标对象的功能增强。

缺点:

1、代理模式会造成系统设计中类的数量增加。 2、在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢。 3、增加了系统的复杂度。

策略模式

策略模式(Strategy Pattern)是指定义了算法家族、分别封装起来,让它们之间可以互 相替换,此模式让算法的变化不会影响到使用算法的用户。

策略模式的应用场景 1、假如系统中有很多类,而他们的区别仅仅在于他们的行为不同。 2、一个系统需要动态地在几种算法中选择一种。

特点:最终执行结果固定, 执行过程和执行逻辑不一样(可以进行选择)

案例:用不同的支付方式进行支付(支付过程和算法不一样,结果相同),通过策略模式封装支付过程

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/**
 * 支付渠道
 * Payment 抽象类,定义支付规范和支付逻辑
 */
public abstract class Payment {

    //支付类型
    public abstract String getName();

    //查询余额
    protected abstract double queryBalance(String uid);

    //扣款支付
    public MsgResult pay(String uid, double amount) {
        if(queryBalance(uid) < amount){
            return new MsgResult(500,"支付失败","余额不足");
        }
        return new MsgResult(200,"支付成功","支付金额:" + amount);
    }
}

/**
 * 具体的支付方式
 */
public class AliPay extends Payment {

    public String getName() {
        return "支付宝";
    }

    protected double queryBalance(String uid) {
        return 900;
    }

}

public class WechatPay extends Payment {

    public String getName() {
        return "微信支付";
    }

    protected double queryBalance(String uid) {
        return 256;
    }

}
/**
 * 支付完成以后的状态
 */
public class MsgResult {
    private int code;
    private Object data;
    private String msg;

    public MsgResult(int code, String msg, Object data) {
        this.code = code;
        this.data = data;
        this.msg = msg;
    }

    public String toString(){
        return ("支付状态:[" + code + "]," + msg + ",交易详情:" + data);
    }
}
/**
 * 支付策略管理
 */
public class PayStrategy {
    public static final String ALI_PAY = "AliPay";
    public static final String JD_PAY = "JdPay";
    public static final String UNION_PAY = "UnionPay";
    public static final String WECHAT_PAY = "WechatPay";
    public static final String DEFAULT_PAY = ALI_PAY;

    private static Map<String,Payment> payStrategy = new HashMap<String,Payment>();
    static {
        payStrategy.put(ALI_PAY,new AliPay());
        payStrategy.put(WECHAT_PAY,new WechatPay());
        payStrategy.put(UNION_PAY,new UnionPay());
        payStrategy.put(JD_PAY,new JDPay());
    }

    public static Payment get(String payKey){
        if(!payStrategy.containsKey(payKey)){
            return payStrategy.get(DEFAULT_PAY);
        }
        return payStrategy.get(payKey);
    }
}

// 订单类
public class Order {
    private String uid;
    private String orderId;
    private double amount;

    public Order(String uid,String orderId,double amount){
        this.uid = uid;
        this.orderId = orderId;
        this.amount = amount;
    }

    //完美地解决了switch的过程,不需要在代码逻辑中写switch了
    //更不需要写if    else if
    public MsgResult pay(){
        return pay(PayStrategy.DEFAULT_PAY);
    }

    public MsgResult pay(String payKey){
        Payment payment = PayStrategy.get(payKey);
        System.out.println("欢迎使用" + payment.getName());
        System.out.println("本次交易金额为:" + amount + ",开始扣款...");
        return payment.pay(uid,amount);
    }
}
// 测试
    public static void main(String[] args) {
        //省略把商品添加到购物车,再从购物车下单
        //直接从点单开始
        Order order = new Order("1","20180311001000009",324.45);

        //开始支付,选择微信支付、支付宝、银联卡、京东白条、财付通
        //每个渠道它支付的具体算法是不一样的
        //基本算法固定的

        //这个值是在支付的时候才决定用哪个值
        System.out.println(order.pay(PayStrategy.ALI_PAY));
    }
}

策略模式的优缺点 优点: 1、策略模式符合开闭原则。 2、避免使用多重条件转移语句,如if…else…语句、switch 语句 3、使用策略模式可以提高算法的保密性和安全性。 缺点: 1、客户端必须知道所有的策略,并且自行决定使用哪一个策略类。 2、代码中会产生非常多策略类,增加维护难度。

模板模式(模板方法模式)

模板模式通常又叫模板方法模式(Template Method Pattern)是指定义一个算法的骨架,并允许子类为一个或者多个步骤提供实现。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤,属于行为性设计模式。

模板方法适用于以下应用场景: 1、一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。 2、各子类中公共的行为被提取出来并集中到一个公共的父类中,从而避免代码重复。

JdbcTemplate

策略模式:只有选择权(由用户选择已有的固定算法)比如选择支付方式

模板模式:侧重点不是选择,因为没得选择,你必须这么做,可以参与某一部分的自定义(比如创建订单、支付订单)

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/**
 * 模板会有一个或者多个未现实方法,
 * 而且这几个未实现方法有固定的执行循序
 */
public abstract class NetworkCourse {

    protected final void createCourse(){
        //1、发布预习资料
        this.postPreResource();

        //2、制作PPT课件
        this.createPPT();

        //3、在线直播
        this.liveVideo();

        //4、提交课件、课堂笔记
        this.postNote();

        //5、提交源码
        this.postSource();

        //6、布置作业,有些课是没有作业,有些课是有作业的
        //如果有作业的话,检查作业,如果没作业,完成了
        if(needHomework()){
            checkHomework();
        }
    }

    abstract void checkHomework();

    //钩子方法:实现流程的回调
    // 目的是用来干预执行流程,使得我们控制行为流程更加灵活,更符合实际业
    //务的需求。钩子方法的返回值一般为适合条件分支语句的返回值(如boolean、int 等)。
    protected boolean needHomework(){return false;}

    final void postSource(){
        System.out.println("提交源代码");
    }

    final void postNote(){
        System.out.println("提交课件和笔记");
    }

    final void liveVideo(){
        System.out.println("直播授课");
    }

    final void createPPT(){
        System.out.println("创建备课PPT");
    }

    final void postPreResource(){
        System.out.println("分发预习资料");
    }

}

public class BigDataCourse extends NetworkCourse {

    private boolean needHomeworkFlag = false;

    public BigDataCourse(boolean needHomeworkFlag) {
        this.needHomeworkFlag = needHomeworkFlag;
    }

    void checkHomework() {
        System.out.println("检查大数据的课后作业");
    }

    @Override
    protected boolean needHomework() {
        return this.needHomeworkFlag;
    }
}

public class JavaCourse extends NetworkCourse {
    void checkHomework() {
        System.out.println("检查Java的架构课件");
    }
}

//测试方法
 public static void main(String[] args) {
        System.out.println("---Java架构师课程---");
        NetworkCourse javaCourse = new JavaCourse();
        javaCourse.createCourse();

        System.out.println("---大数据课程---");
        NetworkCourse bigDataCourse = new BigDataCourse(true);
        bigDataCourse.createCourse();

    }

模板模式的优缺点

优点: 1、利用模板方法将相同处理逻辑的代码放到抽象父类中,可以提高代码的复用性。 2、将不同的代码不同的子类中,通过对子类的扩展增加新的行为,提高代码的扩展性。 3、把不变的行为写在父类上,去除子类的重复代码,提供了一个很好的代码复用平台, 符合开闭原则。 缺点: 1、类数目的增加,每一个抽象类都需要一个子类来实现,这样导致类的个数增加。 2、类数量的增加,间接地增加了系统实现的复杂度。 3、继承关系自身缺点,如果父类添加新的抽象方法,所有子类都要改一遍。

委派模式(不属于23种涉及模式之一,面向对象设计模式中常用的一种模式)

委派模式(Delegate Pattern)的基本作用就是负责任务的调用和分配任务,跟代理模式很像,可以看做是一种特殊情况下的静态代理的全权代理,但是代理模式注重过程,而委派模式注重结果

spring中的体现:以Delegate结尾的类名、Dispatcher结尾

老板-项目经理-员工的关系(老板给项目经理下达任务,项目经理给员工分配工作)

例:客户请求(Boss)、委派者(Leader)、被被委派者(Target)

委派者要持有被委派者的引用 代理模式注重的是过程, 委派模式注重的是结果 策略模式注重是可扩展(外部扩展),委派模式注重内部的灵活和复用 委派的核心:就是分发、调度、派遣 委派模式:就是静态代理和策略模式一种特殊的组合。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/**
 * 被委派者A
 */
public class EmployeeA implements IEmployee {
    @Override
    public void doing(String command) {
        System.out.println("我是员工A,我现在开始干" + command + "工作");
    }
}
/**
 * 被委派者B
 */
public class EmployeeB implements IEmployee {
    @Override
    public void doing(String command) {
        System.out.println("我是员工B,我现在开始干" + command + "工作");
    }
}
/**
 * 项目经理Leader 负责分派任务
 */
public class Leader implements IEmployee {

    private Map<String,IEmployee> targets = new HashMap<String,IEmployee>();

    public Leader() {
        targets.put("加密",new EmployeeA());
        targets.put("登录",new EmployeeB());
    }

    //项目经理自己不干活
    public void doing(String command){
        targets.get(command).doing(command);
    }
}

/**
 * Boss 类下达命令
 */
public class Boss {

    public void command(String command,Leader leader){
        leader.doing(command);
    }
}
// 测试
public class DelegateTest {
    public static void main(String[] args) {
        //客户请求(Boss)、委派者(Leader)、被被委派者(Target)
        //委派者要持有被委派者的引用
        //代理模式注重的是过程, 委派模式注重的是结果
        //策略模式注重是可扩展(外部扩展),委派模式注重内部的灵活和复用
        //委派的核心:就是分发、调度、派遣

        //委派模式:就是静态代理和策略模式一种特殊的组合

        new Boss().command("登录",new Leader());
    }
}

适配器模式(兼容、转换)

适配器模式(Adapter Pattern)是指将一个类的接口转换成客户期望的另一个接口,使原本的接口不兼容的类可以一起工作,属于结构型设计模式。

为了保持老系统的稳定性,不去修改原来的代码,但是又要为了兼容新的需求或者标准,不得不在系统做一些文章。(向下兼容)

适配器适用于以下几种业务场景: 1、已经存在的类,它的方法和需求不匹配(方法结果相同或相似)的情况。 2、适配器模式不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品、不同厂家造成功能类似而接口不相同情况下的解决方案。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/**
 * 在适配器里面,这个接口是可有可无,不要跟模板模式混淆
 * 模板模式一定是抽象类,而这里仅仅只是一个接口
 */
public interface LoginAdapter {
    boolean support(Object adapter);
    ResultMsg login(String id, Object adapter);
}
// 实现类
public class LoginForQQAdapter implements LoginAdapter {
    public boolean support(Object adapter) {
        return adapter instanceof LoginForQQAdapter;
    }

    public ResultMsg login(String id, Object adapter) {
        return null;
    }
}
// 第三方登录兼容接口
public interface IPassportForThird {

    /**
     * QQ登录
     * @param id
     * @return
     */
    ResultMsg loginForQQ(String id);
}

//实现类
/**
 * 结合策略模式、工厂模式、适配器模式
 */
public class PassportForThirdAdapter extends SiginService implements IPassportForThird {

    public ResultMsg loginForQQ(String id) {
        return processLogin(id,LoginForQQAdapter.class);
    }
    
    //这里用到了简单工厂模式及策略模式
    private ResultMsg processLogin(String key,Class<? extends LoginAdapter> clazz){
        try{
            //适配器不一定要实现接口
            LoginAdapter adapter = clazz.newInstance();

            //判断传过来的适配器是否能处理指定的逻辑
            if(adapter.support(adapter)){
                return adapter.login(key,adapter);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
}
// 测试
  public static void main(String[] args) {
        IPassportForThird passportForThird = new PassportForThirdAdapter();
        passportForThird.loginForQQ("");
    }

// 若实现微信等第三方登录时按照此功能扩展即可

适配器模式的优缺点 优点: 1、能提高类的透明性和复用,现有的类复用但不需要改变。 2、目标类和适配器类解耦,提高程序的扩展性。 3、在很多业务场景中符合开闭原则。 缺点: 1、适配器编写过程需要全面考虑,可能会增加系统的复杂性。 2、增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。

装饰者模式 Decorator(包装器模式)

装饰者模式(Decorator Pattern)是指在不改变原有对象的基础之上,将功能附加到对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能),属于结构型模式。

也就是为了某个实现类在不是修改原始类的基础上进行动态的覆盖或增加方法

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

3、满足is-a 的关系(父子继承关系)

装饰者在代码程序中适用于以下场景: 1、用于扩展一个类的功能或给一个类添加附加职责。 2、动态的给一个对象添加功能,这些功能可以再动态的撤销。

应用:IO 流包装、数据源包装

Spring 中用到的包装器模式在类名上有两种表现:一种是类名中含有Wrapper,另一种是类名中含有 Decorator。基本上都是动态地给一个对象添加一些额外的职责.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
/**
 * 煎饼的抽象Battercake  最初的状态 还未进行装饰
 */
public abstract class Battercake {
    protected abstract String getMsg();
    protected abstract int getPrice();
}
/**
 * 基础套餐
 */
public class BaseBattercake extends Battercake {
    protected String getMsg(){
        return "煎饼";
    }
    public int getPrice(){
        return 5;
    }
}
/**
 * 扩展套餐的抽象装饰者
 */
public abstract class BattercakeDecorator extends Battercake {
    //静态代理,委派
    private Battercake battercake;

    public BattercakeDecorator(Battercake battercake) {
        this.battercake = battercake;
    }
    protected abstract void doSomething();

    @Override
    protected String getMsg() {
        return this.battercake.getMsg();
    }

    @Override
    protected int getPrice() {
        return this.battercake.getPrice();
    }
}
/**
 * 鸡蛋装饰者  可以计算添加鸡蛋之后的价格
 */
public class EggDecorator extends BattercakeDecorator {
    public EggDecorator(Battercake battercake) {
        super(battercake);
    }

    protected void doSomething() {
    }

    @Override
    protected String getMsg() {
        return super.getMsg() + "+1个鸡蛋";
    }

    @Override
    protected int getPrice() {
        return super.getPrice() + 1;
    }
}
/**
 * 香肠装饰者  可以计算添加香肠之后的价格
 */
public class SausageDecorator extends BattercakeDecorator {
    public SausageDecorator(Battercake battercake) {
        super(battercake);
    }

    protected void doSomething() {
    }

    @Override
    protected String getMsg() {
        return super.getMsg() + "+1根香肠";
    }

    @Override
    protected int getPrice() {
        return super.getPrice() + 2;
    }
}

// 测试
public class BattercakeTest {
    public static void main(String[] args) {
        Battercake battercake;
        //路边摊买一个煎饼
        battercake = new BaseBattercake();
        //煎饼有点小,想再加一个鸡蛋
        battercake = new EggDecorator(battercake);
        //很饿,再加根香肠
        battercake = new SausageDecorator(battercake);
        
        //跟静态代理最大区别就是职责不同
        //静态代理不一定要满足is-a的关系
        //静态代理会做功能增强,同一个职责变得不一样

        //装饰器更多考虑是扩展
        System.out.println(battercake.getMsg() + ",总价:" + battercake.getPrice());
    }
}

装饰者模式和适配器模式对比

  装饰者模式 适配器模式
形式 是一种非常特别的适配器模式 没有层级关系,装饰器模式有层级关系
定义 装饰者和被装饰者都实现同一个接口,主要目的是为了扩展之后依旧保留OOP 关系 适配器和被适配者没有必然的联系,通常是采用继承或代理的形式进行包装
关系 满足is-a 的关系 满足has-a 的关系
功能 注重覆盖、扩展 注重兼容、转换
设计 前置考虑 后置考虑

装饰者模式的优缺点 优点: 1、装饰者是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象 扩展功能,即插即用。 2、通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果。 3、装饰者完全遵守开闭原则。 缺点: 1、会出现更多的代码,更多的类,增加程序复杂性。 2、动态装饰时,多层装饰时会更复杂。

观察者模式 Observer

观察者模式(Observer Pattern)定义了对象之间的一对多依赖,让多个观察者对象同时监听一个主体对象,当主体对象发生变化时,它的所有依赖者(观察者)都会收到通知并更新,属于行为型模式。观察者模式有时也叫做发布订阅模式。

发布者(publish)

订阅者(subscribe)

观察者和被观察者没有必然的联系,在注册事件的时候才有了联系

应用场景:监听器、日志收集、短信通知、邮件通知

Spring 中Observer 模式常用的地方是Listener 的实现。如ApplicationListener。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/**
 * JDK提供的一种观察者的实现方式,被观察者
 */
public class GPer extends Observable{

    private String name = "GPer生态圈";
    private static GPer gper = null;
    private GPer(){}

    public static GPer getInstance(){
        if(null == gper){
            gper = new GPer();
        }
        return gper;
    }

    public String getName() {
        return name;
    }

    public void publishQuestion(Question question){
        System.out.println(question.getUserName() + "在" + this.name + "上提交了一个问题。");
        setChanged();
        notifyObservers(question);
    }
}
/**
 * 观察者
 */
public class Teacher implements Observer {
    private String name;
    public Teacher(String name){
        this.name = name;
    }

    public void update(Observable o, Object arg) {
        GPer gper = (GPer)o;
        Question question = (Question)arg;
        System.out.println("===============================");
        System.out.println(name + "老师,你好!\n" +
        "您收到了一个来自“" + gper.getName() + "”的提问,希望您解答,问题内容如下:\n" +
        question.getContent() + "\n" +
        "提问者:" + question.getUserName());
    }
}
// 测试类
public class ObserverTest {
    public static void main(String[] args) {
        GPer gper = GPer.getInstance();
        Teacher tom = new Teacher("Tom");
        Teacher mic = new Teacher("Mic");

        //这为没有@Tom老师
        Question question = new Question();
        question.setUserName("小明");
        question.setContent("观察者设计模式适用于哪些场景?");
        gper.addObserver(tom);
        gper.addObserver(mic);
        gper.publishQuestion(question);
    }
}

基于Guava API 轻松落地观察者模式

先引入maven 依赖包

1
2
3
4
5
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class GuavaEvent {

    @Subscribe
    public void subscribe(String str){
        System.out.println("执行subscribe方法,传入的参数是:" + str);
    }
}

public class GuavaEventTest {

    public static void main(String[] args) {
        //消息总线
        EventBus eventBus = new EventBus();
        GuavaEvent guavaEvent = new GuavaEvent();
        eventBus.register(guavaEvent);
        eventBus.post("Tom");

        //从Struts到SpringMVC的升级
        //因为Struts面向的类,而SpringMVC面向的是方法

        //前面两者面向的是类,Guava面向是方法

        //能够轻松落地观察模式的一种解决方案
        //MQ
    }
}

观察者模式的优缺点 优点: 1、观察者和被观察者之间建立了一个抽象的耦合。 2、观察者模式支持广播通信。 缺点: 1、观察者之间有过多的细节依赖、提高时间消耗及程序的复杂度。 2、使用要得当,要避免循环调用。

Spring涉及模式

工厂模式: BeanFactory

装饰器:BeanWrapper

AopProxy

ApplicationContext

DispatcherServlet

Spring编程思想

AOP:面向切面编程

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

Aspect:切面

面向切面编程,也可以说是面向规则编程;

事务代理:加载service层的增、删、改

AOP相关概念:

1、Aspect(切面):通常是一个类,里面可以定义切入点和通知。 2、JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用。 3、Advice(通知):AOP 在特定的切入点上执行的增强处理,有before、after、 afterReturning、afterThrowing、around

4、Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式 AOP 框架创建的对象,实际就是使用代理对目标对象功能增强。Spring 中的AOP 代理 可以使JDK 动态代理,也可以是CGLIB 代理,前者基于接口,后者基于子类。

代码实现(基于注解)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/**
 * Annotation版Aspect切面Bean
 * @author
 */
//声明这是一个组件
@Component
//声明这是一个切面Bean
@Aspect
@Slf4j
public class AnnotaionAspect {
	
	//配置切入点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点
	@Pointcut("execution(* com.spring.demo.springpatterndemo.aop.service..*(..))")
	public void aspect(){

	}
	/*
	 * 配置前置通知,使用在方法aspect()上注册的切入点
	 * 同时接受JoinPoint切入点对象,可以没有该参数
	 */
	@Before("aspect()")
	public void before(JoinPoint joinPoint){

		log.info("before通知 " + joinPoint);
	}
	
	//配置后置通知,使用在方法aspect()上注册的切入点
	@After("aspect()")
	public void after(JoinPoint joinPoint){

		log.info("after通知  " + joinPoint);
	}
	
	//配置环绕通知,使用在方法aspect()上注册的切入点
	@Around("aspect()")
	public void around(JoinPoint joinPoint){
		long start = System.currentTimeMillis();
		try {
			((ProceedingJoinPoint) joinPoint).proceed();
			long end = System.currentTimeMillis();
			log.info("around通知 " + joinPoint + "\tUse time : " + (end - start) + " ms!");
		} catch (Throwable e) {
			long end = System.currentTimeMillis();
			log.info("around通知 " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());
		}
	}
	//配置后置返回通知,使用在方法aspect()上注册的切入点
	@AfterReturning("aspect()")
	public void afterReturn(JoinPoint joinPoint){
		log.info("afterReturn通知 " + joinPoint);
	}
	
	//配置抛出异常后通知,使用在方法aspect()上注册的切入点
	@AfterThrowing(pointcut="aspect()", throwing="ex")
	public void afterThrow(JoinPoint joinPoint, Exception ex){
		log.info("afterThrow通知 " + joinPoint + "\t" + ex.getMessage());
	}
}

OOP:面向对象编程

用代码去描述世界

BOP:面向Bean编程

Bean与Bean之间的关系,不希望人为的重复管理,由程序来实现自动管理

IOC:控制反转

创建对象的控制权反转,也就是 new对象;

在创建对象时,是谁使用谁new,有了Spring以后,所有的Bean都由Sping来new,所有才叫做控制反转。转交控制权。

new出来的对象需要统一管理起来所有才有了IOC容器(也就是个Map)

DI:依赖注入

解决对象动态赋值的问题,动态调用getter、setter(采用反射)

Sping的加载步骤:定位、载入、注册;在确定要不要初始化Spring