AOP
-
AOP的全程是Aspect-Oriented Programming,即面向切面编程(也成面向方向编程),它是面向对象编程(OOP)的一种补充。
-
AOP的专业术语:
● Aspect(切面):在实际应用中,切面通常是指封装的用于横向插入系统功能(如事务、日志等)的类
● Joinpoint(连接点):在程序执行过程之中的某个阶段点,它实际上是对象的一个操作,例如方法的调用或异常的抛出。
● Pointcut(切入点):是指切面与程序流程的交叉点,即那些需要处理的连接点。通常在程序中,切入点指的是类或者方法名,如某个通知要应用到所有以add开头的方法中,那么所有满足这一规则的方法都是切入点。
● Advice(通知/增强处理):AOP框架在特定的切入点执行的增强处理,即在定义好的切入点处所要执行的程序代码。可以将其理解为切面类中的方法,它是切面的具体实现。
● Target Object(目标对象):是指所有被通知的对象,也称为被增强对象。如果AOP框架采用的是动态的AOP实现,那么该对象就是一个被代理的对象。
● Proxy(代理):将通知应用到目标对象之后,被动态创建的对象。
● Weaving(织入):将切面代码插入到目标对象上,从而生成代理对象的过程。 -
动态代理,Spring中的AOP代理可以是JDK代理,也可以是CHLIB代理。
● JDK动态代理
JDK动态代理是通过java.lang.reflect.Proxy类来实现的,我们可以调用Proxy类的newProxyInstance()方法来创建代理对象。对于使用业务接口的类,Spring默认会使用JDK动态代理来实现AOP。案例:
(1)创建一个名为chapter03的web项目,导入Spring框架所需Jar包到项目的lib目录中,并发布到类路径下。
(2)在src目录下,创建一个com.sinven.jdk包,在该包下创建接口UserDao,并在该接口中编写添加和删除的方法,如下:package com.sinven.jdk public interface UserDao{ void addUser(); void deleteUser(); }
(3)在com.sinven.jdk包中,创建UserDao接口的实现类UserDaoImpl,分别实现接口中的方法,并在每个方法中添加一条输出语句,如下:
package com.sinven.jdk; //目标类 public class UserDaoImpl implements UserDao{ @Override public void addUser(){ System.ou.println("添加用户"); } @Override public void deleteUser(){ System.ou.println("删除用户"); } }
需要注意的是,此案例中会将实现类UserDaoImpl作为目标类,对其中的方法进行增强处理
(4)在src目录下,创建一个com.sinven.aspect包,并在该包下创建切面类MyAspect,在该类中定义一个模拟权限检查的方法和一个模拟记录日志的方法,这两个方法就表示切面中的通知,如下:
package com.sinven.aspect;
public class MyAspect{
public void checkPermissions(){
System.out.println("模拟检查权限");
}
public void log(){
System.out.println("模拟记录日志");
}
}
(5)在com.sinven.jdk包下,创建代理类JdkProxy,该类需要实现InvocationHandler接口,并编写代理方法。在代理方法中,需要通过Proxy类实现动态代理,如下:
package com.sinven.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.sinven.aspect.MyAspect;
/**
* 代理类
* /
public class JdkProxy implements InvocationHandler{
//声明目标类接口
private UserDao userDao;
//创建代理方法
public Object createProxy(Userdao userDao){
this.userDao=userDao;
//1.类加载器
ClassLoader classLoader=JdkProxy.class.getClassLoader();
//2.被代理对象实现的所有接口
Class[] clazz=userDao.getClass().getInterfaces();
//3.使用代理类,进行增强,返回的是代理后的对象
return Proxy.newProxyInstance(classLoader, clazz, this);
}
/**
*所有动态代理类的方法调用都会交由 invoke() 方法处理
*proxy 被代理后的对象
*method 将要被执行的方法信息(反射)
*args 执行方法时需要的参数
*/
@Override
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
//声明切面
MyAspect myAspect=new MyAspect();
//前增强
myAspect.checkPermissions();
//在目标类上调用方法,并传入参数
Object obj=method.invoke(userDao,args);
//后增强
myAspect.log();
return obj;
}
}