手寫Spring框架之AOP

簡介

手寫Spring框架第一篇博客(必讀): 手寫Spring框架

前面兩篇博客已經實現了Bean容器, IOC功能和MVC功能, 本篇博客來實現AOP功能和事務管理. 在看下面的內容之前, 一定要先回顧下Spring AOP和動態代理.

Spring AOP

動態代理

handwritten-mvc-framwork 實現

定義註解

(1) 切面註解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Aspect {
    /**
     * 包名
     */
    String pkg() default "";

    /**
     * 類名
     */
    String cls() default "";
}

(2) 事務註解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Transactional {
}

搭建代理框架

毫無疑問這個代理框架是基於動態代理實現的, 但加了一個鏈式代理的功能, 目的是爲了解決多重代理的問題, 也就是目標對象的方法被多次增強.

(1) Proxy接口

我們自定義了一個最上層的代理接口, 其中doProxy()執行的是鏈式代理, 具體詳情可以看後面的介紹.

public interface Proxy {

    /**
     * 執行鏈式代理
     * 所謂鏈式代理, 就是說, 可將多個代理通過一條鏈子串起來, 一個個地去執行, 執行順序取決於添加到鏈上的先後順序
     */
    Object doProxy(ProxyChain proxyChain) throws Throwable;
}

(2) ProxyChain類

這是一個代理鏈類, proxyList 存儲的是代理列表(也就是增強列表), 當執行doProxyChain() 方法時會按照順序執行增強, 最後再執行目標方法.

public class ProxyChain {

    private final Class<?> targetClass; //目標類
    private final Object targetObject; //目標對象
    private final Method targetMethod; //目標方法
    private final MethodProxy methodProxy; //方法代理
    private final Object[] methodParams; //方法參數

    private List<Proxy> proxyList = new ArrayList<>(); //代理列表
    private int proxyIndex = 0; //代理索引

    public ProxyChain(Class<?> targetClass, Object targetObject, Method targetMethod, MethodProxy methodProxy, Object[] methodParams, List<Proxy> proxyList) {
        this.targetClass = targetClass;
        this.targetObject = targetObject;
        this.targetMethod = targetMethod;
        this.methodProxy = methodProxy;
        this.methodParams = methodParams;
        this.proxyList = proxyList;
    }

    public Object[] getMethodParams() {
        return methodParams;
    }

    public Class<?> getTargetClass() {
        return targetClass;
    }

    public Method getTargetMethod() {
        return targetMethod;
    }

    /**
     * 遞歸執行
     */
    public Object doProxyChain() throws Throwable {
        Object methodResult;
        if (proxyIndex < proxyList.size()) {
            //執行增強方法
            methodResult = proxyList.get(proxyIndex++).doProxy(this);
        } else {
            //目標方法最後執行且只執行一次
            methodResult = methodProxy.invokeSuper(targetObject, methodParams);
        }
        return methodResult;
    }
}

(3) AspectProxy類

AspectProxy是一個切面抽象類, 實現了Proxy接口, 類中定義了切入點判斷和各種增強. 當執行 doProxy() 方法時, 會先進行切入點判斷, 再執行前置增強, 代理鏈的下一個doProxyChain()方法, 後置增強等.

public abstract class AspectProxy implements Proxy {

    private static final Logger logger = LoggerFactory.getLogger(AspectProxy.class);

    @Override
    public final Object doProxy(ProxyChain proxyChain) throws Throwable {
        Object result = null;

        Class<?> cls = proxyChain.getTargetClass();
        Method method = proxyChain.getTargetMethod();
        Object[] params = proxyChain.getMethodParams();

        begin();
        try {
            if (intercept(method, params)) {
                before(method, params);
                result = proxyChain.doProxyChain();
                after(method, params);
            } else {
                result = proxyChain.doProxyChain();
            }
        } catch (Exception e) {
            logger.error("proxy failure", e);
            error(method, params, e);
            throw e;
        } finally {
            end();
        }

        return result;
    }

    /**
     * 開始增強
     */
    public void begin() {
    }

