实现自己的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

实现自己的BeanFactory、AOP以及声明式事务相关推荐

  1. Spring AOP实现声明式事务代码分析

    众所周知,Spring的声明式事务是利用AOP手段实现的,所谓"深入一点,你会更快乐",本文试图给出相关代码分析. AOP联盟为增强定义了org.aopalliance.aop.A ...

  2. spring配置c3p0连接池、spring的声明式事务管理

    一.spring配置c3p0连接池: 1.导入maven依赖: <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 --> & ...

  3. Spring基于 XML 的声明式事务控制(配置方式)

    一.引入依赖 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http ...

  4. 【java学习之路】(java框架)010.声明式事务控制

    声明式事务控制 编程式事务控制相关对象 PlatformTransactionManager* PlatformTransactionManager 接口是 spring 的事务管理器,它里面提供了我 ...

  5. spring声明式事务管理方式( 基于tx和aop名字空间的xml配置+@Transactional注解)

    1. 声明式事务管理分类 声明式事务管理也有两种常用的方式, 一种是基于tx和aop名字空间的xml配置文件,另一种就是基于@Transactional注解. 显然基于注解的方式更简单易用,更清爽. ...

  6. Spring→事务、隔离级别、事务传播行为、编程式事务控制、XML配置声明式事务(原始方式)、XML配置声明式事务(基于tx/aop)、@注解配置声明式事务、优势总结

    事务 Spring事务管理 不考虑隔离引发问题 隔离级别 事务传播行为 演示环境搭建 编程式事务控制 XML配置声明式事务(原始方式) XML配置声明式事务(基于tx/aop) @注解配置声明式事务 ...

  7. Spring AOP声明式事务

    Spring AOP声明式事务 Spring AOP声明式事务 传统spring配置 SpringBoot配置 Spring AOP声明式事务 Spring AOP声明式事务可以帮我们自动管理事务,在 ...

  8. 框架源码专题:Spring声明式事务Transactional的原理

    文章目录 1. @Transactional的使用 2. spring事务的原理 2.1 开启事务,注册bean的后置处理器和相关bean对象,封装Advisor 2.2 匹配并创建动态代理 2.3 ...

  9. spring是如何实现声明式事务的

    前言 有好长一段时间没写文章了,之前一直在找工作,如今工作换好了,可以继续写文章了,今天我们来讲讲spring的声明式事务. 开始 说到声明式事务,我们现在回顾一下事务这个概念,什么是事务呢,事务指的 ...

最新文章

  1. docker容器 cpu memory 资源限制
  2. IT团队如何安全地加速云计算的采用
  3. spring boot 传递 List参数
  4. AE开发使用内存图层
  5. Android之列表对话框
  6. [Git GitHub] Windows下安装git,从0开始搭建git环境(配置环境变量+设置git-ssh key...配置)(超全版)
  7. 字典树从第i个构造HDU2846
  8. python shutil_Python shutil模块
  9. Java虚拟机 —— 内存和线程
  10. 左右mysql事务提交
  11. 把矩阵变为0,1矩阵
  12. 佳能Canon PIXMA G1010 打印机驱动
  13. layerdate时间控件的用法
  14. 萤石云平台接入_萤石开放平台对接海康摄像头(一)
  15. 阿里云添加邮箱解析 实现邮件收发
  16. linux中gnuplot给定文本,Gnuplot (三)输出图片/字体支持、eps/png/pdf/enhanced文本
  17. UPC 2020年夏混合个人训练第五十场【DEG】
  18. 数据库添加账号(mongoDB)
  19. java codeCache
  20. 派克Parker耐高低温伺服电机在汽车检测行业中的重要应用

热门文章

  1. Go语言爬虫项目将结果写入MySql数据库
  2. django和mysql写注册_Django电商项目---完成注册页面和用户登录
  3. 每日一题:leetcode82. 删除排序链表中的重复元素 II
  4. Linux C++ 实现线程池
  5. USB摄像头视频监控项目学习笔记
  6. Java架构师必备框架技能核心笔记,附相关架构及资料
  7. 提高SQL执行性能方案:如何让你的SQL运行得更快zt
  8. scrapy从安装到爬取煎蛋网图片
  9. windows下Call to undefined function curl_init() error问题
  10. -code vs 1474 十进制转m进制