在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底层实现原理的更多相关文章

  1. Spring框架IOC和AOP的实现原理(概念)

    IoC(Inversion of Control) (1). IoC(Inversion of Control)是指容器控制程序对象之间的关系,而不是传统实现中,由程序代码直接操控.控制权由应用代码中 ...

  2. Spring框架IOC和AOP的实现原理

    IoC(Inversion of Control) (1). IoC(Inversion of Control)是指容器控制程序对象之间的关系,而不是传统实现中,由程序代码直接操控.控制权由应用代码中 ...

  3. Spring框架学习05——AOP相关术语详解

    1.Spring AOP 的基本概述 AOP(Aspect Oriented Programing)面向切面编程,AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视.事务管理.安全检查 ...

  4. spring框架学习(三)——AOP( 面向切面编程)

    AOP 即 Aspect Oriented Program 面向切面编程 首先,在面向切面编程的思想里面,把功能分为核心业务功能,和周边功能. 所谓的核心业务,比如登陆,增加数据,删除数据都叫核心业务 ...

  5. spring框架学习(六)AOP

    AOP(Aspect-OrientedProgramming)面向方面编程,与OOP完全不同,使用AOP编程系统被分为方面或关注点,而不是OOP中的对象. AOP的引入 在OOP面向对象的使用中,无可 ...

  6. Spring框架学习总结(上)

    目录 1.Spring的概述 2.Spring的入门(IOC) 3.Spring的工厂类 4.Spring的配置 5.Spring的属性注入 6.Spring的分模块开发的配置 @ 1.Spring的 ...

  7. Spring框架学习笔记(1)

    Spring 框架学习笔记(1) 一.简介 Rod Johnson(spring之父) Spring是分层的Java SE/EE应用 full-stack(服务端的全栈)轻量级(跟EJB比)开源框架, ...

  8. Spring框架学习一

    Spring框架学习,转自http://blog.csdn.net/lishuangzhe7047/article/details/20740209 Spring框架学习(一) 1.什么是Spring ...

  9. Spring框架学习1

    AnonymouL 兴之所至,心之所安;尽其在我,顺其自然 新随笔 管理   Spring框架学习(一)   阅读目录 一. spring概述 核心容器: Spring 上下文: Spring AOP ...

随机推荐

  1. C - Musical Theme (后缀数组)

    题目链接:https://cn.vjudge.net/contest/283743#problem/C 题目大意:给你n个数组,然后问你是否有多个“相似”且不重叠的子串的长度大于等于5(两个子串相似当 ...

  2. JobService相关

    1.在构建JobInfo时,如果设置setPersisted(true),则需要应用申请RECEIVE_BOOT_COMPLETED权限. 这个权限只需要在应用首次安装时被授予,后面覆盖安装时便会默认 ...

  3. Maven入门-运行struts项目进行测试(三)

    maven运行struts项目进行测试: 在入门二中已经导入struts的jar包. 此时的pom.xml文件 <project xmlns="http://maven.apache. ...

  4. linux 中的 open() read() write() close() 函数

    1. open()函数 功能描述:用于打开或创建文件,在打开或创建文件时可以指定文件的属性及用户的权限等各种参数. 所需头文件:#include <sys/types.h>,#includ ...

  5. 【黑客免杀攻防】读书笔记7 - 软件逆向工程基础1(函数调用约定、Main函数查找)

    0x1 准备工作 1.1.准备工具 IDA:交互式反汇编工具 OllyDbg:用户层调试工具 Visual Studio:微软开发工具 1.2.基础知识 C++开发 汇编语言 0x2 查找真正的mai ...

  6. WPF中添加Winform用户自定义控件

    过程:创建WPF工程->创建Winform用户自定义控件工程->WPF中引用控件->添加到Xaml页面 1.首先在WPF工程的解决方案上右击选择添加新建项目: 选择Windows窗体 ...

  7. 配置mysql5.5主从复制、半同步复制、主主复制

    mysql主服务器 192.168.8.40 mysql从服务器 192.168.8.41 全新配置过程(主和从数据库都没有数据): 主从复制主服务器设置: 1.改server-id      2.启 ...

  8. 火狐mozilla官方ftp站点获取旧版本火狐的下载地址

    http://ftp.mozilla.org/pub/firefox/releases/

  9. Jquery属性选择器(同时匹配多个条件,与或非)(附样例)

    1. 前言 为了处理除了两项不符合条件外的选择,需要用到jquery选择器的多个条件匹配来处理,然后整理了一下相关的与或非的条件及其组合. 作为笔记记录. 2. 代码 <!DOCTYPE htm ...

  10. PHP魔术方法实例

    PHP中把以两个下划线__开头的方法称为魔术方法,这些方法在PHP中充当了举足轻重的作用. 魔术方法包括: __construct(),类的构造函数 __destruct(),类的析构函数 __cal ...