簡介
手寫Spring框架第一篇博客(必讀): 手寫Spring框架
前面兩篇博客已經實現了Bean容器, IOC功能和MVC功能, 本篇博客來實現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, 事務管理, 大家可以多看看源代碼, 思路理順之後一定會收穫良多.