模拟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/

模拟spring - 动手写一个spring AOP

时间: 2024-10-15 12:02:54

模拟spring - 动手写一个spring AOP的相关文章

安装使用Spring boot 写一个hello1

一.创建springboot 项目 二.进行代编写 1.连接数据库:application.properties里配置 spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/huoguo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone

动手写一个Remoting测试工具

基于.NET开发分布式系统,经常用到Remoting技术.在测试驱动开发流行的今天,如果针对分布式系统中的每个Remoting接口的每个方法都要写详细的测试脚本,无疑非常浪费时间.所以,我想写一个能自动测试remoting接口的小工具InterfaceTester.而且,当分布式系统中的某个remoting接口出现bug时,该小工具可以提交需要模拟的数据,以便在调试remoting服务的环境中,快速定位和解决bug. InterfaceTester运行起来后的效果如下图: 1.如何使用 (1)首

死磕 java线程系列之自己动手写一个线程池

欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. (手机横屏看源码更方便) 问题 (1)自己动手写一个线程池需要考虑哪些因素? (2)自己动手写的线程池如何测试? 简介 线程池是Java并发编程中经常使用到的技术,那么自己如何动手写一个线程池呢?本文彤哥将手把手带你写一个可用的线程池. 属性分析 线程池,顾名思义它首先是一个"池",这个池里面放的是线程,线程是用来执行任务的. 首先,线程池中的线程应该是有类别的,有的是核心线程,有

写一个Spring Boot的Hello World

尽管这个demo也就hello world水平,但我还是要记录一下(总算能动了QAQ),毕竟老是看文章不动手不行啊 上次写Servlet的CRUD项目还是2月份,虽然代码忘的差不多了,但我就记得JDBC写起来特别累,作为入门还是学学Spring吧,然而被Spring的配置劝退了(现在看好像也不是问题,只是没有那种经验,碰到就懵逼),现在改用SpringBoot了,并且学习牛客网上的小demo作为入门参考 从啥都不会到写个能动的Spring Boot应用需要知道以下概念↓ 1.maven构建项目的

spring ioc原理(看完后大家可以自己写一个spring)

控制反转/依赖注入 最近,买了本Spring入门书:spring In Action .大致浏览了下感觉还不错.就是入门了点.Manning的书还是不错的,我虽然不像哪些只看Manning书的人那样专注于Manning,但怀着崇敬 的心情和激情通览了一遍.又一次接受了IOC .DI.AOP等Spring核心概念. 先就IOC和DI谈一点我的看法. IOC(DI): 其实这个Spring架构核心的概念没有这么复杂,更不像有些书上描述的那样晦涩.java程序员都知道:java程序中的每个业务逻辑至少

(转)spring ioc原理(看完后大家可以自己写一个spring)

最近,买了本Spring入门书:spring In Action .大致浏览了下感觉还不错.就是入门了点.Manning的书还是不错的,我虽然不像哪些只看Manning书的人那样专注于Manning,但怀着崇敬的心情和激情通览了一遍.又一次接受了IOC .DI.AOP等Spring核心概念. 先就IOC和DI谈一点我的看法. IOC(DI):其实这个Spring架构核心的概念没有这么复杂,更不像有些书上描述的那样晦涩.java程序员都知道:java程序中的每个业务逻辑至少需要两个或以上的对象来协

(转)spring ioc原理(看完后大家可以自己写一个spring)

原文地址:https://blog.csdn.net/it_man/article/details/4402245 最近,买了本Spring入门书:spring In Action .大致浏览了下感觉还不错.就是入门了点.Manning的书还是不错的,我虽然不像哪些只看Manning书的人那样专注于Manning,但怀着崇敬的心情和激情通览了一遍.又一次接受了IOC .DI.AOP等Spring核心概念. 先就IOC和DI谈一点我的看法. IOC(DI):其实这个Spring架构核心的概念没有这

学记:为spring boot写一个自动配置

spring boot遵循"约定由于配置"的原则,使用annotation对一些常规的配置项做默认配置,减少或不使用xml配置,让你的项目快速运行起来.spring boot的神奇不是借助代码的生成来实现的,而是通过条件注解来实现的. 自动配置AutoConfiguration是实现spring boot的重要原理,理解AutoConfiguration的运行原理特别重要,自己写一个AutoConfiguration可以加深我们对spring boot的理解. 1.定义Type-saf

挖个坑,写一个Spring+SpringMVC+Mybatis的项目

想挖个坑督促自己练技术,有时候想到一个项目,大概想了一些要实现的功能,怎么实现.现在觉得自己差不多能完成QQ空间的主要功能了.准备立个牌坊,写一个类似功能的网站.并且把进度放到这里来. 初步计划实现以下功能 1.用户注册.登录.信息修改: 2.用户进行好友关注.推送好用动态: 3.发表日志.评论和评论回复: 4.发表说说.评论和回复: 5.留言板功能,相册功能: 我还是个菜鸟,贴出来希望和大家分享,有什么考虑不周和技术运用不恰到的地方大家说出来改进一下.以上有些功能我以前用jsp+servlet