博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring AOP是什么?你都拿它做什么?
阅读量:6291 次
发布时间:2019-06-22

本文共 5292 字,大约阅读时间需要 17 分钟。

hot3.png

         对于最近博主最近写博客的兴致大发,我也在思考:为什么而写博客?在互联网时代,无论你是牛人大咖,还是小白菜鸟,都有发表自己看法的权利。无论你是对的还是错的,都会在这个平台上找到答案。所以,我会尽可能去写自己感兴趣的内容,无论正面或者负面的消息,都尽可能回复我的每一位读者。即使自己只有一个读者,也会坚持写下去。有一个平台,去表达自己,记录自己的点滴,难道不是一种快乐吗?同样,了解技术是一个深入和拓展的过程,需要一个人清晰严谨的逻辑思维。有时候,写博客更像是给自己做笔记,巩固分散的知识!

         为什么会有面向切面编程(AOP)?我们知道Java是一个面向对象(OOP)的语言,但它有一些弊端,比如当我们需要为多个不具有继承关系的对象引入一个公共行为,例如日志,权限验证,事务等功能时,只能在在每个对象里引用公共行为,这样做不便于维护,而且有大量重复代码。AOP的出现弥补了OOP的这点不足。

为了阐述清楚Spring AOP,我们从将以下方面进行讨论:

         1.代理模式。

         2.静态代理原理及实践。

         3.动态代理原理及实践。

         4.Spring AOP原理及实战。

1.代理模式。

         代理模式:为其他对象提供一种代理以控制对这个对象的访问。这段话比较官方,但我更倾向于用自己的语言理解:比如A对象要做一件事情,在没有代理前,自己来做,在对A代理后,由A的代理类B来做。代理其实是在原实例前后加了一层处理,这也是AOP的初级轮廓。

2.静态代理原理及实践。

         静态代理模式:静态代理说白了就是在程序运行前就已经存在代理类的字节码文件,代理类和原始类的关系在运行前就已经确定。废话不多说,我们看一下代码,为了方便阅读,博主把单独的class文件合并到接口中,读者可以直接复制代码运行:

package test.staticProxy;// 接口public interface IUserDao {	void save();	void find();}//目标对象class UserDao implements IUserDao{	@Override	public void save() {		System.out.println("模拟:保存用户!");	}	@Override	public void find() {		System.out.println("模拟:查询用户");	}}/**    静态代理          特点:	1. 目标对象必须要实现接口	2. 代理对象,要实现与目标对象一样的接口 */class UserDaoProxy implements IUserDao{	// 代理对象,需要维护一个目标对象	private IUserDao target = new UserDao();	@Override	public void save() {		System.out.println("代理操作: 开启事务...");		target.save();   // 执行目标对象的方法		System.out.println("代理操作:提交事务...");	}	@Override	public void find() {		target.find();	}}

测试结果:

                               135740_HepP_3577599.png

         静态代理虽然保证了业务类只需关注逻辑本身,代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理。再者,如果增加一个方法,除了实现类需要实现这个方法外,所有的代理类也要实现此方法。增加了代码的维护成本。那么要如何解决呢?答案是使用动态代理。

3.动态代理原理及实践。

         动态代理模式:动态代理类的源码是在程序运行期间通过JVM反射等机制动态生成,代理类和委托类的关系是运行时才确定的。实例如下:

package test.dynamicProxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;// 接口public interface IUserDao {	void save();	void find();}//目标对象 class UserDao implements IUserDao{	@Override	public void save() {		System.out.println("模拟: 保存用户!");	}	@Override	public void find() {		System.out.println("查询");	}}/** * 动态代理: *    代理工厂,给多个目标对象生成代理对象! * */class ProxyFactory {	// 接收一个目标对象	private Object target;	public ProxyFactory(Object target) {		this.target = target;	}	// 返回对目标对象(target)代理后的对象(proxy)	public Object getProxyInstance() {		Object proxy = Proxy.newProxyInstance(			target.getClass().getClassLoader(),  // 目标对象使用的类加载器			target.getClass().getInterfaces(),   // 目标对象实现的所有接口			new InvocationHandler() {			// 执行代理对象方法时候触发				@Override				public Object invoke(Object proxy, Method method, Object[] args)						throws Throwable {										// 获取当前执行的方法的方法名					String methodName = method.getName();					// 方法返回值					Object result = null;					if ("find".equals(methodName)) {						// 直接调用目标对象方法						result = method.invoke(target, args);					} else {						System.out.println("开启事务...");						// 执行目标对象方法						result = method.invoke(target, args);						System.out.println("提交事务...");					}					return result;				}			}		);		return proxy;	}}

测试结果如下:

                       091340_jIKA_3577599.png

在运行测试类中创建测试类对象代码中

IUserDao proxy = (IUserDao)new ProxyFactory(target).getProxyInstance();

其实是JDK动态生成了一个类去实现接口,隐藏了这个过程:

