利用反射手写代码实现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基本概念以及基本用法介绍,以实际场景使用为主. 场景 我们通常有这样一个需求:打印后台接口请求的具体参数,打印接口请求的最终响应结果,以及记录哪个用户在什 ...
随机推荐
- Django入门
Django文档: https://docs.djangoproject.com/en/1.10/ref/ 一.简单创建app 1.1 命令行创建project和app. django-admin s ...
- Python检查xpath和csspath表达式是否合法
在做一个可视化配置爬虫项目时,需要配置爬虫的用户自己输入xpath和csspath路径以提取数据或做浏览器操作.考虑到用户的有时会输入错误的xpath或csspath路径,后台需要对其做合法性校验. ...
- 网页实时聊天之PHP实现websocket
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,bi ...
- 使用 python 获取 Linux 的 IP 信息(通过 ifconfig 命令)
我们可以使用 python 代码通过调用 ifconfig 命令来获取 Linux 主机的 IP 相关信息,包括:网卡名称.MAC地址.IP地址等. 第一种实现方式: #!/usr/bin/pytho ...
- [django]表格的添加与删除实例(可以借鉴参考)
自己并未采用任何表格插件,参考网上例子,自己编写出来的django网页实例,请各位参考! 首先看图做事,表格布局采用bootstrap,俗话说bootstrap橹多了就会css了,呵呵,下面看图: 上 ...
- Java基础知识笔记(一:修饰词、向量、哈希表)
一.Java语言的特点(养成经常查看Java在线帮助文档的习惯) (1)简单性:Java语言是在C和C++计算机语言的基础上进行简化和改进的一种新型计算机语言.它去掉了C和C++最难正确应用的指针和最 ...
- FeWeb基础之JavaScript简介
FeWeb基础之JavaScript简介 1.JavaScript的基本介绍 JavaScript是一种基于对象和事件驱动并具有安全性能的脚本语言,它是通过嵌入或调入在标准的HTML语言中实现的.Ja ...
- 基于.net开发chrome核心浏览器【七】
这是一个系列的文章,前面六篇文章的地址如下: 基于.net开发chrome核心浏览器[六] 基于.net开发chrome核心浏览器[五] 基于.net开发chrome核心浏览器[四] 基于.net开发 ...
- Eclipse导入MyEclipse创建的web项目报错的解决方法
将myeclipse中开发的动态web项目直接引入到eclipse中继续开发,Eclipse中会报项目有错,如下图
- Makefile 编写 tips
1.变量赋值 VARIABLE = value #在执行时扩展,允许递归扩展 VARIABLE := value #在定义时扩展 VARIABLE ?= value #只有在该变量为空时才设置该值 V ...