    /**
     * 切入點判斷
     */
    public boolean intercept(Method method, Object[] params) throws Throwable {
        return true;
    }

    /**
     * 前置增強
     */
    public void before(Method method, Object[] params) throws Throwable {
    }

    /**
     * 後置增強
     */
    public void after(Method method, Object[] params) throws Throwable {
    }

    /**
     * 異常增強
     */
    public void error(Method method, Object[] params, Throwable e) {
    }

    /**
     * 最終增強
     */
    public void end() {
    }
}

(4) ProxyFactory類

這是一個代理工廠類, 我們通過這個類來梳理上面的代理邏輯. 當調用 ProxyFactory.createProxy(final Class<?> targetClass, final List proxyList) 方法來創建一個代理對象後, 每次執行代理方法時都會調用 intercept() 方法, 從而創建一個 ProxyChain 對象, 並調用該對象的 doProxyChain() 方法. 調用doProxyChain()方法時會首先遞歸的執行增強, 最後再執行目標方法.

public class ProxyFactory {

    /**
     * 輸入一個目標類和一組Proxy接口實現, 輸出一個代理對象
     */
    @SuppressWarnings("unchecked")
    public static <T> T createProxy(final Class<?> targetClass, final List<Proxy> proxyList) {
        return (T) Enhancer.create(targetClass, new MethodInterceptor() {
            /**
             * 代理方法, 每次調用目標方法時都會先創建一個 ProxyChain 對象, 然後調用該對象的 doProxyChain() 方法.
             */
            @Override
            public Object intercept(Object targetObject, Method targetMethod, Object[] methodParams, MethodProxy methodProxy) throws Throwable {
                return new ProxyChain(targetClass, targetObject, targetMethod, methodProxy, methodParams, proxyList).doProxyChain();
            }
        });
    }
}

(5) AopHelper 助手類

AopHelper 助手類用來初始化整個AOP框架, 邏輯如下:

框架中所有Bean的實例都是從Bean容器中獲取, 然後再執行該實例的方法, 基於此, 初始化AOP框架實際上就是用代理對象覆蓋掉Bean容器中的目標對象, 這樣根據目標類的Class對象從Bean容器中獲取到的就是代理對象, 從而達到了對目標對象增強的目的.

public final class AopHelper {

    private static final Logger LOGGER = LoggerFactory.getLogger(AopHelper.class);

    static {
        try {
            //切面類-目標類集合的映射
            Map<Class<?>, Set<Class<?>>> aspectMap = createAspectMap();
            //目標類-切面對象列表的映射
            Map<Class<?>, List<Proxy>> targetMap = createTargetMap(aspectMap);
            //把切面對象織入到目標類中, 創建代理對象
            for (Map.Entry<Class<?>, List<Proxy>> targetEntry : targetMap.entrySet()) {
                Class<?> targetClass = targetEntry.getKey();
                List<Proxy> proxyList = targetEntry.getValue();
                Object proxy = ProxyFactory.createProxy(targetClass, proxyList);
                //覆蓋Bean容器裏目標類對應的實例, 下次從Bean容器獲取的就是代理對象了
                BeanHelper.setBean(targetClass, proxy);
            }
        } catch (Exception e) {
            LOGGER.error("aop failure", e);
        }
    }

    /**
     * 獲取切面類-目標類集合的映射
     */
    private static Map<Class<?>, Set<Class<?>>> createAspectMap() throws Exception {
        Map<Class<?>, Set<Class<?>>> aspectMap = new HashMap<Class<?>, Set<Class<?>>>();
        addAspectProxy(aspectMap);
        return aspectMap;
    }

    /**
     *  獲取普通切面類-目標類集合的映射
     */
    private static void addAspectProxy(Map<Class<?>, Set<Class<?>>> aspectMap) throws Exception {
        //所有實現了AspectProxy抽象類的切面
        Set<Class<?>> aspectClassSet = ClassHelper.getClassSetBySuper(AspectProxy.class);
        for (Class<?> aspectClass : aspectClassSet) {
            if (aspectClass.isAnnotationPresent(Aspect.class)) {
                Aspect aspect = aspectClass.getAnnotation(Aspect.class);
                //與該切面對應的目標類集合
                Set<Class<?>> targetClassSet = createTargetClassSet(aspect);
                aspectMap.put(aspectClass, targetClassSet);
            }
        }
    }