class $jdkProxy implements IUserDao{}

        使用jdk生成的动态代理的前提是目标类必须有实现的接口。但这里又引入一个问题,如果某个类没有实现接口,就不能使用JDK动态代理,所以Cglib代理就是解决这个问题的。

       Cglib是以动态生成的子类继承目标的方式实现,在运行期动态的在内存中构建一个子类,如下:

public class UserDao{}//Cglib是以动态生成的子类继承目标的方式实现,程序执行时,隐藏了下面的过程public class $Cglib_Proxy_class  extends UserDao{}

       Cglib使用的前提是目标类不能为final修饰。因为final修饰的类不能被继承。

       现在,我们可以看看AOP的定义:面向切面编程,核心原理是使用动态代理模式在方法执行前后或出现异常时加入相关逻辑。

       通过定义和前面代码我们可以发现3点:

         1.AOP是基于动态代理模式。

         2.AOP是方法级别的。

         3.AOP可以分离业务代码和关注点代码(重复代码),在执行业务代码时,动态的注入关注点代码。切面就是关注点代码形成的类。

4.spring AOP原理及实战。

         前文提到JDK代理和Cglib代理两种动态代理,优秀的Spring框架把两种方式在底层都集成了进去,我们无需担心自己去实现动态生成代理。那么,Spring是如何生成代理对象的?:

         1.创建容器对象的时候,根据切入点表达式拦截的类,生成代理对象。

         2.如果目标对象有实现接口,使用jdk代理。如果目标对象没有实现接口,则使用Cglib代理。然后从容器获取代理后的对象,在运行期植入"切面"类的方法。通过查看Spring源码,我们在DefaultAopProxyFactory类中,找到这样一段话。

110310_PnhR_3577599.png

         简单的从字面意思看出,如果有接口,则使用Jdk代理,反之使用Cglib,这刚好印证了前文所阐述的内容。Spring AOP综合两种代理方式的使用前提有会如下结论:如果目标类没有实现接口,且class为final修饰的,则不能进行Spring AOP编程!

         知道了原理,现在我们将自己手动实现Spring的AOP:

package test.spring_aop_anno;import org.aspectj.lang.ProceedingJoinPoint;public interface IUserDao {	void save();}//用于测试Cglib动态代理class OrderDao {	public void save() {		//int i =1/0;用于测试异常通知		System.out.println("保存订单...");	}}//用于测试jdk动态代理class UserDao implements IUserDao {	public void save() {		//int i =1/0;用于测试异常通知		System.out.println("保存用户...");	}}//切面类class TransactionAop {	public void beginTransaction() {		System.out.println("[前置通知]  开启事务..");	}	public void commit() {		System.out.println("[后置通知] 提交事务..");	}	public void afterReturing(){		System.out.println("[返回后通知]");	}	public void afterThrowing(){		System.out.println("[异常通知]");	}	public void arroud(ProceedingJoinPoint pjp) throws Throwable{		System.out.println("[环绕前:]");		pjp.proceed();    			   // 执行目标方法		System.out.println("[环绕后:]");	}}

Spring的xml配置文件:

切入点表达式不在这里介绍。ref:

代码的测试结果如下:

                092326_1F2Z_3577599.png

到这里,我们已经全部介绍完Spring AOP,回到开篇的问题,我们拿它做什么?

         1.Spring声明式事务管理配置,博主的另一篇文章:

         2.Controller层的参数校验。ref:

         3.

         4.在执行方法前,判断是否具有权限。

         5.,对部分函数的调用进行日志记录。监控部分重要函数,若抛出指定的异常,可以以短信或邮件方式通知相关人员。

         6.信息过滤,页面转发等等功能,博主一个人的力量有限,只能列举这么多,欢迎评论区对文章做补充。

     Spring AOP还能做什么,实现什么魔幻功能,就在于我们每一个平凡而又睿智的程序猿!

转载于:https://my.oschina.net/liughDevelop/blog/1457097

你可能感兴趣的文章
概率论与数理统计习题
查看>>
初学structs2,简单配置
查看>>
Laravel5.0学习--01 入门
查看>>
时间戳解读
查看>>
sbin/hadoop-daemon.sh: line 165: /tmp/hadoop-hxsyl-journalnode.pid: Permission denied
查看>>
@RequestMapping 用法详解之地址映射
查看>>
254页PPT!这是一份写给NLP研究者的编程指南
查看>>
《Data Warehouse in Action》
查看>>
String 源码浅析(一)
查看>>
Spring Boot 最佳实践(三)模板引擎FreeMarker集成
查看>>
Fescar 发布 0.2.3 版本,支持 Redis 和 Apollo
查看>>
Google MapReduce到底解决什么问题?
查看>>
CCNP-6 OSPF试验2(BSCI)
查看>>
Excel 2013 全新的图表体验
查看>>
openstack 制作大于2TB根分区自动扩容的CENTOS镜像
查看>>
Unbuntu安装遭遇 vmware上的Easy install模式
查看>>
几个常用的ASP木马
查看>>
python分析postfix邮件日志的状态
查看>>
Mysql-5.6.x多实例配置
查看>>
psutil
查看>>