Spring框架学习06——AOP底层实现原理
在Java中有多种动态代理技术,如JDK、CGLIB、Javassist、ASM,其中最常用的动态代理技术是JDK和CGLIB。
1、JDK的动态代理
JDK动态代理是java.lang.reflect.*包提供的方法,必须要借助一个接口才能产生代理对象,对于使用业务接口的类,Spring默认使用JDK动态代理实现AOP。
代码示例如下:
创建dao包,并创建StuDao接口和StuDaoImpl实现类,
StuDao接口
public interface StuDao {
public void add();
public void find();
public void update();
public void delete();
}
StuDaoImpl实现类
public class StuDaoImpl implements StuDao {
@Override
public void add() {
System.out.println("添加学生");
} @Override
public void find() {
System.out.println("查询学生");
} @Override
public void update() {
System.out.println("修改学生");
} @Override
public void delete() {
System.out.println("删除学生");
}
}
创建aspect包,并创建切面类MyAspect,该类中可以定义多个通知,即增强处理的方法,示例代码如下:
public class MyAspect {
public void check(){
System.out.println("模拟权限控制");
}
public void except(){
System.out.println("模拟异常处理");
}
public void log(){
System.out.println("模拟日志记录");
}
public void monitor(){
System.out.println("模拟性能检测");
}
}
创建proxy包,并创建代理类MyJdkProxy,在JDK动态代理中代理类必须实现java.lang.reflect.InvocationHandler接口,并编写代理方法,在代理方法中需要通过Proxy实现动态代理。示例代码如下:
package com.aop.proxy; import com.aop.aspect.MyAspect;
import com.aop.dao.StuDao;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; public class MyJdkProxy implements InvocationHandler { //声明目标类接口对象(真实对象)
private StuDao stuDao; public MyJdkProxy(StuDao stuDao){
this.stuDao = stuDao;
} //创建代理的方法,建立代理对象和真实对象的代理关系,返回代理对象
public Object createProxy(){
//1.类加载器
ClassLoader cld = MyJdkProxy.class.getClassLoader();
//2.被代理对象实现的所有接口
Class[] clazz = stuDao.getClass().getInterfaces();
return Proxy.newProxyInstance(cld,clazz,this);
} /**
* 代理的逻辑方法,所有动态代理类的方法调用都交给该方法处理
* @param proxy 被代理对象
* @param method 要执行的方法
* @param args 执行方法时需要的参数
* @return 返回代理结果
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//创建一个切面
MyAspect myAspect = new MyAspect();
//前增强
myAspect.check();
myAspect.except();
//在目标类上调用方法并传入参数,相当于调用stuDao中的方法
Object obj = method.invoke(stuDao,args);
//后增强
myAspect.log();
myAspect.monitor();
return obj;
}
}
创建测试类
@Test
public void testStu(){
//创建目标对象
StuDao stuDao = new StuDaoImpl();
//创建代理对象
MyJdkProxy myJdkProxy = new MyJdkProxy(stuDao);
//从代理对象中获取增强后的目标对象
//该对象是一个被代理的对象,它会进入代理的逻辑方法invoke中
StuDao stuDaoProxy = (StuDao) myJdkProxy.createProxy();
//执行方法
stuDaoProxy.add();
System.out.println("==================");
stuDaoProxy.update();
System.out.println("==================");
stuDaoProxy.delete();
}
运行结果
2、CGLIB的动态代理
JDK动态代理必须提供接口才能使用,对于没有提供接口的类,只能采用CGLIB动态代理。CGLIB采用非常底层的字节码技术,对指定的目标类生产一个子类,并对子类进行增强。在Spring Core 包中已经集成了CGLIB所需要的jar包,无需另外引入jar包。
示例代码如下:
创建目标类TestDao
public class TestDao {
public void save(){
System.out.println("保存方法");
}
public void modify(){
System.out.println("修改方法");
}
public void delete(){
System.out.println("删除方法");
}
}
创建切面类MyAspect,并在该类中定义多个通知
public class MyAspect {
public void check(){
System.out.println("模拟权限控制");
}
public void except(){
System.out.println("模拟异常处理");
}
public void log(){
System.out.println("模拟日志记录");
}
public void monitor(){
System.out.println("模拟性能检测");
}
}
创建代理类MyCglibProxy,并实现MethodInterceptor接口
package com.aop.proxy; import com.aop.aspect.MyAspect;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method; public class MyCglibProxy implements MethodInterceptor { /**
* 创建代理的方法,生成CGLIB代理对象
* @param target 目标对象,需要增强的对象
* @return 返回目标对象的CGLIB代理对象
*/
public Object createProxy(Object target){
//创建一个动态类对象,即增强类对象
Enhancer enhancer = new Enhancer();
//设置其父类
enhancer.setSuperclass(target.getClass());
//确定代理逻辑对象为当前对象
enhancer.setCallback(this);
return enhancer.create();
} /**
* 该方法会在程序执行目标方法时调用
* @param proxy 是CGLIB根据指定父类生成的代理对象
* @param method 是拦截方法
* @param args 拦截方法的参数数组
* @param methodProxy 方法的代理对象,用于执行父类的方法
* @return 返回代理结果
* @throws Throwable
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//创建一个切面
MyAspect myAspect = new MyAspect();
//前置增强
myAspect.check();
//目标方法执行,返回执行结果
Object obj = methodProxy.invokeSuper(proxy,args);
//后置增强
myAspect.log();
return obj;
}
}
创建测试类
@Test
public void test(){
//创建目标对象
TestDao testDao = new TestDao();
//创建代理对象
MyCglibProxy myCglibProxy = new MyCglibProxy();
//获取增强后的目标对象
TestDao testDaoAdvice = (TestDao) myCglibProxy.createProxy(testDao);
//执行方法
testDaoAdvice.save();
System.out.println("==================");
testDaoAdvice.modify();
System.out.println("==================");
testDaoAdvice.delete();
}
运行结果
3、动态代理注意事项
(1)程序中应优先对接口创建代理,便于程序解耦维护;
(2)使用final关键字修饰的方法不能被代理,因为无法覆盖
- JDK动态代理,是针对接口生成子类,接口中方法不能使用final修饰
- CGLIB是针对目标类生成子类,因此类或方法不能使用final修饰
(3)Spring只支持方法连接点,不提供属性连接点
Spring框架学习06——AOP底层实现原理的更多相关文章
- Spring框架IOC和AOP的实现原理(概念)
IoC(Inversion of Control) (1). IoC(Inversion of Control)是指容器控制程序对象之间的关系,而不是传统实现中,由程序代码直接操控.控制权由应用代码中 ...
- Spring框架IOC和AOP的实现原理
IoC(Inversion of Control) (1). IoC(Inversion of Control)是指容器控制程序对象之间的关系,而不是传统实现中,由程序代码直接操控.控制权由应用代码中 ...
- Spring框架学习05——AOP相关术语详解
1.Spring AOP 的基本概述 AOP(Aspect Oriented Programing)面向切面编程,AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视.事务管理.安全检查 ...
- spring框架学习(三)——AOP( 面向切面编程)
AOP 即 Aspect Oriented Program 面向切面编程 首先,在面向切面编程的思想里面,把功能分为核心业务功能,和周边功能. 所谓的核心业务,比如登陆,增加数据,删除数据都叫核心业务 ...
- spring框架学习(六)AOP
AOP(Aspect-OrientedProgramming)面向方面编程,与OOP完全不同,使用AOP编程系统被分为方面或关注点,而不是OOP中的对象. AOP的引入 在OOP面向对象的使用中,无可 ...
- Spring框架学习总结(上)
目录 1.Spring的概述 2.Spring的入门(IOC) 3.Spring的工厂类 4.Spring的配置 5.Spring的属性注入 6.Spring的分模块开发的配置 @ 1.Spring的 ...
- Spring框架学习笔记(1)
Spring 框架学习笔记(1) 一.简介 Rod Johnson(spring之父) Spring是分层的Java SE/EE应用 full-stack(服务端的全栈)轻量级(跟EJB比)开源框架, ...
- Spring框架学习一
Spring框架学习,转自http://blog.csdn.net/lishuangzhe7047/article/details/20740209 Spring框架学习(一) 1.什么是Spring ...
- Spring框架学习1
AnonymouL 兴之所至,心之所安;尽其在我,顺其自然 新随笔 管理 Spring框架学习(一) 阅读目录 一. spring概述 核心容器: Spring 上下文: Spring AOP ...
随机推荐
- sizeof strlen区别于联系
http://www.cnblogs.com/carekee/articles/1630789.html
- pythonic语法
b="$".join(str(x) for x in range(10)) a= 2 if 5<2 else 3 print (a)#a是3
- mysql binglog server的设置方法【原创】
MySQL备份数据都是MySQL备份+binlog,这样才能保证数据的完整性.下面就是利用mysqlbinlog搭建mysql binlog server,可以把binlog传到远程存储上. 试验环境 ...
- Shell脚本中执行sql语句操作mysql的5种方法【转】
对于自动化运维,诸如备份恢复之类的,DBA经常需要将SQL语句封装到shell脚本.本文描述了在Linux环境下mysql数据库中,shell脚本下调用sql语句的几种方法,供大家参考.对于脚本输出的 ...
- 13-JS中的面向对象
创建对象的几种常用方式 1.使用Object或对象字面量创建对象 2.工厂模式创建对象 3.构造函数模式创建对象 4.原型模式创建对象 1.使用Object或对象字面量创建对象 JS中最基本创建对象的 ...
- JDBC辅助类封装 及应用
一:代码图解: 二:配置文件: driverClassName=com.mysql.jdbc.Driver url=jdbc\:mysql\://127.0.0.1\:3306/xlzj_sh_new ...
- keras + tensorflow安装
先安装anaconda 一条指令:conda install keras 就可以把keras,tensorflow装好.
- jvm字节占用空间分析
一个对象实例占用了多少字节,消耗了多少内存?这样的问题在c或c++里使用sizeof()方法就可以得到明确答案,在java里好像没有这样的方法(java一样可以实现),不过通过jmap工具倒是可以查看 ...
- python接口自动化测试二十七:加密与解密MD5、base64
# MD5加密 # 由于MD5模块在python3中被移除# 在python3中使用hashlib模块进行md5操作 import hashlib def MD5(str): # 创建md5对象 hl ...
- python 全栈开发,Day2(in,while else,格式化输出,逻辑运算符,int与bool转换,编码)
一.in的使用 in 操作符用于判断关键字是否存在于变量中 a = '男孩wusir' print('男孩' in a) 执行输出: True in是整体匹配,不会拆分匹配. a = '男孩wusir ...