利用反射手写代码实现spring AOP
前言:上一篇博客自己动手编写spring IOC源码受到了大家的热情关注,在这里博客十分感谢。特别是给博主留言建议的@玛丽的竹子等等。本篇博客我们继续,还是在原有的基础上进行改造。下面请先欣赏一下博主画的一张aop简图(没有艺术天分,画的不好莫见怪)
解析:往往在我们的系统的多个核心流程中会有一部分与之关系不大的相同的横切流程,例如权限认证,事务管理。因此我们一般会抽象出这些相同的比较次要的交给spring aop的Handler来统一处理这些横切流程也就是上图中绿色部分。接下来我们看一下本例结构图:
解析:1,我们的Hostess对象是Master接口的实现,主要实现了WalkDog()和shopping()两个方法,而WalkDog()方法则是调用的是Dog接口的实现类的bark()方法。
2,我们整个程序的入口Client调用的Hostess对象的两个核心方法,HumanHandler处理的Hostess对象的横切流程。
public class Client { public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Master master = (Master)context.getBean("humanProxy"); System.out.println("");
System.out.println("");
System.out.println("");
System.out.println(""); master.shopping();
master.WalkDog(); }
}
package human; import dog.Dog; public class Hostess implements Master { private Dog dog; public void setDog(Dog dog) {
this.dog = dog;
} @Override
public void WalkDog() { dog.bark();
} @Override
public void shopping(){
System.out.println("疯狂购物中");
} }
解析:通过以上代码我们不难发现我们的程序只是调用核心业务,而往往核心业务的周围有很多繁琐的相对于比较次要的横切业务。利用本例中遛狗,购物之前,我们需要再家做一些前提准备。例如:整理一下着装,锁上房门等等,回家之后有需要换鞋之类的。因此我们还需要一个handler来处理这些业务。
package aop; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; public class HumanHandler implements InvocationHandler { private Object target;// 目标是不固定 public void setTarget(Object target) {
this.target = target;
} /*
* return 返回是原来目标方法所返回的内容 method 就是要执行的方法
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
before();
// 具体的业务逻辑代码
Object returnValue = method.invoke(target, args); after();
return returnValue;
} private void before() {
// 前置任务
System.out.println("[代理执行前置任务]整理着装");
System.out.println("[代理执行前置任务]带上钥匙");
System.out.println("");
System.out.println("[核心业务开始]*****************");
} private void after() {
// 后置任务
System.out.println("[核心业务结束]*****************");
System.out.println("");
System.out.println("[代理执行后置任务]开门");
System.out.println("[代理执行后置任务]换鞋");
} }
解析:有了handler我们还需要一个代理工厂
package org.springframework.aop.framework; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy; public class ProxyFactoryBean { private Object target; private InvocationHandler handler; public ProxyFactoryBean(Object target,InvocationHandler handler){
this.target = target;
this.handler = handler;
} //返回本类的一个实例
public Object getProxyBean() throws IllegalArgumentException, InstantiationException, IllegalAccessException, ClassNotFoundException{
Object obj = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler);
return obj;
}
}
接下来我们来看一下本例的具体配置
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="hostess" class="human.Hostess" scope="prototype">
<property name="dog" ref="dog1"></property>
</bean> <bean id="dog1" class="dog.Taidi" scope="prototype"></bean> <bean id="dog2" class="dog.Labuladuo" scope="prototype"></bean> <bean id="humanHandler" class="aop.HumanHandler">
<property name="target" ref="hostess"></property>
</bean> <bean id="humanProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="handlerName" ref="humanHandler"></property>
<property name="target" ref="hostess"></property>
</bean> </beans>
最后一步也是关键,本类中使用到的实例需要我们通过读取上面这份配置文件然后通过反射构造出来。
package aop; import java.io.File;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.aop.framework.ProxyFactoryBean; public class ClassPathXmlApplicationContext implements ApplicationContext { private String fileName; public ClassPathXmlApplicationContext(String fileName){
this.fileName = fileName;
} @Override
public Object getBean(String beanid) { System.out.println("传递过来的ID:"+beanid); //获取本类的当前目录
String currentPath = this.getClass().getResource("").getPath().toString();
SAXReader reader = new SAXReader();//DOM4J解释器
Document doc = null;//xml文档本身
Object obj = null;//目标表创建出来的实例
try {
doc = reader.read( new File(currentPath+fileName) );
String xpath = "/beans/bean[@id='"+beanid+"']";
Element beanNode = (Element) doc.selectSingleNode(xpath);
String className = beanNode.attributeValue("class"); if ("org.springframework.aop.framework.ProxyFactoryBean".equals(className)){ Element interceptorNamesNode =
(Element) beanNode.selectSingleNode("property[@name='handlerName']"); String handlerName_value = interceptorNamesNode.attributeValue("ref"); Element targetNode = (Element) beanNode.selectSingleNode("property[@name='target']");
String targetName_value = targetNode.attributeValue("ref"); return forProxyFactoryBean(targetName_value,handlerName_value);
} obj = Class.forName(className).newInstance(); //查找下一代
Element propertyNode = (Element) beanNode.selectSingleNode("property");
if (propertyNode!=null){
//注入
//System.out.println("发现property节点,准备注入");
//需要注入的属性名
String propertyName = propertyNode.attributeValue("name");
//System.out.println("需要注入的属性:"+propertyName); //注入方法
String executeMethod = "set"+(propertyName.substring(0, 1)).toUpperCase()+propertyName.substring(1,propertyName.length());
//System.out.println("需要执行注入方法:"+executeMethod); //需要注入的对象实例
String di_object_name = propertyNode.attributeValue("ref");
//System.out.println("注入的对象是:"+di_object_name); //定义我们的需要注入的对象实例[递归算法:1.层级是不知道多少层的 2.自己调用自己 3.最后1层会自己结束]
Object di_object = getBean(di_object_name);
//System.out.println("xxx:"+di_object); //Method method = obj.getClass().getMethod(executeMethod,di_object.getClass().getInterfaces());// new Method(executeMethod);
Method []methods = obj.getClass().getMethods(); for (Method m : methods) {
if(executeMethod.equals(m.getName()) ) {
m.invoke(obj, di_object);
break;
}
} }
else{
System.out.println("没有属性,结束即可");
} } catch (Exception e) {
e.printStackTrace();
} System.out.println("返回实例:"+obj);
return obj;
} public Object forProxyFactoryBean(String targetName_value,String handlerName_value) throws Exception{
System.out.println("目标对象"+targetName_value); Object target = getBean(targetName_value); System.out.println("代理对象"+handlerName_value); InvocationHandler handler = (InvocationHandler) getBean(handlerName_value); return new ProxyFactoryBean(target,handler).getProxyBean();
}
}
下面是运行结果
总结:1 spring aop将我们的系统分为两部分,一核心业务,二横切业务。我们的只需关注核心业务,横切业务统一交给代理去处理。
2 本例依旧是利用反射调用横切方法实现aop,还是那句话,我们自己写的自然是漏洞百出,只是为了说明问题。作为一个开源的框架,如果对spring源码感兴趣的朋友可以自行查看。
另:博主的个人博客也在努力搭建中,欢迎与各位学习交流,谢谢!个人博客地址:http://www.singletonh.top/
利用反射手写代码实现spring AOP的更多相关文章
- 今日份学习:写一些代码 (Spring+AOP+Redis+MySQL练习)
笔记 Spring+AOP+Redis+MySQL练习 1. 启动docker->mysql docker run --name mysql -v e:\docker:/var/lib/mysq ...
- 3.1 spring5源码系列--循环依赖 之 手写代码模拟spring循环依赖
本次博客的目标 1. 手写spring循环依赖的整个过程 2. spring怎么解决循环依赖 3. 为什么要二级缓存和三级缓存 4. spring有没有解决构造函数的循环依赖 5. spring有没有 ...
- 关于 Spring AOP (AspectJ) 该知晓的一切
关联文章: 关于Spring IOC (DI-依赖注入)你需要知道的一切 关于 Spring AOP (AspectJ) 你该知晓的一切 本篇是年后第一篇博文,由于博主用了不少时间在构思这篇博文,加上 ...
- 关于 Spring AOP (AspectJ) 你该知晓的一切
版权声明:本文为CSDN博主「zejian_」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明.原文链接:https://blog.csdn.net/javazej ...
- Spring Aop 应用实例与设计浅析
0.代码概述 代码说明:第一章中的代码为了突出模块化拆分的必要性,所以db采用了真实操作.下面代码中dao层使用了打印日志模拟插入db的方法,方便所有人运行demo. 1.项目代码地址:https:/ ...
- Spring学习之旅(六)Spring AOP工作原理初探
AOP(Aspect-Oriented Programming,面向切面编程)是Spring提供的关键技术之一. AOP基于IoC,是对OOP(Object-Oriented Programming ...
- 漫画 | Spring AOP的底层原理是什么?
1.Spring中配置的bean是在什么时候实例化的? 2.描述一下Spring中的IOC.AOP和DI IOC和AOP是Spring的两大核心思想 3.谈谈IOC.AOP和DI在项目开发中的应用场景 ...
- spring---aop(3)---Spring AOP的拦截器链
写在前面 时间断断续续,这次写一点关于spring aop拦截器链的记载.至于如何获取spring的拦截器,前一篇博客已经写的很清楚(spring---aop(2)---Spring AOP的JDK动 ...
- Spring aop+自定义注解统一记录用户行为日志
写在前面 本文不涉及过多的Spring aop基本概念以及基本用法介绍,以实际场景使用为主. 场景 我们通常有这样一个需求:打印后台接口请求的具体参数,打印接口请求的最终响应结果,以及记录哪个用户在什 ...
随机推荐
- 大数据系列(2)——Hadoop集群坏境CentOS安装
前言 前面我们主要分析了搭建Hadoop集群所需要准备的内容和一些提前规划好的项,本篇我们主要来分析如何安装CentOS操作系统,以及一些基础的设置,闲言少叙,我们进入本篇的正题. 技术准备 VMwa ...
- LINUX下的PHP
由于linux系统的稳定性,大部分的PHP服务器都被部署在linux上,而且像redis等扩展在linux能得到更好的支持,所以对于PHP程序员来说,使用linux的功底也相当重要,接下来总结一下我从 ...
- Windows 安装Kafka
Windows 7 安装Apache kafka_2.11-0.9.0.1 下载所需文件 Zookeeper: http://www.apache.org/dyn/closer.cgi/zoo ...
- [python]爬虫学习(一)
要学习Python爬虫,我们要学习的共有以下几点(python2): Python基础知识 Python中urllib和urllib2库的用法 Python正则表达式 Python爬虫框架Scrapy ...
- 修复百度编辑器(UM)禁用时上传图片按钮还可点击的BUG;
找到umeditor.js 大约在9342行,show 函数中,在方法开始部分加入: if(me.root().data().edui$mergeObj.hasClass("edui-dis ...
- Oracle转换时间出现的问题:ORA-01810: format code appears twice
Oralce中的to_date()函数用于将字符串转换为日期对象: to_date( string, [ format_mask ] , [ nls_language ] ) string1 要转换的 ...
- #1014 Trie树
本题主要是求构造一棵Trie树,即词典树用于统计单词. C#代码如下: using System; using System.Collections.Generic; using System.Lin ...
- <转>打工与乘公交
打工与乘公交 去一个公司打工就如同上了一辆公交车.在上车之前,你应该清楚自己打算去哪里,打算在哪里下车. 有的公交车很豪华,有的很破烂,但是这并不是重点,所有能开到目的地的车都是好车. 上了车之后,也 ...
- C#进阶系列——AOP?AOP!
前言:今天大阅兵,可是苦逼的博主还得坐在电脑前写博客,为了弄清楚AOP,博主也是拼了.这篇打算写写AOP,说起AOP,其实博主接触这个概念也才几个月,了解后才知道,原来之前自己写的好多代码原理就是基于 ...
- 恢复 Windows 7 的“回到父目录”按钮
Windows 7 使用以来很多方面一直不习惯,特别是让我无比纠结的”回到父目录“ 按钮从资源管理器中消失了. 不能不说这是一个失败! 很多时候,Win 7 地址栏中自以为是的显示的很多层目录层次的面 ...