1. 程式人生 > >jdk 動態代理模擬mybatis的快取機制

jdk 動態代理模擬mybatis的快取機制

在學習jdk的動態代理的時候我們需要記住一個類java.lang.reflect.Proxy和一個介面java.lang.reflect.InvocationHandler。InvocationHandler將jdk對類的處理以方法引數的方式暴露給我們,這個方法是

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {},以下是三個引數的說明

proxy:代理物件

method:被代理物件的方法,即被實際訪問的方法

args:被實際訪問的方法的引數

這裡的關鍵就是method和args,我們可以對args和method進行處理,比如判斷方法的引數是否是和上次呼叫時的一模一樣,如果引數一樣,方法也一樣,那麼我們就可以從快取中去獲取結果,比如mybatis的快取就是這樣的,一下是模擬的整個過程!

專案結構

User.java

package com.ss.entity;
public class User {
    private Long userId;
    private String userName;
    private String nickName;
    public Long getUserId() {
        return userId;
    }
    public void setUserId(Long userId) {
        this.userId = userId;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getNickName() {
        return nickName;
    }
    public void setNickName(String nickName) {
        this.nickName = nickName;
    }
    @Override
    public String toString() {
        return "User [userId=" + userId + ", userName=" + userName + ", nickName=" + nickName + "]";
    }
    public User(){
        
    }
    public User(Long userId, String userName, String nickName) {
        this.userId = userId;
        this.userName = userName;
        this.nickName = nickName;
    }
}


UserDao.java

package com.ss.dao;
import com.ss.entity.User;
public interface UserDao {
    Integer createUser(User user);
    Integer deleteUser(User user);
    Integer updateUser(User user);
    User retrieveUser(Long userId);
}

UserDaoImpl.java

package com.ss.dao;
import java.lang.reflect.Proxy;
import com.ss.db.Cache;
import com.ss.db.DBProcess;
import com.ss.db.MySQL;
import com.ss.entity.User;
public class UserDaoImpl implements UserDao {
    /**
     * 通過proxy生成的DBProcess的代理物件
     * new Cache(new MySQL())是關鍵
     */
    private DBProcess proxy = (DBProcess) Proxy.newProxyInstance(DBProcess.class.getClassLoader(), new Class[]{DBProcess.class}, new Cache(new MySQL()));
    @Override
    public Integer createUser(User user) {
        System.out.println("createUser in UserDaoImpl is invoked successfully..................");
        proxy.insert(user);
        return 1;
    }
    @Override
    public Integer deleteUser(User user) {
        System.out.println("deleteUser in UserDaoImpl is invoked successfully..................");
        proxy.delete(user);
        return 1;
    }
    @Override
    public Integer updateUser(User user) {
        System.out.println("updateUser in UserDaoImpl is invoked successfully..................");
        proxy.update(user);
        return 1;
    }
    @Override
    public User retrieveUser(Long userId) {
        System.out.println("retrieveUser in UserDaoImpl is invoked successfully..................");
        return proxy.select(userId);
    }
}

DBProcess.java

package com.ss.db;
import com.ss.entity.User;
public interface DBProcess {
    Integer insert(User user);
    Integer delete(User user);
    Integer update(User user);
    User select(Long userId);
}

Cache.java

package com.ss.db;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class Cache implements InvocationHandler{
    //快取容器
    private final static Map<Long, Object> cache = new HashMap<>();
    private DBProcess db;
    //只提供有參的構造器給外界,使得外界必須在獲取物件的同時初始化屬性
    public Cache(DBProcess db) {
        this.db = db;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //1.BootStrapClassLoader 2.ExtClassLoader 3.AppClassLoader 4.自定義ClassLoader
        if(method.getName().equalsIgnoreCase("select")){//如果時查詢方法則進行快取處理
            if(cache.get(args[0])!=null) return cache.get(args[0]);///相同的引數則從快取中獲取
            else{//如果快取中沒有則執行資料庫查詢並進行快取
                Object obj = method.invoke(db, args);
                cache.put((Long)args[0], obj);
                return obj;
            }
        }else{
            cache.clear();
            Object obj = method.invoke(db, args);
            return obj;
        }
    }
}


MySQL.java

package com.ss.db;
import java.util.Random;
import com.ss.entity.User;
public class MySQL implements DBProcess{

    //兩個陣列自行初始化,長度一樣即可
    private static String[] names = {};
    private static String[] nickNames = {};
    @Override
    public Integer insert(User user) {
        System.out.println(user + " is inserted to mysql successfullly............");
        return 1;
    }
    @Override
    public Integer delete(User user) {
        System.out.println(user + " is deleted from mysql successfullly............");
        return 1;
    }
    @Override
    public Integer update(User user) {
        System.out.println(user + " is updated to mysql successfullly............");
        return 1;
    }
    @Override
    public User select(Long userId) {
        System.out.println("............ MySQL's select is invoked ...........");
        Integer random = new Random().nextInt(10);
        return new User(userId, names[random], nickNames[random]);
    }
}

UserServiceImpl.java

package com.ss.service;
import org.junit.Test;
import com.ss.dao.UserDao;
import com.ss.dao.UserDaoImpl;
import com.ss.entity.User;
public class UserServiceImpl {
    @Test
    public void testCache(){
        UserDao dao = new UserDaoImpl();
        User user1 = dao.retrieveUser(12L);
        User user2 = dao.retrieveUser(12L);
        System.out.println(user1);
        System.out.println(user1 == user2);
        dao.deleteUser(new User(1L, "cache", "cc"));
        User user3 = dao.retrieveUser(12L);
        System.out.println(user3);
    }
}

執行testCache方法,結果如下所示