Spring AOP的动态代理

Spring AOP的动态代理

在业务处理代码中,通常都有日志记录、性能统计、安全控制、事务处理、异常处理等操作。尽管使用OOP可以通过封装或继承的方式达到代码的重用,但仍然存在同样的代码分散到各个方法中。因此,采用OOP处理日志记录等操作,不仅增加了开发者的工作量,而且提高了升级维护的困难。为了解决此类问题,AOP思想应运而生。AOP采取横向抽取机制,即将分散在各个方法中的重复代码提取出来,然后在程序编译或运行阶段,再将这些抽取出来的代码应用到需要执行的地方。这种横向抽取机制,采用传统的OOP是无法办到的,因为OOP实现的是父子关系的纵向重用。但是AOP不是OOP的替代品,而是OOP的补充,它们相辅相成。
在这里插入图片描述
1.切面
切面(Aspect)是指封装横切到系统功能(如事务处理)的类。
2.连接点
连接点(Joinpoint)是指程序运行中的一些时间点,如方法的调用或异常的抛出。
3.切入点
切入点(Pointcut)是指那些需要处理的连接点。在Spring AOP 中,所有的方法执行都是连接点,而切入点是一个描述信息,它修饰的是连接点,通过切入点确定哪些连接点需要被处理。

在这里插入图片描述4.通知(增强处理)
由切面添加到特定的连接点(满足切入点规则)的一段代码,即在定义好的切入点处所要执行的程序代码。可以将其理解为切面开启后,切面的方法。因此,通知是切面的具体实现。
5.引入
引入(Introduction)允许在现有的实现类中添加自定义的方法和属性。
6.目标对象
目标对象(Target Object)是指所有被通知的对象。如果AOP 框架使用运行时代理的方式(动态的AOP)来实现切面,那么通知对象总是一个代理对象。
7.代理
代理(Proxy)是通知应用到目标对象之后,被动态创建的对象。
8.组入
组入(Weaving)是将切面代码插入到目标对象上,从而生成代理对象的过程。根据不同的实现技术,AOP织入有三种方式:编译器织入,需要有特殊的Java编译器;类装载期织入,需要有特殊的类装载器;动态代理织入,在运行期为目标类添加通知生成子类的方式。Spring AOP框架默认采用动态代理织入,而AspectJ(基于Java语言的AOP框架)采用编译器织入和类装载期织入。

目前,Spring AOP中常用JDKCGLIB两种动态代理技术。

JDK动态代理

JDK动态代理是java.lang.reflect.*包提供的方式,它必须借助一个接口才能产生代理对象。因此,对于使用业务接口的类,Spring默认使用JDK动态代理实现AOP。

在这里插入图片描述

package dynamic.jdk;

import org.springframework.stereotype.Repository;

@Repository("testDao")
public class TestDaoImpl implements TestDao {

	@Override
	public void save() {
		// TODO Auto-generated method stub
		System.out.println("保存");
	}

	@Override
	public void modify() {
		// TODO Auto-generated method stub
		System.out.println("修改");

	}

	@Override
	public void delete() {
		// TODO Auto-generated method stub
		System.out.println("删除");

	}

}

package aspect;
//切面类,可以定义多个通知,即增强处理的方法
public class MyAspet {
	public void check() {
		System.out.println("模拟权限控制");

	}
	public void except() {
		System.out.println("模拟异常处理");

	}
	public void log() {
		System.out.println("模拟日志记录");

	}
	public void monitor() {
		System.out.println("性能监测");

	}
	
}

package dynamic.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import aspect.MyAspet;