    /**
     * 根據@Aspect定義的包名和類名去獲取對應的目標類集合
     */
    private static Set<Class<?>> createTargetClassSet(Aspect aspect) throws Exception {
        Set<Class<?>> targetClassSet = new HashSet<Class<?>>();
        // 包名
        String pkg = aspect.pkg();
        // 類名
        String cls = aspect.cls();
        // 如果包名與類名均不爲空,則添加指定類
        if (!pkg.equals("") && !cls.equals("")) {
            targetClassSet.add(Class.forName(pkg + "." + cls));
        } else if (!pkg.equals("")) {
            // 如果包名不爲空, 類名爲空, 則添加該包名下所有類
            targetClassSet.addAll(ClassUtil.getClassSet(pkg));
        }
        return targetClassSet;
    }

    /**
     * 將切面類-目標類集合的映射關係 轉化爲 目標類-切面對象列表的映射關係
     */
    private static Map<Class<?>, List<Proxy>> createTargetMap(Map<Class<?>, Set<Class<?>>> aspectMap) throws Exception {
        Map<Class<?>, List<Proxy>> targetMap = new HashMap<Class<?>, List<Proxy>>();
        for (Map.Entry<Class<?>, Set<Class<?>>> proxyEntry : aspectMap.entrySet()) {
            //切面類
            Class<?> aspectClass = proxyEntry.getKey();
            //目標類集合
            Set<Class<?>> targetClassSet = proxyEntry.getValue();
            //創建目標類-切面對象列表的映射關係
            for (Class<?> targetClass : targetClassSet) {
                //切面對象
                Proxy aspect = (Proxy) aspectClass.newInstance();
                if (targetMap.containsKey(targetClass)) {
                    targetMap.get(targetClass).add(aspect);
                } else {
                    //切面對象列表
                    List<Proxy> aspectList = new ArrayList<Proxy>();
                    aspectList.add(aspect);
                    targetMap.put(targetClass, aspectList);
                }
            }
        }
        return targetMap;
    }
}

(6) HelperLoader 類

在手寫Spring之MVC這篇博客裏我們定義了這個類, 目的是爲了集中加載 ClassHelper, BeanHelper, IocHelper, ControllerHelper 這四個助手類, 這裏還要再加上個 AopHelper 類.

public final class HelperLoader {
    public static void init() {
        Class<?>[] classList = {
            ClassHelper.class,
            BeanHelper.class,
            AopHelper.class,
            IocHelper.class,
            ControllerHelper.class
        };
        for (Class<?> cls : classList) {
            ClassUtil.loadClass(cls.getName());
        }
    }
}

AOP實例

我們創建一個監控接口性能的切面, 當接口被調用後, 打印接口的執行時間.

(1) 業務類

public interface IUserService {
    List<User> getAllUser();
}

@Service
public class UserService implements IUserService {
    /**
     * 獲取所有用戶
     */
    public List<User> getAllUser() {
        List<User> userList = new ArrayList<>();
        userList.add(new User(1, "Tom", 22));
        userList.add(new User(2, "Alic", 12));
        userList.add(new User(3, "Bob", 32));
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return userList;
    }
}

(2) 處理器

@Controller
public class UserController {
    @Autowired
    private IUserService userService;

    /**
     * 用戶列表
     * @return
     */
    @RequestMapping(value = "/userList", method = RequestMethod.GET)
    public View getUserList() {
        List<User> userList = userService.getAllUser();
        return new View("index.jsp").addModel("userList", userList);
    }
}

(3) 接口性能監控切面

目標類是 com.tyshawn.controller 包下的 UserController, 切入點是 getUserList() 方法. 日誌記錄切入點方法的執行時間.

@Aspect(pkg = "com.tyshawn.controller", cls = "UserController")
public class EfficientAspect extends AspectProxy {
    private static final Logger LOGGER = LoggerFactory.getLogger(EfficientAspect.class);

