首页 > 基础资料 博客日记
Java-28 深入浅出 Spring - 实现简易Ioc-04 在上节的业务下手动实现AOP
2025-01-14 12:30:12基础资料围观31次
点一下关注吧!!!非常感谢!!持续更新!!!
大数据篇正在更新!https://blog.csdn.net/w776341482/category_12713819.html
目前已经更新到了:
- MyBatis(已更完)
- Spring(正在更新…)
AOP 实现
注意,这里的 AOP 还不具备扩展能力,只是一个简易的实现,帮助我们更好的实现对事务的控制。
AOP(面向切面编程)
AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,它通过分离关注点来提高代码的模块化,尤其是与横切关注点(cross-cutting concerns)相关的功能。这些横切关注点是指那些与业务逻辑并非直接相关,但却需要跨越多个模块的功能,如日志记录、安全验证、事务管理等。AOP 通过提供一种方式来将这些横切关注点与业务逻辑分离,进而增强代码的复用性和可维护性。
AOP的核心概念
AOP 的工作方式与 OOP(面向对象编程)有所不同,OOP 强调的是对象的定义和封装,而 AOP 强调的是如何把程序的不同功能切分成不同的模块。AOP 的关键概念主要包括以下几个:
切面(Aspect)
切面是 AOP 中最重要的概念,它是对某个横切关注点的模块化。一个切面定义了一个与核心业务逻辑无关的功能(例如,日志记录、性能监控等),并把这些功能应用到程序的各个部分。切面可以包含多个通知和切点。
连接点(Joinpoint)
连接点指的是程序中能够插入切面的地方,通常是方法调用、方法执行、异常抛出等。连接点表示了在程序的某个执行点插入切面逻辑的机会。在 Java 中,连接点通常是一个方法调用。
通知(Advice)
通知是切面在连接点上执行的具体行为或操作。通知有不同的类型,例如:
前置通知(Before):在目标方法执行前执行。
后置通知(After):在目标方法执行后执行,不管方法是成功还是失败。
环绕通知(Around):在目标方法执行前后都可以控制,甚至可以选择是否调用目标方法。
异常通知(Throws):在目标方法抛出异常后执行。
返回通知(AfterReturning):在目标方法正常返回后执行。
切点(Pointcut)
切点是一个定义了通知执行位置的表达式,它指定了哪些连接点需要被拦截。切点通常是基于方法签名、注解、执行路径等条件来匹配的。切点用于选择需要应用通知的连接点。
织入(Weaving)
织入是将切面应用到目标对象的过程。这个过程可以在编译时、类加载时或运行时进行。织入的方式决定了 AOP 实现的具体方式。常见的 AOP 织入方式有:
- 编译时织入:在源代码编译时完成切面的织入(例如使用 AspectJ 编译器)。
- 类加载时织入:在类加载过程中通过修改字节码来完成织入。
- 运行时织入:在程序运行时通过代理等方式完成织入(Spring AOP)。
Resources
<?xml version="1.0" encoding="UTF-8" ?>
<!-- BeanFactory 类会进行处理这块内容 -->
<beans>
<!-- WzkConnectionUtils 交给容器管理 -->
<bean id="wzkConnectionUtils" class="wzk.utils.WzkConnectionUtils"></bean>
<!-- 依赖了工具类 WzkConnectionUtils -->
<!-- id 是放入到容器中的名称 -->
<bean id="wzkAccountDao" class="wzk.dao.impl.JdbcWzkAccountDaoImpl">
<!-- name是成员变量名字 ref是引用从容器中拿对象 -->
<property name="WzkConnectionUtils" ref="wzkConnectionUtils"/>
</bean>
<!-- 依赖了 WzkAccountDao -->
<bean id="wzkTransferService" class="wzk.service.impl.WzkTransferServiceImpl">
<!-- name是成员变量名字 ref是引用从容器中拿对象 -->
<property name="WzkAccountDao" ref="wzkAccountDao"></property>
</bean>
<!-- WzkTransactionManager 依赖了 WzkConnectionUtils -->
<bean id="wzkTransactionManager" class="wzk.utils.WzkTransactionManager">
<property name="WzkConnectionUtils" ref="wzkConnectionUtils"></property>
</bean>
<!-- ProxyFactory 依赖了 WzkTransactionManager -->
<bean id="proxyFactory" class="wzk.factory.ProxyFactory">
<property name="WzkTransactionManager" ref="wzkTransactionManager"></property>
</bean>
</beans>
对应的截图如下所示:
WzkTransactionManager
编写:事务控制器
/**
* 事务的控制器 这里只是对 JDBC 等操作的简单封装
* @author wzk
* @date 11:31 2024/11/19
**/
public class WzkTransactionManager {
private WzkConnectionUtils wzkConnectionUtils;
public void setWzkConnectionUtils(WzkConnectionUtils wzkConnectionUtils) {
this.wzkConnectionUtils = wzkConnectionUtils;
}
public void beginTransaction() throws SQLException {
wzkConnectionUtils.getCurrentConnection().setAutoCommit(false);
}
public void commit() throws SQLException {
wzkConnectionUtils.getCurrentConnection().commit();
}
public void rollback() throws SQLException {
wzkConnectionUtils.getCurrentConnection().rollback();
}
}
对应的截图如下所示:
Proxy
ProxyFactory
代理的工厂具体实现如下:
/**
* 代理工厂 + 单例模式
* 参考代理设计模式 实现事务的控制
* 这里主要实现的是动态代理的方式
* @author wzk
* @date 11:33 2024/11/19
**/
public class ProxyFactory {
private WzkTransactionManager wzkTransactionManager;
public void setWzkTransactionManager(WzkTransactionManager wzkTransactionManager) {
this.wzkTransactionManager = wzkTransactionManager;
}
/**
* 动态代理-JDK的实现
* @author wzk
* @date 11:35 2024/11/19
**/
public Object getJdkProxy(Object object) {
return Proxy.newProxyInstance(
object.getClass().getClassLoader(),
object.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result;
// 开启事务
wzkTransactionManager.beginTransaction();
// 执行原来的方法
try {
result = method.invoke(object, args);
} catch (Exception e) {
// 回滚
wzkTransactionManager.rollback();
throw e;
}
// 提交事务
wzkTransactionManager.commit();
return result;
}
}
);
}
/**
* 动态代理-CGLIB的实现
* @author wzk
* @date 13:57 2024/11/19
**/
public Object getCglibProxy(String object) {
return Enhancer.create(
object.getClass(),
new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Object result;
wzkTransactionManager.beginTransaction();
try {
result = method.invoke(object, args);
} catch (Exception e) {
wzkTransactionManager.rollback();
throw e;
}
wzkTransactionManager.commit();
return result;
}
}
);
}
}
对应的截图如下如下:
Controller
WzkServlet
代码可能有点长,本来考虑复制片段,但是考虑到全局,不然大家看的比较晕,所以都复制过来了。
@WebServlet(name="wzkServlet", urlPatterns = "/wzkServlet")
public class WzkServlet extends HttpServlet {
// ========================== 2 ==========================
// 由 BeanFactory 处理
// private WzkTransferService wzkTransferService = (WzkTransferService) BeanFactory.getBean("wzkTransferService");
// =======================================================
// ========================== 3 ==========================
// 另一种方式 相同的
// private WzkTransferService wzkTransferService;
// @Override
// public void init() throws ServletException {
// super.init();
// this.wzkTransferService = (WzkTransferService) BeanFactory.getBean("wzkTransferService");
// }
// ======================================================
// ================== 4 ======================
// 获取代理的对象 有事务 AOP 加持的
private WzkTransferService wzkTransferService;
@Override
public void init() throws ServletException {
super.init();
// 从BeanFactory中国拿到代理工厂ProxyFactory 可以对某个类进行代理扩展
// 这里扩展的功能点:事务提交与回滚
ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory");
this.wzkTransferService = (WzkTransferService) proxyFactory.getJdkProxy(
BeanFactory.getBean("wzkTransferService"));
}
// ===========================================
@Override
protected void doGet(javax.servlet.http.HttpServletRequest req, javax.servlet.http.HttpServletResponse resp) throws javax.servlet.ServletException, IOException {
System.out.println("=== WzkServlet doGet ===");
// ======================= 1 =============================
// 由于没有 Spring 的帮助 我们就需要手动去创建和维护依赖之间的关系
// 组装 DAO DAO层依赖于 ConnectionUtils 和 DruidUtils
// JdbcWzkAccountDaoImpl jdbcWzkAccountDaoImpl = new JdbcWzkAccountDaoImpl();
// jdbcWzkAccountDaoImpl.setConnectionUtils(new WzkConnectionUtils());
// 组装 Service
// WzkTransferServiceImpl wzkTransferService = new WzkTransferServiceImpl();
// wzkTransferService.setWzkAccountDao(jdbcWzkAccountDaoImpl);
// ======================================================
// 执行业务逻辑
System.out.println("wzkTransferService: " + wzkTransferService);
try {
wzkTransferService.transfer("1", "2", 100);
} catch (Exception e) {
e.printStackTrace();
System.out.println("=== transfer error ====");
}
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().print("=== WzkServlet doGet ===");
}
}
对应的截图如下所示:
测试运行1
启动项目进行测试,这里测试顺利执行的情况。
对应的截图如下所示,可以看到顺利的执行了:
对应的数据库的处理也是可以的:
测试运行2
这里测试,当运行过程中出现问题,需要进行回滚的情况。
我们需要在 impl 里 WzkTransferServiceImpl 的 transfer 代码中,随便加入一个异常,比如 用 1 除以 0 这种。
@Override
public void transfer(String fromCard, String toCard, int money) throws Exception {
WzkAccount from = wzkAccountDao.selectWzkAccount(fromCard);
WzkAccount to = wzkAccountDao.selectWzkAccount(toCard);
from.setMoney(from.getMoney() - money);
to.setMoney(to.getMoney() + money);
int fromResult = wzkAccountDao.updateWzkAccount(from);
// 故意制造异常
int i = 1 / 0;
int toResult = wzkAccountDao.updateWzkAccount(to);
System.out.println("transfer fromResult: " + fromResult + " toResult: " + toResult);
}
对应的代码截图如下所示,此时执行之后,数据库的数据应该是不会变化才对(因为事务管理器回滚了):
对应的结果如下所示,可以看到确实是报错了:
数据库的数据如下所示:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!
标签: