单例模式

保证一个类仅有一个实例,并且提供一个访问的全局访问点

饿汉模式, 利用jvm的类加载机制提供了线程安全的保证,在类加载的时候就已经完成了对象的初始化。不能保证有其他方式导致类装载,从而没有实现懒加载。

1
2
3
4
5
6
7
8
9
public class Singleton{
private static Singleton instance = new Singleton();
private Singleton(){

}
public static Singleton getInstance(){
return instance;
}
}

懒汉模式(线程不安全)

1
2
3
4
5
6
7
8
9
10
public class Singleton{
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance==null){
instance = new instance();
}
return instance;
}
}

懒汉模式(线程安全),每次获取实例都要加锁,造成大量不必要开销

1
2
3
4
5
6
7
8
9
10
public class Singleton{
private static Singleton instance;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(instance==null){
instance = new instance();
}
return instance;
}
}

双重检查模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Singleton{
private volatile static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance==null){
synchronized(Singleton.class){
if(instance==null){
instance = new instance();
}
}
}
return instance;
}
}

静态内部单例模式,第一次加载Singleton类时并不会初始化instance,只有第一次调用getInstance方法时虚拟机加载SingletonHolder并初始化instance ,这样不仅能确保线程安全也能保证Singleton类的唯一性,所以推荐使用静态内部类单例模式。

1
2
3
4
5
6
7
8
9
public class Singleton{
private Singleton(){}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
private static class SingletonHolder{
private static final Singleton instance = new Singleton();
}
}

代理模式

在代理模式中,一个类代表另一个类的功能,为其他对象提供一个代理以控制对某个对象的访问,即通过代理对象访问目标对象。Spring AOP通过JDK提供的Proxy类或者通过字节码增强来实现。

  • 静态代理

    对目标对象进行封装,并拦截方法调用。静态代理的实现为每个实际对象都产生一个代理类,并将其作为内部属性。这会导致类数量的急速增加,并且代理类的代码存在大量重复。这就需要动态代理来实现。

  • 动态代理

    • JDK动态代理实现

      DK提供的动态代理无需为每个对象都实现一个代理类,通过处理器,可以对不同的对象生成代理类。JDK的动态代理是通过代理接口实现的,如果对象没有实现接口,那就无能为力了。因为产生的代理类本身继承自Proxy,受限于java的单继承。

    • CGLIB动态代理

      CGLIB是通过动态生成目标类的子类,在子类中设置拦截逻辑,来进行动态代理。因此目标类的方法不能被final修饰。

代理模式与装饰者模式

从UML上看装饰者模式和代理模式很像,装饰者和代理者都对目标方法进行修改,但是二者还是有很大的区别。

  • 装饰者模式关注于对目标方法进行增强。装饰者只能在外加一层装饰。
  • 代理模式关注于对目标方法的控制。代理者拥有控制权限,可以决定目标方法是否调用。