模拟spring - 动手写一个spring AOP
一、前言
AOP (Aspect Oriented Programing) - 面向切面编程,它主要用于日志记录、性能分析、安全控制、事务处理、异常处理等方面。
AOP主要使用JDK的反射和动态代理,AOP代理其实是由AOP框架动态生成的一个对象,该对象可作为目标对象使用,AOP代理包含了目标对象的全部方法,但AOP代理的方法与目标对象的方法存在差异:AOP方法在特定切入点添加了增强处理,并回调了目标对象的方法。
动态代理的文章请参考:http://blog.csdn.net/zdp072/article/details/24868895
二、实现细节
下面这个例子利用AOP来实现日志记录:
附上一张类的结构图,该例子需要导入dom4j.jar
①业务逻辑接口
/** * 业务逻辑类接口 * @author zhangjim */ public interface BusinessService { /** * 处理业务 */ public void process(); }② 业务逻辑实现
/** * 业务逻辑对象实现类 * @author zhangjim */ public class BusinessServiceImpl implements BusinessService { /** * 处理业务 */ public void process() { System.out.println("process business logic..."); } }③ 通知类接口
/** * 通知类接口 * @author zhangjim */ public interface Advisor { /** * 所做的操作 */ public void doInAdvisor(Object proxy, Method method, Object[] args); }④ 方法的前置通知
import java.lang.reflect.Method; /** * 方法前置通知,它完成方法的前置操作 * @author zhangjim */ public class BeforeMethodAdvisor implements Advisor { /** * 在方法执行前所进行的操作 */ public void doInAdvisor(Object proxy, Method method, Object[] args) { System.out.println("before process... "); } }⑤ 方法的后置通知
/** * 方法的后置通知,它完成方法的后置操作 * @author zhangjim */ public class AfterMethodAdvisor implements Advisor { /** * 在方法执行后所进行的操作 */ public void doInAdvisor(Object proxy, Method method, Object[] args) { System.out.println("after process..."); } }⑥ AOP处理器
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import com.zdp.advisor.Advisor; /** * AOP处理器 * @author zhangjim */ public class AopHandler implements InvocationHandler { private Object target; // 需要代理的目标对象 Advisor beforeAdvisor; // 方法前置通知 Advisor afterAdvisor; // 方法后置通知 /** * 设置代理目标对象,并生成动态代理对象. * @param target 代理目标对象 * @return 返回动态代理对象 */ public Object setObject(Object target) { this.target = target; // 设置代理目标对象 Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); // 根据代理目标对象生成动态代理对象 return proxy; } /** * 若定义了前置处理,则在方法执行前执行前置处理, 若定义了后置处理,则在方法调用后调用后置处理. * * @param proxy 代理对象 * @param method 调用的业务方法 * @param args 方法的参数 * @return 返回结果信息 * @throws Throwable */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (beforeAdvisor != null) { beforeAdvisor.doInAdvisor(proxy, method, args); // 进行业务方法的前置处理 } method.invoke(target, args); // 执行业务方法 if (afterAdvisor != null) { afterAdvisor.doInAdvisor(proxy, method, args); // 进行业务方法的后置处理 } return null; // 返回结果对象 } /** * 设置方法的前置通知 * @param advisor 方法的前置通知 */ public void setBeforeAdvisor(Advisor advisor) { this.beforeAdvisor = advisor; } /** * 设置方法的后置通知 * @param advisor 方法的后置通知 */ public void setAfterAdvisor(Advisor advisor) { this.afterAdvisor = advisor; } }⑦ Bean工厂类
import java.io.InputStream; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; import com.zdp.advisor.Advisor; /** * Bean工厂类 * @author zhangjim */ public class BeanFactory { private Map<String, Object> beansMap = new HashMap<String, Object>(); /** * Bean工厂的初始化 */ public void init(String xml) { try { SAXReader reader = new SAXReader(); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); InputStream ins = classLoader.getResourceAsStream(xml); // 读取指定的配置文件 Document doc = reader.read(ins); Element root = doc.getRootElement(); AopHandler aopHandler = new AopHandler(); // 创建AOP处理器(动态生成的proxy类中持有了aopHandle的引用) for (Iterator iter = root.elementIterator("bean"); iter.hasNext();) { // 遍历bean Element element = (Element) iter.next(); Attribute id = element.attribute("id"); // 获取bean的属性id、class、aopClass、aopType Attribute cls = element.attribute("class"); Attribute aopClass = element.attribute("aopClass"); Attribute aopType = element.attribute("aopType"); if (aopClass != null && aopType != null) { // 如果配置了aopClass和aopType属性时,需进行拦截操作 Class advisorClass = Class.forName(aopClass.getText()); // 根据aopClass字符串获取对应的类 Advisor advisor = (Advisor) advisorClass.newInstance(); // 创建该类的对象 if ("before".equals(aopType.getText())) { // 根据aopType的类型来设置前置或后置顾问 aopHandler.setBeforeAdvisor(advisor); } else if ("after".equals(aopType.getText())) { aopHandler.setAfterAdvisor(advisor); } } Class clazz = Class.forName(cls.getText()); // 利用Java反射机制,通过class的名称获取Class对象 Object obj = clazz.newInstance(); // 创建一个对象 Object proxy = (Object) aopHandler.setObject(obj); // 产生代理对象proxy beansMap.put(id.getText(), proxy); // 将对象放入beansMap中,其中id为key,对象为value } } catch (Exception e) { System.out.println(e.toString()); } } /** * 通过bean的id获取bean的对象. * @param beanName bean的id * @return 返回对应对象 */ public Object getBean(String beanName) { Object obj = beansMap.get(beanName); return obj; } }⑧ 配置文件beans.xml
<?xml version="1.0" encoding="UTF-8"?> <beans> <bean id="businessService" class="com.zdp.service.BusinessServiceImpl" aopClass="com.zdp.advisor.BeforeMethodAdvisor" aopType="before"></bean> </beans>⑨ 测试类
import com.zdp.service.BusinessService; import com.zdp.spring.BeanFactory; /** * 测试类 * @author zhangjim */ public class Client { public static void main(String[] args) { BeanFactory beanFactory = new BeanFactory(); beanFactory.init("beans.xml"); BusinessService proxy = (BusinessService) beanFactory.getBean("businessService"); proxy.process(); } }
三、小结
上文仅仅是简单地模拟了spring的AOP的实现,但还是很好地展现了JDK反射和动态代理在spring中的应用,对于初学者理解AOP应该会有一点帮助。
源码下载地址: http://download.csdn.net/detail/zdp072/7284987
参考资料:http://www.ibm.com/developerworks/cn/java/j-lo-springaopcglib/
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。