手写Spring框架第一篇博客(必读): 手写Spring框架
前面两篇博客已经实现了Bean容器, IOC功能和MVC功能, 本篇博客来实现AOP功能和事务管理. 在看下面的内容之前, 一定要先回顾下Spring AOP和动态代理.
Spring AOP
动态代理
定义注解
(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 proxyList = new ArrayList<>(); //代理列表
private int proxyIndex = 0; //代理索引
public ProxyChain(Class> targetClass, Object targetObject, Method targetMethod, MethodProxy methodProxy, Object[] methodParams, List 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 createProxy(final Class> targetClass, final List 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, Set>> aspectMap = createAspectMap();
//目标类-切面对象列表的映射
Map, List> targetMap = createTargetMap(aspectMap);
//把切面对象织入到目标类中, 创建代理对象
for (Map.Entry, List> targetEntry : targetMap.entrySet()) {
Class> targetClass = targetEntry.getKey();
List 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, Set>> createAspectMap() throws Exception {
Map, Set>> aspectMap = new HashMap, Set>>();
addAspectProxy(aspectMap);
return aspectMap;
}
/**
* 获取普通切面类-目标类集合的映射
*/
private static void addAspectProxy(Map, Set>> aspectMap) throws Exception {
//所有实现了AspectProxy抽象类的切面
Set> aspectClassSet = ClassHelper.getClassSetBySuper(AspectProxy.class);
for (Class> aspectClass : aspectClassSet) {
if (aspectClass.isAnnotationPresent(Aspect.class)) {
Aspect aspect = aspectClass.getAnnotation(Aspect.class);
//与该切面对应的目标类集合
Set> targetClassSet = createTargetClassSet(aspect);
aspectMap.put(aspectClass, targetClassSet);
}
}
}
/**
* 根据@Aspect定义的包名和类名去获取对应的目标类集合
*/
private static Set> createTargetClassSet(Aspect aspect) throws Exception {
Set> targetClassSet = new HashSet>();
// 包名
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, List> createTargetMap(Map, Set>> aspectMap) throws Exception {
Map, List> targetMap = new HashMap, List>();
for (Map.Entry, Set>> proxyEntry : aspectMap.entrySet()) {
//切面类
Class> aspectClass = proxyEntry.getKey();
//目标类集合
Set> targetClassSet = proxyEntry.getValue();
//创建目标类-切面对象列表的映射关系
for (Class> targetClass : targetClassSet) {
//切面对象
Proxy aspect = (Proxy) aspectClass.newInstance();
if (targetMap.containsKey(targetClass)) {
targetMap.get(targetClass).add(aspect);
} else {
//切面对象列表
List aspectList = new ArrayList();
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 getAllUser();
}
@Service
public class UserService implements IUserService {
/**
* 获取所有用户
*/
public List getAllUser() {
List 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 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_HOLDER;
private static final QueryRunner QUERY_RUNNER;
private static final BasicDataSource DATA_SOURCE;
static {
CONNECTION_HOLDER = new ThreadLocal();
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 queryEntity(Class entityClass, String sql, Object... params) {
T entity;
try {
Connection conn = getConnection();
entity = QUERY_RUNNER.query(conn, sql, new BeanHandler(entityClass), params);
} catch (SQLException e) {
LOGGER.error("query entity failure", e);
throw new RuntimeException(e);
}
return entity;
}
/**
* 查询实体列表
*/
public static List queryEntityList(Class entityClass, String sql, Object... params) {
List entityList;
try {
Connection conn = getConnection();
entityList = QUERY_RUNNER.query(conn, sql, new BeanListHandler(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 boolean insertEntity(Class entityClass, Map 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 boolean updateEntity(Class entityClass, long id, Map 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
(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, Set>> aspectMap = createAspectMap();
//目标类-切面对象列表的映射
Map, List> targetMap = createTargetMap(aspectMap);
//把切面对象织入到目标类中, 创建代理对象
for (Map.Entry, List> targetEntry : targetMap.entrySet()) {
Class> targetClass = targetEntry.getKey();
List 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, Set>> createAspectMap() throws Exception {
Map, Set>> aspectMap = new HashMap, Set>>();
addAspectProxy(aspectMap);
addTransactionProxy(aspectMap);
return aspectMap;
}
/**
* 获取普通切面类-目标类集合的映射
*/
private static void addAspectProxy(Map, Set>> aspectMap) throws Exception {
//所有实现了AspectProxy抽象类的切面
Set> aspectClassSet = ClassHelper.getClassSetBySuper(AspectProxy.class);
for (Class> aspectClass : aspectClassSet) {
if (aspectClass.isAnnotationPresent(Aspect.class)) {
Aspect aspect = aspectClass.getAnnotation(Aspect.class);
//与该切面对应的目标类集合
Set> targetClassSet = createTargetClassSet(aspect);
aspectMap.put(aspectClass, targetClassSet);
}
}
}
/**
* 获取事务切面类-目标类集合的映射
*/
private static void addTransactionProxy(Map, Set>> aspectMap) {
Set> serviceClassSet = ClassHelper.getClassSetByAnnotation(Service.class);
aspectMap.put(TransactionProxy.class, serviceClassSet);
}
/**
* 根据@Aspect定义的包名和类名去获取对应的目标类集合
*/
private static Set> createTargetClassSet(Aspect aspect) throws Exception {
Set> targetClassSet = new HashSet>();
// 包名
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, List> createTargetMap(Map, Set>> aspectMap) throws Exception {
Map, List> targetMap = new HashMap, List>();
for (Map.Entry, Set>> proxyEntry : aspectMap.entrySet()) {
//切面类
Class> aspectClass = proxyEntry.getKey();
//目标类集合
Set> targetClassSet = proxyEntry.getValue();
//创建目标类-切面对象列表的映射关系
for (Class> targetClass : targetClassSet) {
//切面对象
Proxy aspect = (Proxy) aspectClass.newInstance();
if (targetMap.containsKey(targetClass)) {
targetMap.get(targetClass).add(aspect);
} else {
//切面对象列表
List aspectList = new ArrayList();
aspectList.add(aspect);
targetMap.put(targetClass, aspectList);
}
}
}
return targetMap;
}
}
事务管理实例
(1) 业务类
public interface IUserService {
List getAllUser();
User GetUserInfoById(Integer id);
boolean updateUser(int id, Map fieldMap);
}
@Service
public class UserService implements IUserService {
/**
* 获取所有用户
*/
public List 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 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 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 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" %>
用户信息
用户信息
用户id
名称
年龄
${userinfo.id}
${userinfo.name}
${userinfo.age}
详情
编辑
(4) 结果
http://localhost:8081/handwritten/userList
点击编辑, 控制台打印:
begin transaction
commit transaction
结束
到此为止, handwritten-mvc-framwork 框架的所有功能都已实现, 包括Bean容器, IOC, MVC, AOP, 事务管理, 大家可以多看看源代码, 思路理顺之后一定会收获良多.