Spring的核心功能是IOC容器以及AOP面向切面編程,同樣也是很多Web后端工程師每天都要打交道的框架,
1.簡單工廠模式
考慮這樣一個場景:當(dāng)A對象需要調(diào)用B對象的方法時,需要在A中new一個B的實例,把這種方式叫作硬編碼耦合,它的缺點是一旦需求發(fā)生變化,比如需要使用C類來代替B時,就要改寫A類的方法。假如應(yīng)用中有1000個類以硬編碼的方式耦合了B,那改起來就費勁了。于是簡單工廠模式就登場了,簡單工廠模式又叫靜態(tài)工廠方法,其實質(zhì)是由一個工廠類根據(jù)傳入的參數(shù),動態(tài)決定應(yīng)該創(chuàng)建哪一個產(chǎn)品類。
Spring中的BeanFactory就是簡單工廠模式的體現(xiàn),BeanFactory是Spring IOC容器中的一個核心接口,它的定義如下:
public interface BeanFactory {
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType);
Object getBean(String name, Object... args);
<T> T getBean(Class<T> requiredType);
<T> T getBean(Class<T> requiredType, Object... args);
boolean containsBean(String name);
boolean isSingleton(String name);
boolea isPrototype(String name);
boolean isTypeMatch(String name, ResolvableType typeToMatch);
boolean isTypeMatch(String name, Class<?> typeToMatch);
Class<?> getType(String name);
String[] getAliases(String name);
}
可以通過它的具體實現(xiàn)類(比如ClassPathXmlApplicationContext)來獲取Bean:
BeanFactory bf = new ClassPathXmlApplicationContext("spring.xml");
User userBean = (User) bf.getBean("userBean");
從上面代碼可以看到,使用者不需要自己來new對象,而是通過工廠類的方法getBean來獲取對象實例,這是典型的簡單工廠模式,只不過Spring是用反射機制來創(chuàng)建Bean的。
2.工廠方法模式
工廠方法模式說白了其實就是簡單工廠模式的一種升級或者說是進(jìn)一步抽象,它可以應(yīng)用于更加復(fù)雜的場景,靈活性也更高。在簡單工廠中,由工廠類進(jìn)行所有的邏輯判斷、實例創(chuàng)建;如果不想在工廠類中進(jìn)行判斷,可以為不同的產(chǎn)品提供不同的工廠,不同的工廠生產(chǎn)不同的產(chǎn)品,每一個工廠都只對應(yīng)一個相應(yīng)的對象,這就是工廠方法模式。
Spring中的FactoryBean就是這種思想的體現(xiàn),F(xiàn)actoryBean可以理解為工廠Bean,先來看看它的定義:
public interface FactoryBean<T> {
T getObject();
Class<?> getObjectType();
boolean isSingleton();
}
定義一個類UserFactoryBean來實現(xiàn)FactoryBean接口,主要是在getObject方法里new一個User對象。這樣通過getBean(id) 獲得的是該工廠所產(chǎn)生的User的實例,而不是UserFactoryBean本身的實例,像下面這樣:
BeanFactory bf = new ClassPathXmlApplicationContext("user.xml");
User userBean = (User) bf.getBean("userFactoryBean");
3.單例模式
單例模式是指一個類在整個系統(tǒng)運行過程中,只允許產(chǎn)生一個實例。在Spring中,Bean可以被定義為兩種模式:Prototype(多例)和Singleton(單例),Spring Bean默認(rèn)是單例模式。那Spring是如何實現(xiàn)單例模式的呢?答案是通過單例注冊表的方式,具體來說就是使用了HashMap。對代碼進(jìn)行了簡化:
public class DefaultSingletonBeanRegistry {
//使用了線程安全容器ConcurrentHashMap,保存各種單實例對象
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>;
protected Object getSingleton(String beanName) {
//先到HashMap中拿Object
Object singletonObject = singletonObjects.get(beanName);
//如果沒拿到通過反射創(chuàng)建一個對象實例,并添加到HashMap中
if (singletonObject == null) {
singletonObjects.put(beanName,
Class.forName(beanName).newInstance());
}
//返回對象實例
return singletonObjects.get(beanName);
}
}
上面的代碼邏輯比較清晰,先到HashMap去拿單實例對象,沒拿到就創(chuàng)建一個添加到HashMap。
4.代理模式
所謂代理,是指它與被代理對象實現(xiàn)了相同的接口,客戶端必須通過代理才能與被代理的目標(biāo)類進(jìn)行交互,而代理一般在交互的過程中(交互前后),進(jìn)行某些特定的處理,比如在調(diào)用這個方法前做前置處理,調(diào)用這個方法后做后置處理。代理模式中有下面幾種角色:
- 抽象接口:定義目標(biāo)類及代理類的共同接口,這樣在任何可以使用目標(biāo)對象的地方都可以使用代理對象。
- 目標(biāo)對象: 定義了代理對象所代表的目標(biāo)對象,專注于業(yè)務(wù)功能的實現(xiàn)。
- 代理對象: 代理對象內(nèi)部含有目標(biāo)對象的引用,收到客戶端的調(diào)用請求時,代理對象通常不會直接調(diào)用目標(biāo)對象的方法,而是在調(diào)用之前和之后實現(xiàn)一些額外的邏輯。
代理模式的好處是,可以在目標(biāo)對象業(yè)務(wù)功能的基礎(chǔ)上添加一些公共的邏輯,比如我們想給目標(biāo)對象加入日志、權(quán)限管理和事務(wù)控制等功能,就可以使用代理類來完成,而沒必要修改目標(biāo)類,從而使得目標(biāo)類保持穩(wěn)定。這其實是開閉原則的體現(xiàn),不要隨意去修改別人已經(jīng)寫好的代碼或者方法。
代理又分為靜態(tài)代理和動態(tài)代理兩種方式。靜態(tài)代理需要定義接口,被代理對象(目標(biāo)對象)與代理對象(Proxy)一起實現(xiàn)相同的接口,我們通過一個例子來理解一下:
//抽象接口
public interface IStudentDao {
void save();
}
//目標(biāo)對象
public class StudentDao implements IStudentDao {
public void save() {
System.out.println("保存成功");
}
}
//代理對象
public class StudentDaoProxy implements IStudentDao{
//持有目標(biāo)對象的引用
private IStudentDao target;
public StudentDaoProxy(IStudentDao target){
this.target = target;
}
//在目標(biāo)功能對象方法的前后加入事務(wù)控制
public void save() {
System.out.println("開始事務(wù)");
target.save();//執(zhí)行目標(biāo)對象的方法
System.out.println("提交事務(wù)");
}
}
public static void main(String[] args) {
//創(chuàng)建目標(biāo)對象
StudentDao target = new StudentDao();
//創(chuàng)建代理對象,把目標(biāo)對象傳給代理對象,建立代理關(guān)系
StudentDaoProxy proxy = new StudentDaoProxy(target);
//執(zhí)行的是代理的方法
proxy.save();
}
而Spring的AOP采用的是動態(tài)代理的方式,而動態(tài)代理就是指代理類在程序運行時由JVM動態(tài)創(chuàng)建。在上面靜態(tài)代理的例子中,代理類(StudentDaoProxy)是我們自己定義好的,在程序運行之前就已經(jīng)編譯完成。而動態(tài)代理,代理類并不是在Java代碼中定義的,而是在運行時根據(jù)我們在Java代碼中的“指示”動態(tài)生成的。那我們怎么“指示”JDK去動態(tài)地生成代理類呢?
在Java的java.lang.reflect包里提供了一個Proxy類和一個InvocationHandler接口,通過這個類和這個接口可以生成動態(tài)代理對象。具體來說有如下步驟:
- step1.定義一個InvocationHandler類,將需要擴展的邏輯集中放到這個類中,比如下面的例子模擬了添加事務(wù)控制的邏輯。
public class MyInvocationHandler implements InvocationHandler {
private Object obj;
public MyInvocationHandler(Object obj){
this.obj=obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("開始事務(wù)");
Object result = method.invoke(obj, args);
System.out.println("開始事務(wù)");
return result;
}
}
- step2.使用Proxy的newProxyInstance方法動態(tài)的創(chuàng)建代理對象:
public static void main(String[] args) {
//創(chuàng)建目標(biāo)對象StudentDao
IStudentDao stuDAO = new StudentDao();
//創(chuàng)建MyInvocationHandler對象
InvocationHandler handler = new MyInvocationHandler(stuDAO);
//使用Proxy.newProxyInstance動態(tài)的創(chuàng)建代理對象stuProxy
IStudentDao stuProxy = (IStudentDao)
Proxy.newProxyInstance(stuDAO.getClass().getClassLoader(), stuDAO.getClass().getInterfaces(), handler);
//動用代理對象的方法
stuProxy.save();
}
上面的代碼實現(xiàn)和靜態(tài)代理一樣的功能,相比于靜態(tài)代理,動態(tài)代理的優(yōu)勢在于可以很方便地對代理類的函數(shù)進(jìn)行統(tǒng)一的處理,而不用修改每個代理類中的方法。
Spring實現(xiàn)了通過動態(tài)代理對類進(jìn)行方法級別的切面增強,解釋一下這句話,其實就是動態(tài)生成目標(biāo)對象的代理類,并在代理類的方法中設(shè)置攔截器,通過執(zhí)行攔截器中的邏輯增強了代理方法的功能,從而實現(xiàn)AOP。