Spring AOP原理
来源:互联网 发布:tensorflow gpu 测试 编辑:程序博客网 时间:2024/06/16 13:26
AOP
AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
AOP技术恰恰相反,它利用一种称为”横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为”Aspect”,即切面。所谓”切面”,简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
使用”横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
AOP核心概念
1、横切关注点
对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
2、切面(aspect)
类是对物体特征的抽象,切面就是对横切关注点的抽象
3、连接点(joinpoint)
被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器
4、切入点(pointcut)
对连接点进行拦截的定义
5、通知(advice)
所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类
6、目标对象
代理的目标对象
7、织入(weave)
将切面应用到目标对象并导致代理对象创建的过程
8、引入(introduction)
在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段
Spring的动态代理有两种:一是JDK的动态代理;另一个是cglib动态代理(通过修改字节码来实现代理)。今天咱们主要讨论JDK动态代理的方式。JDK的代理方式主要就是通过反射跟动态编译来实现的,下面咱们就通过代码来看看它具体是怎么实现的。
假设我们要对下面这个用户管理进行代理:
//用户管理接口
package com.tgb.proxy;
public interface UserMgr {
void addUser();
void delUser();
}
//用户管理的实现
package com.tgb.proxy;
public class UserMgrImpl implements UserMgr {
@Override public void addUser() { System.out.println("添加用户....."); } @Override public void delUser() { System.out.println("删除用户....."); }
}
按照代理模式的实现方式,肯定是用一个代理类,让它也实现UserMgr接口,然后在其内部声明一个UserMgrImpl,然后分别调用addUser和delUser方法,并在调用前后加上我们需要的其他操作。但是这样很显然都是写死的,我们怎么做到动态呢?别急,接着看。 我们知道,要实现代理,那么我们的代理类跟被代理类都要实现同一接口,但是动态代理的话我们根本不知道我们将要代理谁,也就不知道我们要实现哪个接口,那么要怎么办呢?我们只有知道要代理谁以后,才能给出相应的代理类,那么我们何不等知道要代理谁以后再去生成一个代理类呢?想到这里,我们好像找到了解决的办法,就是动态生成代理类!
这时候我们亲爱的反射又有了用武之地,我们可以写一个方法来接收被代理类,这样我们就可以通过反射知道它的一切信息——包括它的类型、它的方法等等
JDK动态代理的两个核心分别是InvocationHandler和Proxy,下面我们就用简单的代码来模拟一下它们是怎么实现的:
InvocationHandler接口:
package com.tgb.proxy;
import java.lang.reflect.Method;
public interface InvocationHandler {
public void invoke(Object o, Method m);
}
实现动态代理的关键部分,通过Proxy动态生成我们具体的代理类:
package com.tgb.proxy;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;
public class Proxy {
/**
*
* @param infce 被代理类的接口
* @param h 代理类
* @return
* @throws Exception
*/
public static Object newProxyInstance(Class infce, InvocationHandler h) throws Exception {
String methodStr = “”;
String rt = “\r\n”;
//利用反射得到infce的所有方法,并重新组装 Method[] methods = infce.getMethods(); for(Method m : methods) { methodStr += " @Override" + rt + " public "+m.getReturnType()+" " + m.getName() + "() {" + rt + " try {" + rt + " Method md = " + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt + " h.invoke(this, md);" + rt + " }catch(Exception e) {e.printStackTrace();}" + rt + " }" + rt ; } //生成Java源文件 String srcCode = "package com.tgb.proxy;" + rt + "import java.lang.reflect.Method;" + rt + "public class $Proxy1 implements " + infce.getName() + "{" + rt + " public $Proxy1(InvocationHandler h) {" + rt + " this.h = h;" + rt + " }" + rt + " com.tgb.proxy.InvocationHandler h;" + rt + methodStr + rt + "}"; String fileName = "d:/src/com/tgb/proxy/$Proxy1.java"; File f = new File(fileName); FileWriter fw = new FileWriter(f); fw.write(srcCode); fw.flush(); fw.close(); //将Java文件编译成class文件 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null); Iterable units = fileMgr.getJavaFileObjects(fileName); CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units); t.call(); fileMgr.close(); //加载到内存,并实例化 URL[] urls = new URL[] {new URL("file:/" + "d:/src/")}; URLClassLoader ul = new URLClassLoader(urls); Class c = ul.loadClass("com.tgb.proxy.$Proxy1"); Constructor ctr = c.getConstructor(InvocationHandler.class); Object m = ctr.newInstance(h); return m; }
}
这个类的主要功能就是,根据被代理对象的信息,动态组装一个代理类,生成Proxy1.java文件,然后将其编译成Proxy1.class。这样我们就可以在运行的时候,根据我们具体的被代理对象生成我们想要的代理类了。这样一来,我们就不需要提前知道我们要代理谁。也就是说,你想代理谁,想要什么样的代理,我们就给你生成一个什么样的代理类。
然后,在客户端我们就可以随意的进行代理了。
package com.tgb.proxy;
public class Client {
public static void main(String[] args) throws Exception {
UserMgr mgr = new UserMgrImpl();
//为用户管理添加事务处理 InvocationHandler h = new TransactionHandler(mgr); UserMgr u = (UserMgr)Proxy.newProxyInstance(UserMgr.class,h); //为用户管理添加显示方法执行时间的功能 TimeHandler h2 = new TimeHandler(u); u = (UserMgr)Proxy.newProxyInstance(UserMgr.class,h2); u.addUser(); System.out.println("\r\n==========华丽的分割线==========\r\n"); u.delUser(); }
}
这里我写了两个代理的功能,一个是事务处理,一个是显示方法执行时间的代理,当然都是非常简单的写法,只是为了说明这个原理。当然,我们可以想Spring那样将这些AOP写到配置文件,因为之前那篇已经写了怎么通过配置文件注入了,这里就不重复贴了。 到这里,你可能会有一个疑问:你上面说,只要放到容器里的对象,都会有容器的公共服务,我怎么没看出来呢?好,那我们就继续看一下我们的代理功能:
事务处理:
package com.tgb.proxy;
import java.lang.reflect.Method;
public class TransactionHandler implements InvocationHandler {
private Object target; public TransactionHandler(Object target) { super(); this.target = target; } @Override public void invoke(Object o, Method m) { System.out.println("开启事务....."); try { m.invoke(target); } catch (Exception e) { e.printStackTrace(); } System.out.println("提交事务....."); }
}
从代码中不难看出,我们代理的功能里没有涉及到任何被代理对象的具体信息,这样有什么好处呢?这样的好处就是将代理要做的事情跟被代理的对象完全分开,这样一来我们就可以在代理和被代理之间随意的进行组合了。也就是说同一个功能我们只需要一个。同样的功能只有一个,那么这个功能不就是公共的功能吗?不管容器中有多少给对象,都可以享受容器提供的服务了。这就是容器的好处。
- spring aop 原理(转)
- spring aop 原理
- spring AOP原理
- spring的aop原理
- spring aop原理(转)
- spring AOP实现原理
- Spring的AOP原理
- spring AOP原理
- spring AOP原理
- spring ioc 、 aop原理
- 浅析Spring AOP原理
- Spring AOP原理
- Spring aop(原理)
- Spring AOP 实现原理
- Spring AOP 实现原理
- Spring AOP 原理
- Spring Aop原理
- Spring Aop 实现原理
- 对web前端的认知
- Android--新一代开源Android渠道包生成工具Walle
- lua 函数初识(一)
- Windows Server 2003下Apache Http Server升级到2.2.32,如何打补丁
- RadioGroup 的check(id)和他的子控件radiobutton 的setcheck(boolean)的区别
- Spring AOP原理
- PHP加速器:APC、eAccelerator、XCache比较
- 细说VLAN与Trunk .
- Spring+Redis Cluster3.0.x
- Android开发基础之Handler的使用
- link和@import的区别
- Full Screen Drag, Rotate, and Zoom——全屏、旋转和缩放
- 第一次写博客,很紧张
- MySQL sql优化