实现本身的BeanFactory java
在使用spring时,咱们不多用"new"关键字建立对象,而是经过spring容器BeanFactory提供的getBean()方法获得对象:mysql
BeanFactory ctx = new ClassPathXmlApplicationContext();
经过spring容器统一管理bean的建立,避免了代码中四处散落的"new"关键字,使咱们可以统一管理对象的建立与销毁,本节将模仿spring建立一个本身版本的BeanFactory。首先咱们定义一个Dao接口和实现,该接口提供一个方法用于保存Student:spring
public interface StudentDao { void saveStudent(); } public class StudentDaoImpl implements StudentDao { public void saveStudent(){ System.out.println("save success!"); } }
而后咱们再建立一个service,该service将调用上面定义的Dao用于存储Student:sql
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对应对象并返回。ide
最后,咱们建立一个测试类,用于测试咱们编写的代码是否正确:函数
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未能插入数据。回头看看咱们的设计,咱们发现,咱们把事务处理相关的代码放到了统一的地方,避免了与业务代码耦合,只需在配置文件中配置哪些方法须要事务支持,哪些不须要事务支持,大大简化了代码复杂度。