前言:上一篇博客自己动手编写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. J2EE基础之Web服务简介

    J2EE基础之Web服务简介 1.什么是Web服务? 在人们的日常生活中,经常会查询网页上某城市的天气信息,这些信息都是动态的.实时的,它是专业的气象站提供的一种服务.例如,在网上购物时,通常采用网上 ...

  2. css3实现循环执行动画,且动画每次都有延迟

    一.最终效果 需求:gift图片的小动画每隔2s执行一次. 需求就一句话,我们看一下实现过程. 二.实现过程 1.网页结构 <!DOCTYPE html> <html lang=&q ...

  3. ZBrush中该如何调节多个SubTool

    我们学习了人体的基本雕刻,了解了人体结构.比例.骨骼.肌肉对于人物模型雕刻的重要性.本节课对ZBrush中的"Transpose Master"功能进行讲解,这个插件是ZBrush ...

  4. Ural 1010. Discrete Function

    1010. Discrete Function Time limit: 1.0 secondMemory limit: 64 MB There is a discrete function. It i ...

  5. Shell(C++实现,CodeBlocks+GCC编译)

    程序效果: 只实现了login .cd .ls .cat 四个命令.而且只能在 Windows 下运行. 代码: //main.cpp 1 #include <iostream> #inc ...

  6. [No000079]罗辑思维2016.1.2日前的所有每日语音,python3做的网络爬虫

    源码地址:https://github.com/charygao/Download_the_LouJiSiWei 写过很久了,vision1.0里有不少bug,今天重新整理修改了一下,运行了一下,2个 ...

  7. android:exported 属性详解

    属性详解 标签: android 2015-06-11 17:47 27940人阅读 评论(7) 收藏 举报 分类: Android(95) 项目点滴(25) 昨天在用360扫描应用漏洞时,扫描结果, ...

  8. 洛谷P1111 修复公路

    题目背景 A地区在地震过后,连接所有村庄的公路都造成了损坏而无法通车.政府派人修复这些公路. 题目描述 给出A地区的村庄数N,和公路数M,公路是双向的.并告诉你每条公路的连着哪两个村庄,并告诉你什么时 ...

  9. HDU 3032 Nim or not Nim (sg函数)

    加强版的NIM游戏,多了一个操作,可以将一堆石子分成两堆非空的. 数据范围太大,打出sg表后找规律. # include <cstdio> # include <cstring> ...

  10. 可运行jar包的几种打包/部署方式

    java项目开发中,最终生成的jar,大概可分为二类,一类是一些通用的工具类(不包含main入口方法),另一类是可直接运行的jar包(有main入口方法),下面主要讲的是后者,要让一个jar文件可直接 ...