    private long begin;

    /**
     * 切入點判斷
     */
    @Override
    public boolean intercept(Method method, Object[] params) throws Throwable {
        return method.getName().equals("getUserList");
    }

    @Override
    public void before(Method method, Object[] params) throws Throwable {
        LOGGER.debug("---------- begin ----------");
        begin = System.currentTimeMillis();
    }

    @Override
    public void after(Method method, Object[] params) throws Throwable {
        LOGGER.debug(String.format("time: %dms", System.currentTimeMillis() - begin));
        LOGGER.debug("----------- end -----------");
    }
}

(4) 結果

http://localhost:8081/handwritten/userList

---------- begin ----------
time: 1001ms
----------- end -----------

事務管理

我們要達到的目的是, 當我們在目標方法上加 @Transactional 註解後, 該方法就擁有了事務管理. 用AOP實現的思路就是, 前置增強爲開啓事務, 後置增強爲提交事務, 異常增強爲回滾事務.

(1) DatabaseHelper 助手類

DatabaseHelper 爲數據庫操作助手類, 可以通過該助手類進行增刪改查, 事務等一系列的數據庫操作.

public final class DatabaseHelper {

    private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseHelper.class);

    private static final ThreadLocal<Connection> CONNECTION_HOLDER;

    private static final QueryRunner QUERY_RUNNER;

    private static final BasicDataSource DATA_SOURCE;

    static {
        CONNECTION_HOLDER = new ThreadLocal<Connection>();

        QUERY_RUNNER = new QueryRunner();

        DATA_SOURCE = new BasicDataSource();
        DATA_SOURCE.setDriverClassName(ConfigHelper.getJdbcDriver());
        DATA_SOURCE.setUrl(ConfigHelper.getJdbcUrl());
        DATA_SOURCE.setUsername(ConfigHelper.getJdbcUsername());
        DATA_SOURCE.setPassword(ConfigHelper.getJdbcPassword());
    }

    /**
     * 獲取數據源
     */
    public static DataSource getDataSource() {
        return DATA_SOURCE;
    }

    /**
     * 獲取數據庫連接
     */
    public static Connection getConnection() {
        Connection conn = CONNECTION_HOLDER.get();
        if (conn == null) {
            try {
                conn = DATA_SOURCE.getConnection();
            } catch (SQLException e) {
                LOGGER.error("get connection failure", e);
                throw new RuntimeException(e);
            } finally {
                CONNECTION_HOLDER.set(conn);
            }
        }
        return conn;
    }

    /**
     * 開啓事務
     */
    public static void beginTransaction() {
        Connection conn = getConnection();
        if (conn != null) {
            try {
                conn.setAutoCommit(false);
            } catch (SQLException e) {
                LOGGER.error("begin transaction failure", e);
                throw new RuntimeException(e);
            } finally {
                CONNECTION_HOLDER.set(conn);
            }
        }
    }

    /**
     * 提交事務
     */
    public static void commitTransaction() {
        Connection conn = getConnection();
        if (conn != null) {
            try {
                conn.commit();
                conn.close();
            } catch (SQLException e) {
                LOGGER.error("commit transaction failure", e);
                throw new RuntimeException(e);
            } finally {
                CONNECTION_HOLDER.remove();
            }
        }
    }

    /**
     * 回滾事務
     */
    public static void rollbackTransaction() {
        Connection conn = getConnection();
        if (conn != null) {
            try {
                conn.rollback();
                conn.close();
            } catch (SQLException e) {
                LOGGER.error("rollback transaction failure", e);
                throw new RuntimeException(e);
            } finally {
                CONNECTION_HOLDER.remove();
            }
        }
    }

    /**
     * 查詢實體
     */
    public static <T> T queryEntity(Class<T> entityClass, String sql, Object... params) {
        T entity;
        try {
            Connection conn = getConnection();
            entity = QUERY_RUNNER.query(conn, sql, new BeanHandler<T>(entityClass), params);
        } catch (SQLException e) {
            LOGGER.error("query entity failure", e);
            throw new RuntimeException(e);
        }
        return entity;
    }

    /**
     * 查詢實體列表
     */
    public static <T> List<T> queryEntityList(Class<T> entityClass, String sql, Object... params) {
        List<T> entityList;
        try {
            Connection conn = getConnection();
            entityList = QUERY_RUNNER.query(conn, sql, new BeanListHandler<T>(entityClass), params);
        } catch (SQLException e) {
            LOGGER.error("query entity list failure", e);
            throw new RuntimeException(e);
        }
        return entityList;
    }

    /**
     * 執行更新語句(包括:update、insert、delete)
     */
    public static int update(String sql, Object... params) {
        int rows;
        try {
            Connection conn = getConnection();
            rows = QUERY_RUNNER.update(conn, sql, params);
        } catch (SQLException e) {
            LOGGER.error("execute update failure", e);
            throw new RuntimeException(e);
        }
        return rows;
    }

    /**
     * 插入實體
     */
    public static <T> boolean insertEntity(Class<T> entityClass, Map<String, Object> fieldMap) {
        if (MapUtils.isEmpty(fieldMap)) {
            LOGGER.error("can not insert entity: fieldMap is empty");
            return false;
        }

        String sql = "INSERT INTO " + entityClass.getSimpleName();
        StringBuilder columns = new StringBuilder("(");
        StringBuilder values = new StringBuilder("(");
        for (String fieldName : fieldMap.keySet()) {
            columns.append(fieldName).append(", ");
            values.append("?, ");
        }
        columns.replace(columns.lastIndexOf(", "), columns.length(), ")");
        values.replace(values.lastIndexOf(", "), values.length(), ")");
        sql += columns + " VALUES " + values;

        Object[] params = fieldMap.values().toArray();

        return update(sql, params) == 1;
    }

    /**
     * 更新實體
     */
    public static <T> boolean updateEntity(Class<T> entityClass, long id, Map<String, Object> fieldMap) {
        if (MapUtils.isEmpty(fieldMap)) {
            LOGGER.error("can not update entity: fieldMap is empty");
            return false;
        }

        String sql = "UPDATE " + entityClass.getSimpleName() + " SET ";
        StringBuilder columns = new StringBuilder();
        for (String fieldName : fieldMap.keySet()) {
            columns.append(fieldName).append(" = ?, ");
        }
        sql += columns.substring(0, columns.lastIndexOf(", ")) + " WHERE id = ?";

        List<Object> paramList = new ArrayList<Object>();
        paramList.addAll(fieldMap.values());
        paramList.add(id);
        Object[] params = paramList.toArray();

        return update(sql, params) == 1;
    }

    /**
     * 刪除實體
     */
    public static <T> boolean deleteEntity(Class<T> entityClass, long id) {
        String sql = "DELETE FROM " + entityClass.getSimpleName() + " WHERE id = ?";
        return update(sql, id) == 1;
    }
}

