实现自己的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();
}
}
@Override
public 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未能插入数据。回头看看我们的设计,我们发现,我们把事务处理相关的代码放到了统一的地方,避免了与业务代码耦合,只需在配置文件中配置哪些方法需要事务支持,哪些不需要事务支持,大大简化了代码复杂度。
实现自己的BeanFactory、AOP以及声明式事务的更多相关文章
- Spring注解驱动开发(四)-----aop、声明式事务
AOP 概念 指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式:-----基于动态代理 一个aop示例 1.导入aop模块:Spring AOP:(spring-aspects ...
- Spring AOP实现声明式事务代码分析
众所周知,Spring的声明式事务是利用AOP手段实现的,所谓"深入一点,你会更快乐",本文试图给出相关代码分析. AOP联盟为增强定义了org.aopalliance.aop.A ...
- 27Spring_的事务管理_银行转账业务加上事务控制_基于tx.aop进行声明式事务管理
上一篇文章中,银行转账业务没有使用事务,会出现问题,所以这篇文章对上篇文章出现的问题进行修改. 事务 依赖 AOP , AOP需要定义切面, 切面由Advice(通知) 和 PointCut(切点) ...
- AOP:声明式事务管理流程
1. 注册BeanFactoryTransactionAttributeSourceAdvisor @EnableTransactionManagement --> @Import(Transa ...
- Spring AOP声明式事务异常回滚(转)
转:http://hi.baidu.com/iduany/item/20f8f8ed24e1dec5bbf37df7 Spring AOP声明式事务异常回滚 近日测试用例,发现这样一个现象:在业务代码 ...
- Spring AOP声明式事务异常回滚
近日测试用例,发现这样一个现象:在业务代码中,有如下两种情况,比如:throw new RuntimeException("xxxxxxxxxxxx"); 事物回滚throw ne ...
- Spring -12 -声明式事务及完整的XML配置文件信息 -声明式事务中的相关属性(tx:advice的标签)
1.编程式事务: 1.1由程序员编程事务控制代码. 1.2OpenSessionInView 就属于编程式事务: session.commit()和rollback() 2.声明式事务: 2.1事务控 ...
- spring aop 声明式事务管理
一.声明式事务管理的概括 声明式事务(declarative transaction management)是Spring提供的对程序事务管理的方式之一. Spring的声明式事务顾名思义就是采用声明 ...
- Spring声明式事务管理基于tx/aop命名空间
目的:通过Spring AOP 实现Spring声明式事务管理; Spring支持编程式事务管理和声明式事务管理两种方式. 而声明式事务管理也有两种常用的方式,一种是基于tx/aop命名空间的xml配 ...
随机推荐
- 一句话搞定python六剑客
六剑客 一行搞定六剑客:三个函数:map filter reduce + lambda 切片 推导列表 python最有特点的一行代码,所有代码均可以借用一行代码(目标) 1.map(函数,列表或者字 ...
- 使用MingGW-w64 Build Script 3.6.7搭建ffmpeg编译环境
在Linux下编译的Windows版本ffmpeg没有其他的依赖库 使用的是centos 1.脚本下载 wget http://zeranoe.com/scripts/mingw_w64_build/ ...
- Flask中的request模板渲染Jinja以及Session
Flask中的request与django相似介绍几个常用的以后用的时候直接查询即可 1.request from flask import request(用之前先引用,与django稍有不同) r ...
- TCP/IP 网络模型
前言 互联网是怎么构成的,又是怎么运作的?什么是 TCP/IP 网络?为什么远隔万里的计算机可以互相通信?计算机网络作为 IT 行业的基石,是工程师永远绕不开的话题. 计算机网络的分层体系结构 计算机 ...
- 【ABAP系列】SAP ABAP 的替代和校验
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[ABAP系列]SAP ABAP 的替代和校验 ...
- Django-DRF组件学习-视图学习
1.请求与响应 drf除了在数据序列化部分简写代码以外,还在视图中提供了简写操作.所以在django原有的django.views.View类基础上,drf封装了多个子类出来提供给我们使用. Djan ...
- sklearn+nltk ——情感分析(积极、消极)
转载:https://www.iteye.com/blog/dengkane-2406703 步骤: 1 有标签的数据.数据:好评文本:pos_text.txt 差评文本:neg_text.txt ...
- mysql先分组,然后取每个分组中的第2大的记录
文章参考http://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/ 首先建表: ...
- uwsgi + nginx 部署python项目(一)
uWSGI uWSGI是一个Web服务器,它实现了WSGI协议.uwsgi.http等协议.Nginx中HttpUwsgiModule的作用是与uWSGI服务器进行交换. 要注意 WSGI / uws ...
- JavaSE_Java跨平台原理
Java语言的核心优势就是跨平台. C/C++语言都是直接编译成针对特定平台的机器码,如果要跨平台,需要借用相应的编译器重新编译.Java源程序(.java)要先编译成与平台无关的字节码文件(.cla ...