实现自己的BeanFactory、AOP以及声明式事务

实现自己的BeanFactory                                                                  

      在使用spring时,我们很少用"new"关键字创建对象,而是通过spring容器BeanFactory提供的getBean()方法得到对象:

BeanFactory ctx = new ClassPathXmlApplicationContext();

      通过spring容器统一管理bean的创建,避免了代码中四处散落的"new"关键字,使我们能够统一管理对象的创建与销毁,本节将模仿spring创建一个自己版本的BeanFactory。首先我们定义一个Dao接口和实现,该接口提供一个方法用于保存Student:

public interface StudentDao {void saveStudent();
}public class StudentDaoImpl implements StudentDao {public void saveStudent(){System.out.println("save success!");}
}

  然后我们再创建一个service,该service将调用上面定义的Dao用于存储Student:

public class StudentService {private StudentDao stuDao;public void saveStudent() {stuDao.saveStudent();}public StudentDao getStuDao() {return stuDao;}public void setStuDao(StudentDao stuDao) {this.stuDao = stuDao;}
}

  和spring一样,我们也需要一个xml文件用于定义bean对象的创建规则,该xml文件即为beans.xml,其内容如下:

<beans><bean id="stuDao" class="dao.impl.StudentDaoImpl" /><bean id="stuService" class="service.StudentService"><property name="stuDao" bean="stuDao"/></bean>
</beans>

  现在,两个JavaBean对象已经定义好了,分别是:StudentDaoImpl和StudentService,beans.xml文件也定义好了,现在我们需要定义一个工厂(Factory),该Factory将根据beans.xml定义的对象创建规则创建JavaBean,然后把创建的JavaBean保存起来,并提供一个getBean()方法以便用户获得这些JavaBean,这个工厂的接口与实现如下:

public interface BeanFactory {public Object getBean(String name);
}public class ClassPathXmlApplicationContext implements BeanFactory {private Map<String, Object> beans = new HashMap<String, Object>();public ClassPathXmlApplicationContext() {try {SAXBuilder sb = new SAXBuilder();Document doc = (Document) sb.build(this.getClass().getClassLoader().getResourceAsStream("beans.xml"));Element root = doc.getRootElement();List<Element> list = (List<Element>) root.getChildren("bean");for (int i = 0; i < list.size(); i++) {Element element = (Element) list.get(i);String id = element.getAttributeValue("id");String clazz = element.getAttributeValue("class");Object o = Class.forName(clazz).newInstance();beans.put(id, o);for (Element element2 : (List<Element>) element.getChildren("property")) {String name = element2.getAttributeValue("name");String bean = element2.getAttributeValue("bean");Object beanObject = beans.get(bean);String methodName = "set"+ name.substring(0, 1).toUpperCase()+ name.substring(1);Method m = o.getClass().getMethod(methodName,beanObject.getClass().getInterfaces()[0]);m.invoke(o, beanObject);}}} catch (Exception e) {e.printStackTrace();}}@Overridepublic Object getBean(String name) {return beans.get(name);}
}

  可以看到,在ClassPathXmlApplicationContext的构造函数中,我们读取并解析xml文件,然后创建对象,并把对象保存在一个HashMap中,ClassPathXmlApplicationContext类的getBean方法传入一个bean name,然后我们在map中查找name对应对象并返回。

      最后,我们创建一个测试类,用于测试我们编写的代码是否正确:

public class Test {public static void main(String[] args) {BeanFactory ctx = new ClassPathXmlApplicationContext();StudentService s = (StudentService) ctx.getBean("stuService");s.saveStudent();}
}

  至此,一个简单的BeanFactory实现了,这个BeanFactory的实现使用到了xml解析技术和反射技术。

实现自己的AOP                                                                           

      AOP,即面向方面编程,主要用于把日志记录,性能统计,异常处理等非业务逻辑代码从业务逻辑代码中分离出来。下面我们通过Java动态代理实现自己的AOP功能,这个例子会在方法启动前和启动后打印当前时间,并计算方法耗时。首先我们定义一个Advice接口和实现,该接口定义了方法调用前和方法调用后的行为:

public interface Advice {void beforeMethod(Method method);void afterMethod(Method method);
}public class MyAdvice implements Advice {long beginTime = 0;public void beforeMethod(Method method) {System.out.println("before time: " + System.currentTimeMillis());beginTime = System.currentTimeMillis();}public void afterMethod(Method method) {System.out.println("after time: " + System.currentTimeMillis());	long endTime = System.currentTimeMillis();System.out.println(method.getName() + " running time of " + (endTime - beginTime));}
}

      然后我们定义一个xml文件,在该xml文件中定义了一个bean,这个bean有一个属性"advice":