(2) TransactionProxy 類

TransactionProxy 爲事務切面類, 同樣實現了Proxy接口, 其 doProxy() 方法就是先判斷代理方法上有沒有 @Transactional 註解, 如果有就加上事務管理, 沒有就直接執行.

public class TransactionProxy implements Proxy {

    private static final Logger LOGGER = LoggerFactory.getLogger(TransactionProxy.class);

    @Override
    public Object doProxy(ProxyChain proxyChain) throws Throwable {
        Object result;
        Method method = proxyChain.getTargetMethod();
        //加了@Transactional註解的方法要做事務處理
        if (method.isAnnotationPresent(Transactional.class)) {
            try {
                DatabaseHelper.beginTransaction();
                LOGGER.debug("begin transaction");
                result = proxyChain.doProxyChain();
                DatabaseHelper.commitTransaction();
                LOGGER.debug("commit transaction");
            } catch (Exception e) {
                DatabaseHelper.rollbackTransaction();
                LOGGER.debug("rollback transaction");
                throw e;
            }
        } else {
            result = proxyChain.doProxyChain();
        }
        return result;
    }
}

(3) AopHelper 助手類

在前面的AOP部分我們已經知道了 AopHelper 的作用以及實現邏輯, 事務代理相比普通代理的差別是, 我們默認所有Service對象都被代理了, 也就是說通過Service的Class對象, 從Bean容器中得到的都是代理對象, 我們在執行代理方法時會判斷目標方法上是否存在 @Transactional 註解, 有就加上事務管理, 沒有就直接執行, 如上面的代碼 TransactionProxy.doProxy().

