前言:上一篇博客自己动手编写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的更多相关文章

  1. 今日份学习:写一些代码 (Spring+AOP+Redis+MySQL练习)

    笔记 Spring+AOP+Redis+MySQL练习 1. 启动docker->mysql docker run --name mysql -v e:\docker:/var/lib/mysq ...

  2. 3.1 spring5源码系列--循环依赖 之 手写代码模拟spring循环依赖

    本次博客的目标 1. 手写spring循环依赖的整个过程 2. spring怎么解决循环依赖 3. 为什么要二级缓存和三级缓存 4. spring有没有解决构造函数的循环依赖 5. spring有没有 ...

  3. 关于 Spring AOP (AspectJ) 该知晓的一切

    关联文章: 关于Spring IOC (DI-依赖注入)你需要知道的一切 关于 Spring AOP (AspectJ) 你该知晓的一切 本篇是年后第一篇博文,由于博主用了不少时间在构思这篇博文,加上 ...

  4. 关于 Spring AOP (AspectJ) 你该知晓的一切

    版权声明:本文为CSDN博主「zejian_」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明.原文链接:https://blog.csdn.net/javazej ...

  5. Spring Aop 应用实例与设计浅析

    0.代码概述 代码说明:第一章中的代码为了突出模块化拆分的必要性,所以db采用了真实操作.下面代码中dao层使用了打印日志模拟插入db的方法,方便所有人运行demo. 1.项目代码地址:https:/ ...

  6. Spring学习之旅(六)Spring AOP工作原理初探

    AOP(Aspect-Oriented  Programming,面向切面编程)是Spring提供的关键技术之一. AOP基于IoC,是对OOP(Object-Oriented Programming ...

  7. 漫画 | Spring AOP的底层原理是什么?

    1.Spring中配置的bean是在什么时候实例化的? 2.描述一下Spring中的IOC.AOP和DI IOC和AOP是Spring的两大核心思想 3.谈谈IOC.AOP和DI在项目开发中的应用场景 ...

  8. spring---aop(3)---Spring AOP的拦截器链

    写在前面 时间断断续续,这次写一点关于spring aop拦截器链的记载.至于如何获取spring的拦截器,前一篇博客已经写的很清楚(spring---aop(2)---Spring AOP的JDK动 ...

  9. Spring aop+自定义注解统一记录用户行为日志

    写在前面 本文不涉及过多的Spring aop基本概念以及基本用法介绍,以实际场景使用为主. 场景 我们通常有这样一个需求:打印后台接口请求的具体参数,打印接口请求的最终响应结果,以及记录哪个用户在什 ...

随机推荐

  1. log4net 自定义Layout日志字段

    最近在使用log4net的时候有一个简单的需求,就是自定义个格式化输出符.这个输出符是专门用来帮我记录下业务ID.业务类型的.比如,“businessID:328593,businessType: o ...

  2. SQL Server自动化运维系列——监控跑批Job运行状态(Power Shell)

    需求描述 在我们的生产环境中,大部分情况下需要有自己的运维体制,包括自己健康状态的检测等.如果发生异常,需要提前预警的,通知形式一般为发邮件告知. 在上一篇文章中已经分析了SQL SERVER中关于邮 ...

  3. SQL Server自动化运维系列——批量执行SQL脚本(Power Shell)

    需求描述 一般在生产环境中,在投产的情况下,需要批量的来执行SQL脚本文件,来完成整个投产,如果投产文件比较多的情况下,无疑这是一个比较痛苦的过程,所以本篇通过PowerShell脚本来批量完成. 监 ...

  4. 关于linux,我们应该学什么?

    为了系统地学习一下linux相关知识,我花了三天的时间看完了那本经典的<鸟哥的linux私房菜>.所谓的经典必然是有其过人之处,而看完这本书之后,我毫无疑问地加入了强烈推荐这本书的行列. ...

  5. sql server之ROW_NUMBER() OVER()取每组的第N行数据

    先看个例子: document_id card_holder_id created_date document_type_id 1 1 2015-7-1 1 2 4 2015-7-2 1 3 4 20 ...

  6. markdown简要说明显示样式

    markdown 什么是markdown:     Markdown是一种可以使用普通文本编辑器编写的标记语言,通过简单的标记语法,它可以使普通文本内容具有一定的格式.   Markdown具有一系列 ...

  7. 网络抓包工具-Wireshark学习资料

    wireshark一个非常牛逼的网络抓包工具.转载一系列博文 一站式学习Wireshark(一):Wireshark基本用法 一站式学习Wireshark(二):应用Wireshark观察基本网络协议 ...

  8. Hadoop op 1)

    设置yarn.scheduler.fair.user-as-default-queue =fasle, 就会阻止每一个用户使用自己默认的队列. 设置yarn.scheduler.fair.allow- ...

  9. BZOJ 3524: [Poi2014]Couriers [主席树]

    3524: [Poi2014]Couriers Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 1892  Solved: 683[Submit][St ...

  10. sicily vector有序插入

    实现了简单的vector有序插入,这个题目值得注意的点是1.当vector为空时,需要判断再排除 2.迭代器的使用是此段代码的特点 int insertVector(vector<int> ...