zoukankan      html  css  js  c++  java
  • 实现自己的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未能插入数据。回头看看我们的设计,我们发现,我们把事务处理相关的代码放到了统一的地方,避免了与业务代码耦合,只需在配置文件中配置哪些方法需要事务支持,哪些不需要事务支持,大大简化了代码复杂度。

  • 相关阅读:
    git 配置免密上传,配置ssh key
    spring @value 为什么没有获取到值
    idea 下maven 导入本地jar,以及导入之后 java不能引用问题
    在git远程仓创建项目之后,提交本地项目的使用方法
    mysql 查询数据库参数命令
    spring Existing transaction found for transaction marked with propagation 'never' 解决
    nginx for ubuntu
    spring中for循环中事务
    面向接口编程详解(一)——思想基础
    实战MEF(5):导出元数据
  • 原文地址:https://www.cnblogs.com/timlearn/p/4099419.html
Copyright © 2011-2022 走看看