一、AOP的概念
AOP:全称是Aspect Oriented Programming,翻译过来就是面向切面编程,它是OOP(面向对象编程)的延伸。AOP技术能在运行期间动态代理实现程序统一的维护。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
作用场景:可以不修改源代码,对总体业务逻辑里面添加新的功能。比如查询数据库操作,我们有的数据可能只有管理员身份才能操作,那如果最开始的时候没有实现验证管理员身份这个功能,我们可以利用Aop不修改原有的源代码,添加一个验证的功能。
二、AOP术语
-
连接点:类里面有哪些方法可以被增强(介入其他功能),这些方法就叫做连接点。
-
切入点:真正被增强的方法。
-
通知:实际增强的逻辑部分叫做通知。
通知有几种类型:
(1)前置通知:切入点方法前面执行的操作。
(2)后置通知:切入点方法后面执行的操作。
(3)环绕通知:切入点前面后面都要执行的操作。
(4)异常通知:出现异常才执行的操作。
(5)最终通知:最后执行的操作,就像try catch里面最后执行的final。 -
切面:这是一个过程,就是把通知应用到切入点的过程。
-
目标对象:将要被代理的类。
三、JDK动态代理
实现步骤:
1、利用Proxy 类创建代理对象
Proxy是一个动态代理类 ,是一个实现在类创建时在运行时指定的接口列表的类,具有如下所述的行为。 代理接口是由代理类实现的接口。 代理实例是代理类的一个实例。 每个代理实例都有一个关联的调用处理程序对象,它实现了接口InvocationHandler 。 通过其代理接口之一的代理实例上的方法调用将被分派到实例调用处理程序的invoke方法,传递代理实例, java.lang.reflect.Method被调用方法的java.lang.reflect.Method对象以及包含参数的类型Object Object的数组。 调用处理程序适当地处理编码方法调用,并且返回的结果将作为方法在代理实例上调用的结果返回。
2、实现newProxyInstance方法
API中对这个方法的介绍如下:
想要了解这个方法就得知道参数列表里面是什么:
-loder:就是类加载器,可以通过 代理类.Class.getClassLoader()获取。AOP里面就是传入的目标对象的类加载器。
-interfaces:显而易见,这是接口的意思,在jdk动态代理时一个接口可能会有很多的实现类,那这个参数就是传入实现类接口的列表。
-h: InvocationHandler是由代理实例的调用处理程序实现的接口 。每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。我们需要实现这个接口创建代理对象,利用invoke写增强方法。
所以这个方法就是用来创建接口实现类的代理对象的。
3.编写JDK动态代理代码
- 编写接口,实现接口里面的方法。
- 实现InvocationHandler,创建代理对象,重写invoke增强方法
我写了一个StuDaoProxy类作为StuDao代理对象的类,将StuDao配置到该类里面才能增强。利用有参构造器可以传入StuDao。
public class StuDaoProxy implements InvocationHandler {
private StuDaoImpl stuDao;
public StuDaoProxy(StuDaoImpl stuDao) {
this.stuDao = stuDao;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法执行前逻辑
System.out.println("我是前置通知~~~");
//被增强方法
Object invoke = method.invoke(stuDao, args);
//方法执行后逻辑
System.out.println("我是后置通知~~~");
return invoke;
}
}
- 写测试类,看最后select方法是否增强
@Test
//测试JDK动态代理
public void stuDaoTest001(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
StuDaoImpl stuDao = applicationContext.getBean("stuDao", StuDaoImpl.class);
ClassLoader classLoader = StuDaoTest.class.getClassLoader();
Class[] interfaces = {StuDao.class};
StuDaoProxy stuDaoProxy = new StuDaoProxy(stuDao);
StuDao stu = (StuDao) Proxy.newProxyInstance(classLoader, interfaces, stuDaoProxy);
stu.select();
}
输出结果:
前置通知和后置通知都打印出来了,说明方法增强成功。当然这里只在控制台打印只是为了说明实现过程,后期就是在对应位置实现讹误逻辑的增强了。