public final class AopHelper {

    private static final Logger LOGGER = LoggerFactory.getLogger(AopHelper.class);

    static {
        try {
            //切面類-目標類集合的映射
            Map<Class<?>, Set<Class<?>>> aspectMap = createAspectMap();
            //目標類-切面對象列表的映射
            Map<Class<?>, List<Proxy>> targetMap = createTargetMap(aspectMap);
            //把切面對象織入到目標類中, 創建代理對象
            for (Map.Entry<Class<?>, List<Proxy>> targetEntry : targetMap.entrySet()) {
                Class<?> targetClass = targetEntry.getKey();
                List<Proxy> proxyList = targetEntry.getValue();
                Object proxy = ProxyFactory.createProxy(targetClass, proxyList);
                //覆蓋Bean容器裏目標類對應的實例, 下次從Bean容器獲取的就是代理對象了
                BeanHelper.setBean(targetClass, proxy);
            }
        } catch (Exception e) {
            LOGGER.error("aop failure", e);
        }
    }

    /**
     * 獲取切面類-目標類集合的映射
     */
    private static Map<Class<?>, Set<Class<?>>> createAspectMap() throws Exception {
        Map<Class<?>, Set<Class<?>>> aspectMap = new HashMap<Class<?>, Set<Class<?>>>();
        addAspectProxy(aspectMap);
        addTransactionProxy(aspectMap);
        return aspectMap;
    }

    /**
     *  獲取普通切面類-目標類集合的映射
     */
    private static void addAspectProxy(Map<Class<?>, Set<Class<?>>> aspectMap) throws Exception {
        //所有實現了AspectProxy抽象類的切面
        Set<Class<?>> aspectClassSet = ClassHelper.getClassSetBySuper(AspectProxy.class);
        for (Class<?> aspectClass : aspectClassSet) {
            if (aspectClass.isAnnotationPresent(Aspect.class)) {
                Aspect aspect = aspectClass.getAnnotation(Aspect.class);
                //與該切面對應的目標類集合
                Set<Class<?>> targetClassSet = createTargetClassSet(aspect);
                aspectMap.put(aspectClass, targetClassSet);
            }
        }
    }

    /**
     *  獲取事務切面類-目標類集合的映射
     */
    private static void addTransactionProxy(Map<Class<?>, Set<Class<?>>> aspectMap) {
        Set<Class<?>> serviceClassSet = ClassHelper.getClassSetByAnnotation(Service.class);
        aspectMap.put(TransactionProxy.class, serviceClassSet);
    }

    /**
     * 根據@Aspect定義的包名和類名去獲取對應的目標類集合
     */
    private static Set<Class<?>> createTargetClassSet(Aspect aspect) throws Exception {
        Set<Class<?>> targetClassSet = new HashSet<Class<?>>();
        // 包名
        String pkg = aspect.pkg();
        // 類名
        String cls = aspect.cls();
        // 如果包名與類名均不爲空,則添加指定類
        if (!pkg.equals("") && !cls.equals("")) {
            targetClassSet.add(Class.forName(pkg + "." + cls));
        } else if (!pkg.equals("")) {
            // 如果包名不爲空, 類名爲空, 則添加該包名下所有類
            targetClassSet.addAll(ClassUtil.getClassSet(pkg));
        }
        return targetClassSet;
    }

