单例模式
spring中bean有个默认作用域,是singleton(单例)的,他是通过单例注册表的特殊方式实现单例模式的。
// 通过 ConcurrentHashMap(线程安全) 实现单例注册表
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "'beanName' must not be null");
synchronized (this.singletonObjects) {
// 检查缓存中是否存在实例
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
//...省略了很多代码
try {
singletonObject = singletonFactory.getObject();
}
//...省略了很多代码
// 如果实例对象在不存在,我们注册到单例注册表中。
addSingleton(beanName, singletonObject);
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
}
//将对象添加到单例注册表
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
}
}
}
懒汉式
public class LazyMan {
private LazyMan(){
System.out.println("1");
}
private static LazyMan lazyMan;
public static LazyMan getInstance(){
//简单的懒汉式 如果不为null 才加载
if (lazyMan==null){
lazyMan = new LazyMan();
}
return lazyMan;
}
}
特点:首先肯定是懒加载,饿汉式的问题就是不能懒加载,会产生垃圾对象。
但是懒汉式这样是多线程不安全的,实现也比较简单。
饿汉式
public class HungryMan {
private HungryMan(){
//上来先new一个私有对象
}
//随着类加载就直接加载了 所以称为饿汉模式
private final static HungryMan HUNGRY_MAN=new HungryMan();
public static HungryMan getInstance(){
return HUNGRY_MAN;
}
public static void main(String[] args) {
System.out.println(HungryMan.getInstance());
}
}
饿汉式的问题就是没有懒加载,是比较常用的,就是容易产生垃圾对象,优点是没有加锁且线程安全,缺点就是类加载时就初始化,浪费内存。
它基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。
懒汉式线程安全
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
直接用synchronized关键字保证多线程下的单例,但是这种直接加锁的方式会导致性能差太多,毕竟99%的情况下,都不会需要加锁的。
双检锁DCL
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
采用volatile和synchronized实现双锁机制,因为不像上面的那种直接加锁,所以既线程安全又能保持高性能,就是实现稍微复杂了点,还是推荐使用的,但是反射是可以破解的,单纯的这样写反射是完全可以直接拿到多个对象的,可以加一个静态对象标志位,来进行判断,但是标志位也可能被修改为true,还是不能锁死反射。
内部类实现
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
枚举
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
枚举是实现单例的最佳方法,简单且可以绝对防止多次实例化,枚举是1.5之后添加的,它本身也是一个类,每个枚举都是Enum的子类,且无序继承,可以有多个枚举项,枚举的构造器都是private,不能让外界创建类,可以有抽象方法。
工厂模式
工厂模式属于创建型模式,提供了创建对象的最佳方式。
意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
主要解决:主要解决接口选择的问题。
何时使用:我们明确地计划不同条件下创建不同实例时。
如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。
关键代码:创建过程在其子类执行。
策略模式
运行时更改类的行为或算法,从而达到修改其功能的目的;
使用场景: 一个系统需要动态地在几种算法中选择一种,而这些算法之间仅仅是他们的行为不同。 此外决策过程中过多的出现if else,也可以考虑使用该模式。
实现:将这些算法封装成可单独运行的类,由使用者根据需要进行替换。
优点: 较为灵活,扩展性好,避免大量的if else结构。
缺点: 对外暴露了类所有的行为和算法,行为过多导致策略类膨胀。
策略模式的核心是将一系列的操作拆分成可若干可单独重复使用的轮子,特定条件下直接选取其中一个使用,而不是传递条件,使用if else来进行条件判断以执行相应的操作。
代理模式
静态代理
动态代理
Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候Spring AOP会使用Cglib ,这时候Spring AOP会使用 Cglib 生成一个被代理对象的子类来作为代理,使用 AOP 之后我们可以把一些通用功能抽象出来,在需要用到的地方直接使用即可,这样大大简化了代码量。我们需要增加新功能时也方便,这样也提高了系统扩展性。日志功能、事务管理等等场景都用到了 AOP 。