<beans><bean id="testObject" class="java.util.ArrayList"><property advice="aoptest.MyAdvice"/></bean>
</beans>

  最后我们还是定义一个BeanFactory,该BeanFactory会解析这个xml文件:

public class BeanFactory {private Map<String, Object> beans = new HashMap<String, Object>();public BeanFactory() {try {SAXBuilder sb = new SAXBuilder();Document doc = (Document) sb.build(this.getClass().getClassLoader().getResourceAsStream("aop.xml"));Element root = doc.getRootElement();List<Element> list = (List<Element>) root.getChildren("bean");for (int i = 0; i < list.size(); i++) {Element element = (Element) list.get(i);String id = element.getAttributeValue("id");String clazz = element.getAttributeValue("class");Object target = Class.forName(clazz).newInstance();for (Element element2 : (List<Element>) element.getChildren("property")) {String adviceStr = element2.getAttributeValue("advice");MyAdvice advice = (MyAdvice) Class.forName(adviceStr).newInstance();beans.put(id, getProxy(advice, target));}}} catch (Exception e) {e.printStackTrace();}}public Object getProxy(final MyAdvice advice, final Object target) {Object result = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),new InvocationHandler() {public Object invoke(Object proxy, Method method,Object[] args) throws Throwable {advice.beforeMethod(method);Object retVal = method.invoke(target, args);advice.afterMethod(method);return retVal;}});return result;}public Object getBean(String name) {return beans.get(name);}
}

  注意,这个beanFactory的实现,在最后调用beans.put(id, getProxy(advice, target))方法时,存入map中的是一个代理对象,并不是xml中定义的原生方法。最后,我们编写一个测试类:

public class Test {public static void main(String[] args) throws Exception {Object bean = new BeanFactory().getBean("testObject");((Collection) bean).add(12);}
}

  在该测试类中,我们先从BeanFactory中得到bean,再调用bean上的add方法,该测试类的输出如下:

before time: 1416066155411
after time: 1416066155411
add running time of 0

  可以看到,如同我们预想的,在方法开始前打印了一下当前时间,在方法结束后又打印了时间,最后计算出了方法耗时,使用AOP的方法统计计算耗时,可以避免把统计代码与业务代码耦合在一起,可以方便统计代码的复用。

实现自己的声明式事务                                                                  

      声明式事务可以让我们从复杂的事务处理中得到解脱,使我们再也不需要在与事务相关的方法中处理大量的try...catch...finally代码,这章我们将实现自己的声明式事务,使用到的技术是上一章节介绍的aop技术。首先我们定义一个JdbcUtils类,该类有一个方法:getConnection,该方法会返回一个JDBC连接:

public final class JdbcUtils {public static Connection conn = null;public static boolean autoCommit = true;static {try {Class.forName("com.mysql.jdbc.Driver");conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/temp", "root", "");} catch (Exception e) {throw new ExceptionInInitializerError(e);}}private JdbcUtils() {}public static Connection getConnection() throws SQLException {conn.setAutoCommit(autoCommit);return conn;}
}

  注意,该类还包含一个autoCommit的静态布尔属性,在返回Connection之前会用该属性定义是否自动提交。然后,我们定义一个类用于数据库操作:

public interface UserDao {void save1() throws Exception;void save2() throws Exception;
}public class UserDaoImpl implements UserDao {public void save1() throws Exception {Connection conn = JdbcUtils.getConnection();Statement stmt = conn.createStatement();stmt.executeUpdate("insert into user(name, birthday, money) values('save1', '1984-10-11', 87446)");}public void save2() throws Exception {Connection conn = JdbcUtils.getConnection();Statement stmt = conn.createStatement();stmt.executeUpdate("insert into user(name, birthday, money) values('save2', '1984-10-11', 87446)");throw new RuntimeException("qq");}
}

      接着,我们定义一个Advice,该Advice在方法调用前把autoCommit设置为false,方法执行完成之后commit方法,如果捕捉到异常就回滚事务,最后再把autoCommit设置为true:

public class MyAdvice{public void beforeMethod(Method method) {JdbcUtils.autoCommit = false;}public void afterMethod(Method method) throws Exception {JdbcUtils.conn.commit();}public void finallyMethod(Method method) {JdbcUtils.autoCommit = true;}public void onException(Method method) throws SQLException {JdbcUtils.conn.rollback();}
}

  然后,我们定义一个xml文件,把bean和advice关系注册一下:

<beans><bean id="testObject" class="test.UserDaoImpl"><property advice="aopframework.MyAdvice"/></bean>
</beans>