    /**
     * 將切面類-目標類集合的映射關係 轉化爲 目標類-切面對象列表的映射關係
     */
    private static Map<Class<?>, List<Proxy>> createTargetMap(Map<Class<?>, Set<Class<?>>> aspectMap) throws Exception {
        Map<Class<?>, List<Proxy>> targetMap = new HashMap<Class<?>, List<Proxy>>();
        for (Map.Entry<Class<?>, Set<Class<?>>> proxyEntry : aspectMap.entrySet()) {
            //切面類
            Class<?> aspectClass = proxyEntry.getKey();
            //目標類集合
            Set<Class<?>> targetClassSet = proxyEntry.getValue();
            //創建目標類-切面對象列表的映射關係
            for (Class<?> targetClass : targetClassSet) {
                //切面對象
                Proxy aspect = (Proxy) aspectClass.newInstance();
                if (targetMap.containsKey(targetClass)) {
                    targetMap.get(targetClass).add(aspect);
                } else {
                    //切面對象列表
                    List<Proxy> aspectList = new ArrayList<Proxy>();
                    aspectList.add(aspect);
                    targetMap.put(targetClass, aspectList);
                }
            }
        }
        return targetMap;
    }
}

事務管理實例

(1) 業務類

public interface IUserService {
    List<User> getAllUser();

    User GetUserInfoById(Integer id);

    boolean updateUser(int id, Map<String, Object> fieldMap);
}

@Service
public class UserService implements IUserService {
    /**
     * 獲取所有用戶
     */
    public List<User> getAllUser() {
        String sql = "SELECT * FROM user";
        return DatabaseHelper.queryEntityList(User.class, sql);
    }

    /**
     * 根據id獲取用戶信息
     */
    public User GetUserInfoById(Integer id) {
        String sql = "SELECT * FROM user WHERE id = ?";
        return DatabaseHelper.queryEntity(User.class, sql, id);
    }

    /**
     * 修改用戶信息
     */
    @Transactional
    public boolean updateUser(int id, Map<String, Object> fieldMap) {
        return DatabaseHelper.updateEntity(User.class, id, fieldMap);
    }
}

(2) 處理器

@Controller
public class UserController {
    @Autowired
    private IUserService userService;

    /**
     * 用戶列表
     *
     * @return
     */
    @RequestMapping(value = "/userList", method = RequestMethod.GET)
    public View getUserList() {
        List<User> userList = userService.getAllUser();
        return new View("index.jsp").addModel("userList", userList);
    }

    /**
     * 用戶詳情
     *
     * @param param
     * @return
     */
    @RequestMapping(value = "/userInfo", method = RequestMethod.GET)
    public Data getUserInfo(Param param) {
        String id = (String) param.getParamMap().get("id");
        User user = userService.GetUserInfoById(Integer.parseInt(id));

        return new Data(user);
    }

    @RequestMapping(value = "/userEdit", method = RequestMethod.GET)
    public Data editUser(Param param) {
        String id = (String) param.getParamMap().get("id");
        Map<String, Object> fieldMap = new HashMap<>();
        fieldMap.put("age", 911);
        userService.updateUser(Integer.parseInt(id), fieldMap);

        return new Data("Success.");
    }
}

(3) JSP頁面

<%@ page pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set var="BASE" value="${pageContext.request.contextPath}"/>
<html>
<head>
    <title>用戶信息</title>
</head>
<body>
<h1>用戶信息</h1>
<table>
    <tr>
        <th>用戶id</th>
        <th>名稱</th>
        <th>年齡</th>
    </tr>
    <c:forEach var="userinfo" items="${userList}">
        <tr>
            <td>${userinfo.id}</td>
            <td>${userinfo.name}</td>
            <td>${userinfo.age}</td>
            <td>
                <a href="${BASE}/userInfo?id=${userinfo.id}">詳情</a>
                <a href="${BASE}/userEdit?id=${userinfo.id}">編輯</a>
            </td>
        </tr>
    </c:forEach>
</table>
</body>
</html>

(4) 結果

http://localhost:8081/handwritten/userList

點擊編輯, 控制檯打印: 
begin transaction
commit transaction

結束

到此爲止, handwritten-mvc-framwork 框架的所有功能都已實現, 包括Bean容器, IOC, MVC, AOP, 事務管理, 大家可以多看看源代碼, 思路理順之後一定會收穫良多.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章