Aop 引入了Aspect概念,用来以模块化的形式对系统中的横切关注点进行封装。Aspect相对于Aop,就相当于Class对于OOP(面向对象)。
Asprct 直接以java字节码的形式编译到java类中。
在运行期间,为相应的接口动态生成对应的代理对象(反射),可以将横切点逻辑封装到动态代理的InvocationHandler
,然后在运行器,根据横切点,将横切逻辑织入到相应的代理类中。
spring 默认采用此方案。
在程序运行期间,动态的构建字节码的class文件。我们可以为需要织入横切逻辑的类在运行期间,通过cglib为这些类生成相应子类,讲这些横切逻辑加到子类中,让应用程序在执行期间使用的是动态生成的子类。
spring在无法使用jdk动态代理的情况下,会使用cglib库的动态字节码增强实现aop功能扩展。
aop功能模块在织入oop中需要知道在系统的那些执行点上进行织入操作,我们将要在其之上进行织入操作的系统执行点称为joinpoint。
常见的Joinpoint类型.
方法调用
方法执行
字段设置
字段获取
异常处理
pointcut 的概念是joinpoint的表达方式,将横切逻辑织入当前系统的过程中,需要参照pointcut规定的joinpoint信息,才可以知道应该往系统的那些joinpoint织入横切逻辑。
直接指定
正则表达式
使用特定的pointcut表述语言
advice 是单一横切关注点逻辑载体,它代表将会织入Joinpoint的横切逻辑,根据advice在joinpoint位置时机的差异,分成多种形式。
Before Advice
是在joinpoint指定位置之前执行的Advice类型。
After Advice
在相应连接点之后执行的Advice类型包括以下三种
After Return Advice
只有当前joinpoint处执行流程正常完成,才会执行
Throw Advice
只有在抛出异常的情况下,才会执行
After Advice
无论执行过程中是否抛出异常,都会执行
Around Advice
可以在Joinpoint之前和之后都指定相应的逻辑,可以完成Before Advice和After Advice
Aspect 是对系统中横切点逻辑进行模块化封装的Aop概念实体,包含多个Pointcut以及相关Advice定义
Spring中使用@Aspect
的注解并结合普通pojo来声明Aspect
只有经过织入过程,以Aspect模块化的横切点才会集成到oop现存系统中,而完成织入过程中的类就是织入器,
spring 中ProxyFactory就是织入器
代理模式中代理处于访问者与被访问者之间,隔离了这两者之间的直接交互。代理能够处理访问的请求就不需要劳烦被访问者了,减少被访问者负担,此外代理最终要将访问请求转发给真正的被访问者,所以可以在请求之前或者后面加入特定的逻辑
SubjectProxy内部持有SubjectImpl,在进行请求时会将该请求转发给SubjectImpl,不过更多的是对请求添加访问限制。
在使用静态代理时,针对不一样的目标类型,我们要为其单独实现代理对象。而所加的横切逻辑都是一样的,而我们可以使用动态代理,可以为指定的接口在系统运行期间动态的生成代理对象。
其机制需要实现 proxy类和InvocationHandle接口
被代理对象cat
/**
* @description: 被代理的对象。不同的类型,但是只要横切逻辑一样即可
*/
public class Cat implements ICat{
@Override
public void eat() {
System.out.println("Cat eat fish");
}
@Override
public void saty() {
}
}
被代理对象 dog
/**
* @description: 被代理对象。不同的类型,但是只要横切逻辑一样即可
*/
public class Dog implements IDog{
@Override
public void eatFood() {
System.out.println("Dog eat dogfood");
}
@Override
public void play() {
System.out.println("sdfs");
}
}
实现统一切面逻辑
/**
* @description: 实现统一切面逻辑
*/
public class EatHandle implements InvocationHandler {
//被代理对象
private Object object;
public EatHandle(Object object){
this.object=object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//代理逻辑的实现
if(method.getName().startsWith("eat")){
eat();
return method.invoke(object,args);
}
return null;
}
public void eat(){
System.out.println("该吃饭了");
}
}
创建代理对象
@Test
public void t9(){
//创建cat代理对象
ICat iCatProxy = (ICat) Proxy.newProxyInstance(
Cat.class.getClassLoader(),
new Class[]{ICat.class},
new EatHandle(new Cat()) );
iCatProxy.eat();
//创建 Dog代理对象
IDog iDogProxy = (IDog) Proxy.newProxyInstance(org.example.Day04.Dog.class.getClassLoader(),
new Class[]{IDog.class},
new EatHandle(new org.example.Day04.Dog()));
iDogProxy.eatFood();
}
jdk 代理机制只能对实现相应Interface的类使用。
对目标对象进行继承扩展,为其生成相应的子类,将横切逻辑实现放到子类中,然后让系统使用目标对象的子类,我们需要借助cglib动态字节码生成库,在系统运行期间动态为目标对象生成相应的扩展子类。
这里需要实现Callback,我们会直接使用MethodInterceptor接口(扩展了callback接口)
实现切面逻辑
public class TestCallBack implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
if(method.getName().startsWith("eat")){
System.out.println("该吃饭了");
//此方法并不是反射
return methodProxy.invokeSuper(o,objects);
}
return null;
}
}
创建其子类
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(Cat.class);
//设置回调函数
enhancer.setCallback(new TestCallBack());
//创建子类
Cat proxy= (Cat) enhancer.create();
proxy.eat();
通过cglib的enhance为目标对象动态地生成一个子类,并将里面callback中的横切逻辑附加到了需要的对象