  最后,定义BeanFactory解析xml文件,这段代码的内容和第二节代码十分相似,只有一点区别,在创建代理时候套上了try-catch-finally以便进行事务回滚:

public class BeanFactory {private Map<String, Object> beans = new HashMap<String, Object>();public BeanFactory() {try {SAXBuilder sb = new SAXBuilder();Document doc = (Document) sb.build(this.getClass().getClassLoader().getResourceAsStream("aop.xml"));Element root = doc.getRootElement();List<Element> list = (List<Element>) root.getChildren("bean");for (int i = 0; i < list.size(); i++) {Element element = (Element) list.get(i);String id = element.getAttributeValue("id");String clazz = element.getAttributeValue("class");Object target = Class.forName(clazz).newInstance();for (Element element2 : (List<Element>) element.getChildren("property")) {String adviceStr = element2.getAttributeValue("advice");MyAdvice advice = (MyAdvice) Class.forName(adviceStr).newInstance();beans.put(id, getProxy(advice, target));}}} catch (Exception e) {e.printStackTrace();}}public Object getProxy(final MyAdvice advice, final Object target) {Object result = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),new InvocationHandler() {public Object invoke(Object proxy, Method method,Object[] args) throws Throwable {Object retVal = null;try {advice.beforeMethod(method);retVal = method.invoke(target, args);advice.afterMethod(method);} catch (Exception e) {advice.onException(method);} finally {advice.finallyMethod(method);}return retVal;}});return result;}public Object getBean(String name) {return beans.get(name);}
}

  测试代码与第二节代码一致:

public class AopFrameworkTest {public static void main(String[] args) throws Exception {Object bean = new BeanFactory().getBean("testObject");((UserDao) bean).save1();((UserDao) bean).save2();}
}

  运行后,在数据库查看,可以发现只有save1方法插入的数据生效了,save2未能插入数据。回头看看我们的设计,我们发现,我们把事务处理相关的代码放到了统一的地方,避免了与业务代码耦合,只需在配置文件中配置哪些方法需要事务支持,哪些不需要事务支持,大大简化了代码复杂度。

 

转载于:https://www.cnblogs.com/timlearn/p/4099419.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/401256.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Docker遇到的一些问题和感想

Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/course/detail/35475 Docker 是“不可变”架构。 当你希望改变一个服务的时候&#xff08;比如更新版本、修改配置、开放端口&#xff09;&#…

hdu 4293 Groups DP

http://acm.hdu.edu.cn/showproblem.php?pid4293 题意&#xff1a; 有n个人分成了若干组走在一条林荫道路上&#xff0c;导游为了能够确定人数&#xff0c;要求每个人喊出自己所在的队伍前边有多少人Ai表示,后边有多少人Bi表示&#xff0c;于是我们得到了n条信息。这里面有错误…

IAR切BANK--命令连接器文件xcl格式说明

IAREWARM中段后缀含义 后缀 段类型 含义 AC CONST 绝对地址定位常数 AN DATA 用关键字__no_init声明的绝对地址定位数据 C CONST 常数 I DATA 初始化值为非0的数据 ID CONST 上述数据的初始式 N DATA 未初始化的数据 Z DATA 初始化值为0的数据 ROM用于…

ARM系列处理器的分类

1.ARM ARM即以英国ARM&#xff08;Advanced RISC Machines&#xff09;公司的内核芯片作为CPU&#xff0c;同时附加其他外围功能的嵌入式开发板&#xff0c;用以评估内核芯片的功能和研发各科技类企业的产品. ARM 微处理器目前包括下面几个系列&#xff0c;以及其它厂商基于 AR…

DBA_Oracle Table Partition表分区概念汇总(概念)

2014-06-20 Created By BaoXinjian 一、摘要 有关表分区的一些维护性操作&#xff1a; 注&#xff1a;分区根据具体情况选择。 表分区有以下优点&#xff1a; 1、数据查询&#xff1a;数据被存储到多个文件上&#xff0c;减少了I/O负载&#xff0c;查询速度提高。 2、数据修剪&…

electron打包vue项目

Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/course/detail/35475 创建项目 点击这里 添加electron-builder 1、在项目目录下运行命令&#xff1a;vue add electron-builder 2、electron-…

Hdu 4293 DP

题意&#xff1a; n个人说自己前面有多少人 后面有多少人 求出说真话人数最多的情况 每个样例有 一个 n 表示n个人 接下来 n 行有a b 表示他前面的人数和后面的人数 思路&#xff1a; 如果已经知道了其中一组的人数~ 就往前找..找在这一组之前与这一组的话不矛盾的最多人数 Ti…

