閉關修煉180天--手寫IOC和AOP(xml篇)
帝莘
首先先分享一波思維導圖,涵蓋了一些Spring的知識點,當然這裡並不全面,後期我會持續更新知識點。
在手寫實現IOC和AOP之前(也就是打造一個簡單的Spring框架),先簡單的瞭解一些Spring以及它的兩個核心思想IOC和AOP的相關概念。
Spring:
概述:spring是分層的全棧輕量級開源框架,以ioc和AOP為核心,提供了展現層spring mvc和業務層管理等眾多的企業應用技術,還能整合眾多開源的第三方框架。
優勢:1.方便解耦,簡化開發 2.AOP程式設計的支援 3.宣告式事務的支援。4.方便程式的測試。5.方便整合各種優秀框架 6.降低javaEE API的使用難度。6.原始碼是經典的java學習案例。
Spring 的核⼼結構:Spring是⼀個分層⾮常清晰並且依賴關係、職責定位⾮常明確的輕量級框架,主要包括⼏個⼤模組:資料處理模組、Web模組、AOP(Aspect Oriented Programming)/Aspects模組、
Core Container模組和 Test 模組,如下圖所示,Spring依靠這些基本模組,實現了⼀個令⼈愉悅的融合了現有解決⽅案的零侵⼊的輕量級框架。
IOC:
IOC概念:控制反轉,它是一個技術思想,不是一個技術實現,描述的事情是:java開發領域物件的建立,管理的問題。
傳統開發方式:比如A依賴於B,往往會在A中new一個B的物件。
IOC思想下的開發方式:我們不用自己去new物件了,而是由IOC容器(Spring框架)去幫助我們例項化物件並且管理它,我們需要使用哪個物件,去問IOC容器要即可。我們喪失了一個權力(建立、管理對
象的權力),得到了一個福利(不用考慮物件的建立、管理一系列事情)
為什麼叫控制反轉?控制:指的是物件建立(例項化、管理)的權力;反轉:控制權交給了外部環境(spring框架)
IOC解決了什麼問題:IOC解決了物件之間的耦合問題
IOC和DI的區別?IOC是控制反轉,DI是依賴注入。
怎麼理解:IOC和DI描述的是一件事情(物件例項化及依賴關係維護這件事),不過角度不同。
IOC是站在物件的角度,物件例項化及其管理的權力交給了(反轉)容器。
DI是站在容器的角度,容器會把物件依賴的其他物件注入(送過去),比如A物件例項化過程中因為宣告瞭一個B型別的屬性,那麼就需要容器把B物件注入給A。
手寫Spring框架流程:
實現思路:
- 編寫xml檔案,在根標籤beans下條件bean,每一組bean對應一個需要加入容器管理的物件。
- 編寫BeanFactory:解析xml檔案,將解析出的一個個bean裝載進map集合中,其中id為bean標籤內id屬性的值,value為根據bean標籤內class屬性的全路徑值反射得到的目標類的例項物件。
- BeanFactory還要提供一個獲取例項的方法getBean(String id)
- 為了新增不同類的依賴關係,在xml檔案裡面的bean標籤組內新增property標籤,其中property內的name屬性的值是父標籤所代表的類的set方法後面的名字,ref屬性的值是它所依賴的物件的bean-id。在BeanFactory中進一步解析,把對應依賴關係新增進物件中,更新map。
- 配置事務,保證一個執行緒使用一個資料庫連線,根據代理模式生產代理物件,根據代理物件回撥用invoke方法的特性,在原方法前後增加事務增強,在這裡,具體的事務管理我們交給TransactionManager類進行具體的管理,配置好代理類和事務管理類之間的依賴關係。
程式碼實現:
先看一下整體的專案結構:
sql建表語句:
SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for users -- ---------------------------- DROP TABLE IF EXISTS `users`; CREATE TABLE `users` ( `id` int(11) NOT NULL, `username` varchar(255) COLLATE utf8_bin DEFAULT NULL, `balance` double(11,0) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; -- ---------------------------- -- Records of users -- ---------------------------- INSERT INTO `users` VALUES ('1', '汪蘇瀧', '1000'); INSERT INTO `users` VALUES ('2', '薛之謙', '1000');
幾個pom依賴
<!--dom4j--> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>jaxen</groupId> <artifactId>jaxen</artifactId> <version>1.1.6</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.17</version> </dependency> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <!--引入cglib依賴--> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.1_2</version> </dependency>
xml檔案程式碼:bean.xml
<?xml version="1.0" encoding="UTF-8" ?> <beans> <bean id="UserDao" class="com.radorm.spring.dao.impl.UserDaoImpl"> <property name="ConnectionUtils" ref="ConnectionUtils"></property> </bean> <bean id="UserService" class="com.radorm.spring.service.impl.UserServiceImpl"> <property name="UserDao" ref="UserDao"></property> </bean> <bean id="ConnectionUtils" class="com.radorm.spring.utils.ConnectionUtils"></bean> <bean id="TransactionManager" class="com.radorm.spring.utils.TransactionManager"> <property name="ConnectionUtils" ref="ConnectionUtils"></property> </bean> <bean id="ProxyFactory" class="com.radorm.spring.factory.ProxyFactory"> <property name="TransactionManager" ref="TransactionManager"></property> </bean> </beans>
三個工具類:DataSourceUtils ConnectionUtils ,TransactionManager
import com.mchange.v2.c3p0.ComboPooledDataSource; import javax.sql.DataSource; import java.beans.PropertyVetoException; import java.sql.Connection; import java.sql.SQLException; public class DataSourceUtils { private DataSourceUtils(){} private static DataSourceUtils dataSourceUtils = new DataSourceUtils(); public static DataSourceUtils getInstance(){ return dataSourceUtils; } public DataSource createDataSource(){ //建立連線池 ComboPooledDataSource dataSource = new ComboPooledDataSource(); try { dataSource.setDriverClass("com.mysql.jdbc.Driver"); dataSource.setJdbcUrl("jdbc:mysql:///mybatis"); dataSource.setUser("root"); dataSource.setPassword("root"); } catch (PropertyVetoException e) { e.printStackTrace(); } return dataSource; } public Connection getConnection(){ DataSource dataSource = createDataSource(); try { return dataSource.getConnection(); } catch (SQLException e) { e.printStackTrace(); } return null; } }
import java.sql.Connection; public class ConnectionUtils { /** * 宣告當前執行緒 */ private ThreadLocal<Connection> threadLocal = new ThreadLocal<>(); /** * 獲取當前執行緒裡面的連線 * @return */ public Connection getLocalConnection(){ Connection connection = threadLocal.get(); if(connection == null){ //如果在當前執行緒內獲取不到連線,那就線上程池中獲取,並且放到當前執行緒中 connection = DataSourceUtils.getInstance().getConnection(); threadLocal.set(connection); } return connection; } }
import java.sql.SQLException; /** * 事務管理工廠 */ public class TransactionManager { private ConnectionUtils connectionUtils; public void setConnectionUtils(ConnectionUtils connectionUtils) { this.connectionUtils = connectionUtils; } public void beginTransaction() throws SQLException { connectionUtils.getLocalConnection().setAutoCommit(false); } public void commitTransaction() throws SQLException{ connectionUtils.getLocalConnection().commit(); } public void rollbackTransaction(){ try { connectionUtils.getLocalConnection().rollback(); } catch (SQLException e) { e.printStackTrace(); } } }
兩個工廠類:BeanFactory,ProxyFactory
import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.List; import java.util.Map; public class BeanFactory { private static Map<String,Object> map = new HashMap<>(); /** * 1.使用dom4j技術讀取配置檔案資訊 * 2.將讀取到的物件以及key封裝進map中 * key = id,value = class反射產生的例項物件 */ static { //根據xml的路徑載入為位元組流到記憶體中 InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml"); SAXReader saxReader = new SAXReader(); try { //讀取該位元組流為document Document document = saxReader.read(inputStream); //獲取根標籤下的元素 Element rootElement = document.getRootElement(); //獲取根標籤內部所有的bean標籤裡面的內容 List<Element> beanList = rootElement.selectNodes("//bean"); for (Element element : beanList) { //根據屬性名獲取bean標籤裡面的內容 String id = element.attributeValue("id"); String aClass = element.attributeValue("class"); //利用反射技術根據全路徑獲取類物件 Class<?> aClass1 = Class.forName(aClass); //根據類物件獲取例項 Object o = aClass1.newInstance(); //組裝map map.put(id,o); } //獲取所有的property List<Element> propertyList = rootElement.selectNodes("//property"); for (Element element : propertyList) { //獲取property標籤裡面的屬性值 String name = element.attributeValue("name"); String ref = element.attributeValue("ref"); //獲取父標籤 Element parentElement = element.getParent(); //獲取父標籤的id String parentId = parentElement.attributeValue("id"); //根據父標籤的id獲取出父標籤的例項化物件 Object parentObj = map.get(parentId); //獲取父物件想要新增依賴的例項化物件 Object refObj = map.get(ref); //獲取父物件的所有方法 Method[] methods = parentObj.getClass().getMethods(); for (Method method : methods) { String methodName = method.getName(); //判斷是否是set方法 if(methodName.equalsIgnoreCase("set"+name)){ //往方法中傳入引數 method.invoke(parentObj,refObj); } } //重新將父物件加入到map中 map.put(parentId,parentObj); } } catch (DocumentException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } public static <T> T getBean(String id){ return (T) map.get(id); } }
import com.radorm.spring.utils.TransactionManager; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * JDK動態代理工廠 */ public class ProxyFactory { private TransactionManager transactionManager; public void setTransactionManager(TransactionManager transactionManager) { this.transactionManager = transactionManager; } /** * jdk動態代理:要求被代理的物件實現了介面 * @param object 委託物件 * @param <T> 代理物件 * @return */ public <T> T getProxyJDK(Object object){ Object result = Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object re = null; try { //開啟事務 transactionManager.beginTransaction(); re = method.invoke(object,args); //提交事務 transactionManager.commitTransaction(); } catch (Exception e) { e.printStackTrace(); //回滾事務 transactionManager.rollbackTransaction(); throw e; } return re; } }); return (T) result; } /** * cglib動態代理 * @param obj 委託物件 * @param <T> 代理物件 * @return */ public <T> T getProxyCglib(Object obj){ Object result = Enhancer.create(obj.getClass(), new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { Object od = null; try { //開啟事務 transactionManager.beginTransaction(); od = method.invoke(obj,objects); //提交事務 transactionManager.commitTransaction(); } catch (Exception e) { e.printStackTrace(); //回滾事務 transactionManager.rollbackTransaction(); throw e; } return od; } }); return (T) result; } }
一個pojo:Users
public class Users { private Integer id; private String username; private Double balance; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Double getBalance() { return balance; } public void setBalance(Double balance) { this.balance = balance; } public Users(Integer id, String username, Double balance) { this.id = id; this.username = username; this.balance = balance; } }
一個dao + 它的impl:UserDao + UserDaoImpl
import com.radorm.spring.pojo.Users; public interface UserDao { void draw(); Users select(Integer id); void updateUsers(Users users); }
import com.radorm.spring.dao.UserDao; import com.radorm.spring.pojo.Users; import com.radorm.spring.utils.ConnectionUtils; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class UserDaoImpl implements UserDao { private ConnectionUtils connectionUtils; public void setConnectionUtils(ConnectionUtils connectionUtils) { this.connectionUtils = connectionUtils; } @Override public void draw() { System.out.println("11111111"); } @Override public Users select(Integer id) { Connection connection = connectionUtils.getLocalConnection(); try { PreparedStatement preparedStatement = connection.prepareStatement("select * from users where id = ?"); preparedStatement.setObject(1,id); ResultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()){ int id1 = resultSet.getInt("id"); String username = resultSet.getString("username"); double balance = resultSet.getDouble("balance"); Users users = new Users(id1,username,balance); return users; } } catch (SQLException e) { e.printStackTrace(); } return null; } @Override public void updateUsers(Users users) { Connection localConnection = connectionUtils.getLocalConnection(); try { PreparedStatement preparedStatement = localConnection.prepareStatement("update users set username = ?,balance = ? where id = ?"); preparedStatement.setString(1,users.getUsername()); preparedStatement.setDouble(2,users.getBalance()); preparedStatement.setInt(3,users.getId()); preparedStatement.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } } }
一個service+它的impl:UserService,UserServiceImpl
public interface UserService { void transfer(Integer fromId,Integer toId,Double money) throws Exception; }
import com.radorm.spring.dao.UserDao; import com.radorm.spring.pojo.Users; import com.radorm.spring.service.UserService; public class UserServiceImpl implements UserService { private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } @Override public void transfer(Integer fromId, Integer toId, Double money) throws Exception { Users from = userDao.select(fromId); Users to = userDao.select(toId); from.setBalance(from.getBalance() - money); to.setBalance(to.getBalance() + money); userDao.updateUsers(from); //該異常程式碼可以選擇開啟或者關閉,檢驗事務的作用 //int i = 1/0 userDao.updateUsers(to); } }
test:BeanTest
import com.radorm.spring.factory.ProxyFactory; import com.radorm.spring.service.UserService; import com.radorm.spring.factory.BeanFactory; import org.junit.Test; public class BeanTest { @Test public void test_bean(){ ProxyFactory proxyFactory = BeanFactory.getBean("ProxyFactory"); UserService userService = proxyFactory.getProxyJDK(BeanFactory.getBean("UserService")); try { userService.transfer(1,2,100d); System.out.println("轉賬成功"); } catch (Exception e) { System.out.println("轉賬失敗"); } } }
到此程式碼完成!
我是Slience帝莘,期待與你的技術交流和思想碰撞。