public class JDKDynamicProxy implements InvocationHandler{
	//声明目标类接口对象(真实对象)
	private TestDao testDao;
	//创建代理的方法,建立代理对象和真实对象的代理关系,并返回代理对象
	public Object createProxy(TestDao testDao) {
		this.testDao=testDao;
		//1.类加载器
		ClassLoader cld=JDKDynamicProxy.class.getClassLoader();
		//2.被代理对象实现的所有接口
		Class[] clazz=testDao.getClass().getInterfaces();
		//3.使用代理类进行增强,返回代理后的对象
		return Proxy.newProxyInstance(cld, clazz, this);
	}
	/**
	 * 代理的逻辑方法,所有动态代理类的方法调用,都交给该方法处理
	 * proxy被代理对象
	 * method将要被执行的方法信息
	 * args执行方法时重要的参数
	 * return代理结果
	 * **/
@Override
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
	//创建一个切面
	MyAspet myAspet=new MyAspet();
	//前增强
	myAspet.check();
	myAspet.except();
	//在目标类上调用方法,并传入参数,相当于调用testDao中的方法
	Object obj=method.invoke(testDao, args);
	//后增强
	myAspet.log();
	myAspet.monitor();
	return obj;
}
}

package dynamic.jdk;

public class JDKDynamicTest {
	public static void main(String[] args) {
		//创建代理对象
		JDKDynamicProxy jdkProxy=new JDKDynamicProxy();
		//创建目标对象
		TestDao testDao=new TestDaoImpl();
		//从代理对象中获取增强后的目标对象,该对象是一个被代理的对象,它会进入代理的逻辑方法invoke里
		TestDao testDaoAdvice=(TestDao) jdkProxy.createProxy(testDao);
		//执行方法
		testDaoAdvice.save();
		System.out.println("===========");
		testDaoAdvice.modify();
		System.out.println("===========");
		testDaoAdvice.delete();

	}
}

在这里插入图片描述

CGLIB动态代理

CGLIB(Code Generation Library)是一个高性能开源的代码生成包,采用非常底层的字节码技术,对指定的目标类生成一个子类,并对子类进行增强。在Spring Core包中已经集成了CGLIB所需要的JAR包,不需要另外导入JAR包。

在这里插入图片描述

package dynamic.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import aspect.MyAspet;

public class JDKDynamicProxy implements InvocationHandler{
	//声明目标类接口对象(真实对象)
	private TestDao testDao;
	//创建代理的方法,建立代理对象和真实对象的代理关系,并返回代理对象
	public Object createProxy(TestDao testDao) {
		this.testDao=testDao;
		//1.类加载器
		ClassLoader cld=JDKDynamicProxy.class.getClassLoader();
		//2.被代理对象实现的所有接口
		Class[] clazz=testDao.getClass().getInterfaces();
		//3.使用代理类进行增强,返回代理后的对象
		return Proxy.newProxyInstance(cld, clazz, this);
	}
	/**
	 * 代理的逻辑方法,所有动态代理类的方法调用,都交给该方法处理
	 * proxy被代理对象
	 * method将要被执行的方法信息
	 * args执行方法时重要的参数
	 * return代理结果
	 * **/
@Override
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
	//创建一个切面
	MyAspet myAspet=new MyAspet();
	//前增强
	myAspet.check();
	myAspet.except();
	//在目标类上调用方法,并传入参数,相当于调用testDao中的方法
	Object obj=method.invoke(testDao, args);
	//后增强
	myAspet.log();
	myAspet.monitor();
	return obj;
}
}

package dynamic.jdk;

public class JDKDynamicTest {
	public static void main(String[] args) {
		//创建代理对象
		JDKDynamicProxy jdkProxy=new JDKDynamicProxy();
		//创建目标对象
		TestDao testDao=new TestDaoImpl();
		//从代理对象中获取增强后的目标对象,该对象是一个被代理的对象,它会进入代理的逻辑方法invoke里
		TestDao testDaoAdvice=(TestDao) jdkProxy.createProxy(testDao);
		//执行方法
		testDaoAdvice.save();
		System.out.println("===========");
		testDaoAdvice.modify();
		System.out.println("===========");
		testDaoAdvice.delete();

	}
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值