IoC
什么是IoC?
IoC是Inversion of Control(控制反轉)的簡稱,注意它是一個技術思想。描述的是對象創建、管理的事情。
-
傳統開發方式:比如類A依賴類B,往往會在類A里面new一個B的對象。
-
IoC開發方式:我們不用去new對象,由IoC容器幫我們實例化對象並進行管理。我們需要B對象,就問IoC容器要即可。
控制反轉就是說將對象創建、管理的權力交給了外部環境(IoC容器)。
IoC的作用:解決了對象之間的耦合問題。
什么是DI?
DI是Dependancy Injection(依賴注入)的簡稱,指容器會把對象依賴的其他對象注入。比如A對象里聲明了一個B的屬性,那么就需要容器把B對象注入給A。
什么是AOP?
AOP是 Aspect oriented Programming(⾯向切⾯編程)的簡稱。
在上面的代碼中,多個方法都出現了相同的代碼(可以稱之為橫切邏輯代碼)。這部分代碼不僅重復,而且跟業務邏輯沒有關系但是混雜在一起。這時AOP出現了,它提供了橫向抽取機制,將這部分橫切代碼和業務邏輯代碼分開。
AOP的作用:在不改變原有業務邏輯的情況下,增強橫切邏輯代碼,解耦合。
手寫IOC
首先我們看一下在沒有Spring之前,我們是怎么開發一個web程序的呢?
那么針對上面的兩個問題,我們如何進行解決呢?
- 我們除了用new實例化對象外,還可以用反射的技術。
- 另外項目中往往有很多對象需要實例化,那么可以使用工廠模式來進行優化。
綜上,我們可以用工廠模式+反射技術把對象都實例化好,放在一個map里面,如果需要某個對象,就可以直接從這個map里面取。
除此之外,我們還需要一個xml文件,里面來定義對象的全類名(反射需要),如果有依賴,還需要定義類與類之間的依賴關系。
<beans>
<bean id="accountDao" class="com.mmc.ioc.dao.impl.AccountDaoImpl"></bean>
<bean id="transferService" class="com.mmc.ioc.service.impl.TransferServiceImpl">
<!--這里的name默認為set+name就是方法名-->
<property name="AccountDao" ref="accountDao"></property>
</bean>
</beans>
核心代碼:
public class BeanFactory {
private static Map<String,Object> beanMap=new HashMap<>();
static {
InputStream inputStream=BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
SAXReader saxReader=new SAXReader();
try {
Document document = saxReader.read(inputStream);
Element rootElement = document.getRootElement();
List<Element> beans = rootElement.selectNodes("//bean");
for (Element element:beans){
String id = element.attributeValue("id");
String clazz = element.attributeValue("class");
Object instance = Class.forName(clazz).newInstance();
beanMap.put(id,instance);
}
//實例完后填充對象的依賴
List<Element> propertys = rootElement.selectNodes("//property");
for (Element element:propertys){
String name = element.attributeValue("name");
String ref = element.attributeValue("ref");
Element parent = element.getParent();
String parentId = parent.attributeValue("id");
Object instance = beanMap.get(parentId);
Object refInstance = beanMap.get(ref);
Method setMethod = instance.getClass().getDeclaredMethod("set" + name,refInstance.getClass().getInterfaces());
setMethod.invoke(instance,beanMap.get(ref));
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static Object getBean(String name){
return beanMap.get(name);
}
}
那么接下來我們想要使用對象的時候,就不用new了,而是從beanFactory里面去拿。
這樣一個簡易的AOP就完成了。
手寫AOP實現
我們解決了上面的問題1,那么問題2事務控制如何解決呢?
分析:數據庫事務歸根結底是Connection的事務,connection.commit()提交事務,connection.rollback()回滾事務。
- 我們是想保證service里的方法里面執行的眾多數據庫操作要么都成功,要么都失敗。
- 同一個service方法里面的dao層必須要使用的是同一個connection,也就是說同一個線程內要是同一個connection,所以可以使用ThreadLocal實現
改造完成:
@Override
public void transfer(String fromCardNo, String toCardNo, int money) throws Exception {
//關閉自動提交
connectionUtils.getThreadConn().setAutoCommit(false);
try {
Account from = accountDao.queryAccountByCardNo(fromCardNo);
Account to = accountDao.queryAccountByCardNo(toCardNo);
from.setMoney(from.getMoney()-money);
to.setMoney(to.getMoney()+money);
accountDao.updateAccountByCardNo(to);
int i=10/0;
accountDao.updateAccountByCardNo(from);
//提交事務
connectionUtils.getThreadConn().commit();
} catch (Exception e) {
//回滾事務
connectionUtils.getThreadConn().rollback();
throw e;
}
}
在兩次update語句中間手動加了個異常,可以發現數據庫兩條數據都沒變,說明事務控制成功。
但是如果多個方法都需要加事務控制的話,我們需要給多個方法加上下面這一套重復的代碼
connectionUtils.getThreadConn().setAutoCommit(false);
try {
//省略部分代碼
// -----
//提交事務
connectionUtils.getThreadConn().commit();
} catch (Exception e) {
//回滾事務
connectionUtils.getThreadConn().rollback();
throw e;
}
怎么解決呢?
我們可以通過代理模式給每個方法代理
代碼如下:
public class ProxyFactory {
private ConnectionUtils connectionUtils;
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
public Object getJdkProxy(Object object){
return Proxy.newProxyInstance(ProxyFactory.class.getClassLoader(), object.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//關閉自動提交
connectionUtils.getThreadConn().setAutoCommit(false);
Object result;
try {
result= method.invoke(object,args);
//提交事務
connectionUtils.getThreadConn().commit();
} catch (Exception e) {
//回滾事務
connectionUtils.getThreadConn().rollback();
throw e;
}
return result;
}
});
}
}
每個需要加事務的對象,只要調用getJdkProxy方法獲取到代理對象,再使用代理對象執行方法,就能實現事務控制了。
使用方法如下:
private ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory");
private TransferService transferService= (TransferService) proxyFactory.getJdkProxy(BeanFactory.getBean("transferService"));
這樣就相當於把橫切邏輯代碼提取出來了,如果把這套機制抽出來就是AOP的實現了。