閉關修煉180天--手寫IOC和AOP(xml篇)

閉關修煉180天--手寫IOC和AOP(xml篇)

帝莘java

 

首先先分享一波思惟導圖,涵蓋了一些Spring的知識點,固然這裏並不全面,後期我會持續更新知識點。mysql

在手寫實現IOC和AOP以前(也就是打造一個簡單的Spring框架),先簡單的瞭解一些Spring以及它的兩個核心思想IOC和AOP的相關概念。spring

Spring:

  概述:spring是分層的全棧輕量級開源框架,以ioc和AOP爲內核,提供了展示層spring mvc和業務層管理等衆多的企業應用技術,還能整合衆多開源的第三方框架。sql

  優點:1.方便解耦,簡化開發 2.AOP編程的支持 3.聲明式事務的支持。4.方便程序的測試。5.方便集成各類優秀框架 6.下降javaEE API的使用難度。6.源碼是經典的java學習案例。數據庫

  Spring 的核⼼結構:Spring是⼀個分層⾮常清晰而且依賴關係、職責定位⾮常明確的輕量級框架,主要包括⼏個⼤模塊:數據處理模塊、Web模塊、AOP(Aspect Oriented Programming)/Aspects模塊、編程

Core Container模塊和 Test 模塊,以下圖所示,Spring依靠這些基本模塊,實現了⼀個令⼈愉悅的融合了現有解決⽅案的零侵⼊的輕量級框架。mybatis

 

IOC: 

  IOC概念:控制反轉,它是一個技術思想,不是一個技術實現,描述的事情是:java開發領域對象的建立,管理的問題。mvc

  傳統開發方式:好比A依賴於B,每每會在A中new一個B的對象。框架

  IOC思想下的開發方式:咱們不用本身去new對象了,而是由IOC容器(Spring框架)去幫助咱們實例化對象而且管理它,咱們須要使用哪一個對象,去問IOC容器要便可。咱們喪失了一個權力(建立、管理對dom

象的權力),獲得了一個福利(不用考慮對象的建立、管理一系列事情)

  爲何叫控制反轉?控制:指的是對象建立(實例化、管理)的權力;反轉:控制權交給了外部環境(spring框架)

  IOC解決了什麼問題:IOC解決了對象之間的耦合問題

  IOC和DI的區別?IOC是控制反轉,DI是依賴注入

  怎麼理解:IOC和DI描述的是一件事情(對象實例化及依賴關係維護這件事),不過角度不一樣。

  IOC是站在對象的角度,對象實例化及其管理的權力交給了(反轉)容器。

  DI是站在容器的角度,容器會把對象依賴的其餘對象注入(送過去),好比A對象實例化過程當中由於聲明瞭一個B類型的屬性,那麼就須要容器把B對象注入給A。

 

手寫Spring框架流程:

實現思路:

  1. 編寫xml文件,在根標籤beans下條件bean,每一組bean對應一個須要加入容器管理的對象。
  2. 編寫BeanFactory:解析xml文件,將解析出的一個個bean裝載進map集合中,其中id爲bean標籤內id屬性的值,value爲根據bean標籤內class屬性的全路徑值反射獲得的目標類的實例對象。
  3. BeanFactory還要提供一個獲取實例的方法getBean(String id)
  4. 爲了添加不一樣類的依賴關係,在xml文件裏面的bean標籤組內添加property標籤,其中property內的name屬性的值是父標籤所表明的類的set方法後面的名字,ref屬性的值是它所依賴的對象的bean-id。在BeanFactory中進一步解析,把對應依賴關係添加進對象中,更新map。
  5. 配置事務,保證一個線程使用一個數據庫鏈接,根據代理模式生產代理對象,根據代理對象回調用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帝莘,期待與你的技術交流和思想碰撞。

相關文章
相關標籤/搜索