IAR切BANK--程序中的使用

一、在IAR的集成开发环境中实现数据变量定位方法如下三种 1、__no_init char alpha 0x0200; 2、#pragma location 0x0202 const int beta; 3、const int gamma 0x0204 3; 或&#xff1a; 1&#xff09;__no_init int alpha "MYSEGMENT"; //MYSEGMENT段可在…

你需要知道的vue2 jsx render函数

通常开发vue我们使用的是模板语法&#xff0c;其实还有和react相同的语法&#xff0c;那就是render函数&#xff0c;同样支持jsx语法。 Vue 的模板实际是编译成了 render 函数。 0 传统的createElement方法 createElement(anchored-heading, {props: {level: 1}}, [createEleme…

|与||的区别

在众多编程语言中&#xff0c;|与||代表了不同的运算。其中|是按位或运算&#xff0c;||是逻辑或运算。从字面意思来理解&#xff0c;|常可以用于具体数值的计算&#xff0c;结果为数值&#xff0c;而||是用来逻辑运算的没结果只有False或者True。例如int a 2; int b 3; int …

CSS/CSS3语法新特性笔记

Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/course/detail/35475 CSS层叠样式表 三大特性 层叠性&#xff1a;相同的样式会覆盖 继承性&#xff1a;属性可向下继承 优先级&#xff1a;范…

vb 坐标点击

引用 &#xff1a;http://www.vbgood.com/thread-113934-1-1.html 第一步在窗体的通用栏写如下代码&#xff1a;Private Type pointapiX As LongY As LongEnd TypePrivate Declare Function GetCursorPos Lib "user32" (lpPoint As pointapi) As Long第二步在窗体上放…

开始写技术博客

2012年年底掏钱参加的java的培训&#xff0c;13年初找JAVA工作&#xff0c;发现很难找&#xff0c;公司需要的都是有工作经验的&#xff0c;刚刚从培训机构出来的学生不受欢迎&#xff0c;工作很难找&#xff0c;一个月之后打算退而求其次&#xff0c;找了份PHP的工作(那时对ph…

C# winform 自定义皮肤制作

最近要做个软件正在做技术准备&#xff0c;由于WINFORM生成的窗体很丑陋&#xff0c;一个好的软件除了功能性很重要外&#xff0c;UI的体验也是不容忽视的。习惯性的在网上搜素了下&#xff0c;换肤控件也有好几款&#xff0c;但是有些用起来不是很好用&#xff0c;好点的也要花…

Vue 源码解读(10)—— 编译器 之 生成渲染函数

Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/course/detail/35475 前言 这篇文章是 Vue 编译器的最后一部分&#xff0c;前两部分分别是&#xff1a;Vue 源码解读&#xff08;8&#xff09;…

蓝牙PROFILE

Bluetooth的一个很重要特性&#xff0c;就是所有的Bluetooth产品都无须实现全部 的Bluetooth规范。为了更容易的保持Bluetooth设备之间的兼容&#xff0c;Bluetooth规范中定义了Profile。Profile定义了设备如何实现一种连接或者应用&#xff0c;你可以把Profile理解为连接层或者…

Laravel Session 遇到的坑

这两天遇到了一个很奇怪的问题&#xff0c;更新session &#xff0c;session的值不变。经过一番追查&#xff0c;终于找到问题&#xff0c;并搞明白了原理。写这篇博客记录下。 框架版本 Laravel 5.4 问题 先来描述下问题&#xff0c;我在我们项目基础的Middleware中&#xff0…

bootstrap 中这段代码 使bundles 失败

bootstrap 中这段代码 使bundles 失败 _:-ms-fullscreen, :root input[type"date"], _:-ms-fullscreen, :root input[type"time"], _:-ms-fullscreen, :root input[type"datetime-local"], _:-ms-fullscreen, :root input[type"month"…

敏捷结果30天之第十二天:效率角色-你是启动者还是完成者

一.学习1.启动者&#xff1a;善于思考新想法&#xff0c;有太多想法还未开始&#xff0c;喜欢启动一些新事物&#xff0c;但是当事物成型之后就会离开去寻找下一个创新点。2.完成者&#xff1a;喜欢通过从头到尾的做完一件完整的事情来获得成就满足感。知道自己属于那种效率角色…

netty系列之:EventLoop,EventLoopGroup和netty的默认实现

Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/course/detail/35475 目录* 简介 EventLoopGroup和EventLoopEventLoopGroup在netty中的默认实现EventLoop在netty中的默认实现总结 简介 在net…