Spring AOP潜入易懂的讲解
为什么会有面向切面编程(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();
}
}
测试结果:
静态代理虽然保证了业务类只需关注逻辑本身,代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理。再者,如果增加一个方法,除了实现类需要实现这个方法外,所有的代理类也要实现此方法。增加了代码的维护成本。那么要如何解决呢?答案是使用动态代理。
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 = ;
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;
}
}
测试结果如下:
在运行测试类中,创建测试类对象代码中: 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是方法级别的(要测试的方法不能为static修饰,因为接口中不能存在静态方法,编译就会报错)。
3.AOP可以分离业务代码和关注点代码(重复代码),在执行业务代码时,动态的注入关注点代码。切面就是关注点代码形成的类。
4.spring AOP原理及实战
前文提到JDK代理和Cglib代理两种动态代理,优秀的Spring框架把两种方式在底层都集成了进去,我们无需担心自己去实现动态生成代理。那么,Spring是如何生成代理对象的?:
1.创建容器对象的时候,根据切入点表达式拦截的类,生成代理对象。
2.如果目标对象有实现接口,使用jdk代理。如果目标对象没有实现接口,则使用Cglib代理。然后从容器获取代理后的对象,在运行期植入"切面"类的方法。通过查看Spring源码,我们在DefaultAopProxyFactory类中,找到这样一段话。
简单的从字面意思看出,如果有接口,则使用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配置文件:
xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="userDao" class="test.spring_aop_anno.UserDao">bean> <bean id="orderDao" class="test.spring_aop_anno.OrderDao">bean> <bean id="transactionAop" class="test.spring_aop_anno.TransactionAop">bean> <aop:config> <aop:pointcut expression="execution(* test.spring_aop_anno.*Dao.*(..))" id="transactionPointcut"/> <aop:aspect ref="transactionAop"> <aop:around method="arroud" pointcut-ref="transactionPointcut"/> <aop:before method="beginTransaction" pointcut-ref="transactionPointcut" /> <aop:after method="commit" pointcut-ref="transactionPointcut"/> <aop:after-returning method="afterReturing" pointcut-ref="transactionPointcut"/> <aop:after-throwing method="afterThrowing" pointcut-ref="transactionPointcut"/>
aop:aspect>
aop:config>
beans>
切入点表达式不在这里介绍。ref:Spring AOP 切入点表达式:http://blog.csdn.net/keda8997110/article/details/50747923
代码的测试结果如下:
到这里,我们已经全部介绍完Spring AOP,回到开篇的问题,我们拿它做什么?
1.Spring声明式事务管理配置。
2.Controller层的参数校验。
3.使用Spring AOP实现MySQL数据库读写分离案例分析
4.在执行方法前,判断是否具有权限。
5.对部分函数的调用进行日志记录。监控部分重要函数,若抛出指定的异常,可以以短信或邮件方式通知相关人员。
6.信息过滤,页面转发等等功能,博主一个人的力量有限,只能列举这么多,欢迎评论区对文章做补充。
原文:my.oschina.net/liughDevelop/blog/1457097
Spring AOP潜入易懂的讲解的更多相关文章
- Spring AOP前置通知实例讲解与AOP详细解析
一.引出问题 有个接口TestServiceInter,有两个实现方法TestService和Test2Service.他们都有sayHello():我们的需求是在调用这两个方法之前,要先完成写日志的 ...
- Spring入门(十):Spring AOP使用讲解
1. 什么是AOP? AOP是Aspect Oriented Programming的缩写,意思是:面向切面编程,它是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. 可以认为AOP是 ...
- Spring AOP详解
一.前言 在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一下什么是aop具体怎么用,看到了一篇博文写得还不错,就转载来学习一下,博文地址:http://www.cnbl ...
- 基于 Annotation 拦截的 Spring AOP 权限验证方法
基于 Annotation 拦截的 Spring AOP 权限验证方法 转自:http://www.ibm.com/developerworks/cn/java/j-lo-springaopfilte ...
- Spring AOP在函数接口调用性能分析及其日志处理方面的应用
面向切面编程可以实现在不修改原来代码的情况下,增加我们所需的业务处理逻辑,比如:添加日志.本文AOP实例是基于Aspect Around注解实现的,我们需要在调用API函数的时候,统计函数调用的具体信 ...
- 基于@AspectJ配置Spring AOP之一--转
原文地址:http://tech.it168.com/j/2007-08-30/200708302209432.shtml 概述 在低版本Spring中定义一个切面是比较麻烦的,需要实现特定的接口,并 ...
- TinyFrame再续篇:整合Spring AOP实现日志拦截
上一篇中主要讲解了如何使用Spring IOC实现依赖注入的.但是操作的时候,有个很明显的问题没有解决,就是日志记录问题.如果手动添加,上百个上千个操作,每个操作都要写一遍WriteLog方法,工作量 ...
- TinyFrame尾篇:整合Spring AOP实现用户认证
创建Manager用户验证表 这一篇主要讲解使用AOP对用户操作进行验证,如果通过验证,则继续执行,反之,则不能执行.其思想和上一篇完全一致. 由于需要用到用户认证,所以我们新建一个Manager实体 ...
- spring ioc 原理 spring aop原理
大家一直都说spring的IOC如何如何的强大,其实我倒觉得不是IOC如何的强大,说白了IOC其实也非常的简单.我们先从IOC说起,这个概念其实是从我们平常new一个对象的对立面来说的,我们平常使用对 ...
随机推荐
- luogu题解 P3629 【[APIO2010]巡逻】树的直径变式
题目链接: https://www.luogu.org/problemnew/show/P3629 分析 最近被众多dalao暴虐,这道题傻逼地调了两天才知道错哪 不过这题比较良心给你一个容易发现性质 ...
- Docker本地镜像发布到阿里云和从阿里云拉取镜像
登录阿里云官网,找到容器镜像服务 进入镜像仓库,创建仓库 输入信息 选择本地仓库 这里我要将这个镜像提交到仓库 回到仓库列表,点击管理 docker login --username=cn丶moti ...
- js之数据类型(对象类型——构造器对象——日期)
Date对象是js语言中内置的数据类型,用于提供日期与时间的相关操作.学习它之前我们先了解一下什么是GMT,什么时UTC等相关的知识. GMT: 格林尼治标准时间(Greenwich Mean Tim ...
- windows 日志清理和设置
Windows日志路径 c:/windows/system32/winevt/logs 这里的日志文件可以ctrl+a 选中后使用shift+delete进行删除,删不掉的可以点击跳过. 在“管理 ...
- FlowPortal BPM 安装环境的配置
l 操作系统:Windows Server 2003 及以上: l IIS: 在Internet信息服务(IIS)管理器中将ISAPI和CGI限制全部设为“允许” l 需要安装.Net Fram ...
- AOP底层实现原理,动态代理如何动态
代理 指定另外一个主体代替原来的某个主体去执行某个事物 代理执行的人 需要代理的人 需要代理的事情是一定要做的 但是被代理的人没有时间或自己做的不专业 静态代理: 父母朋友帮忙物色找对象 代理人掌握需 ...
- 深入SpringBoot注解原理及使用
首先,先看SpringBoot的主配置类: @SpringBootApplication public class StartEurekaApplication { public static voi ...
- 【python】写csv文件时遇到的错误
1.错误 在许多文件中,写入csv文件时都加"wb",w指写入,b指二进制 如: csvwrite=csv.writer(open("output.csv",& ...
- linecache:高效的读取文本文件
介绍 可以很方便的读取文件 读取特定行 import linecache ''' 我们常用的序列的索引是从0开始的,但是linecache模块读取的文件行号是从1开始的 ''' # 表示读取C:\p ...
- python异步编程 (转载)
Python Async/Await入门指南 转自:https://zhuanlan.zhihu.com/p/27258289 本文将会讲述Python 3.5之后出现的async/await的使 ...