在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. sqlite limit offset

    limit 0,20 表示从第1条开始取20条数据 limit 20 offset 2  表示从第2条开始取出20条数据

  2. saltstack系列~第三篇

    一 简介:今天来聊聊sls文件的编写二 目的:通过编写特定的sls文件实现环境的初始化和固定软件的安装三 目录: /srv/salt/ 可以构造deploy文件夹四 基本构造:配置基本以yaml规范. ...

  3. SpringBoot2.x整合Redis实战 4节课

    1.分布式缓存Redis介绍      简介:讲解为什么要用缓存和介绍什么是Redis,新手练习工具 1.redis官网 https://redis.io/download          2.新手 ...

  4. java Comparable 和 Cloneable接口

    Comparable接口定义了compareTo方法,用于比较对象. 例如,在JavaAPI中,Integer.BigInteger.String以及Date类定义如下 Cloneable接口 Clo ...

  5. Arduino语法详解_含示例详解

    Arduino 的程序可以划分为三个主要部分:结构.变量(变量与常量).函数. 结构部分 一.结构 1.1 setup() 1.2 loop() 二.结构控制 2.1 if 2.2 if...else ...

  6. jQuery中对未来的元素绑定事件用bind、live or on

    对未来的元素绑定事件不能用bind, 1.可以用live代替,但是要注意jquery的版本,根据官方文档,从1.7开始就不推荐live和delegate了,1.9里就去掉live了. 2.推荐用on代 ...

  7. WPF开发中的多线程的问题

    今天帮助同事做了一个WPF版的多线程demo,分享给大家. 要实现的问题就是非主线程thread1 去后台不停的取新数据,当有新数据的时候就会展示到前台. 我给他做的demo实现一个按秒的计数器,随着 ...

  8. kafka系列十、kafka常用管理命令

    一.Topic管理 1.创建topic kafka-topics.sh --zookeeper 47.52.199.52:2181 --create --topic test-15 --replica ...

  9. IIS8.5 运行WCF

    背景 这是一个项目给其它项目提供接口,其实现在哪有用WCF的了,都是restful.不过.net在这方面还是不错,比java强些,java竟然很多采用自己解析xml方式来做Web服务.难以理解. 但是 ...

  10. 几种常用的Interpolator(插值器)的动画效果

    在实现动画的非线性变化的方法中,常用的一种是为动画添加插值器以改变视图的属性值,从而实现理想的动画效果.Interpolator使用相对简单,下面就只给出一些提供的插值器的默认效果. 在代码中:直接调 ...