spring-aop

Spring框架-AOP

JDK动态代理:jdk动态代理要求目标对象必须实现接口,java语言提供三个类支持Proxy Method InvocationHandler

CGLIB动态代理:生成原理是生成目标类的子类,而子类是增强过的这个子类就是代理对象,所以目标类必须可以被继承(不可以是final类)

CGLIB经常被应用于框架中,其效率是高于JDK的

CGLIB动态代理:源码详解系列(一)--cglib动态代理的使用和分析 - 子月生 - 博客园 (cnblogs.com)

注意要使用:org.springframework.cglib.proxy包

依赖:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.5</version>
</dependency>
public class MyTest01 {
    private String name;
    private String password;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String myString() {
        return "MyTest01{" +
                "name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class LogInterceptor  implements MethodInterceptor {
    //需要注意一点,这里要通过proxy.invokeSuper来调用目标类的方法,而不是使用method.invoke,
    // 不然会出现栈溢出等问题。如果你非要调用method.invoke,你需要把目标类对象作为LogInterceptor的成员属性,
    // 在调用method.invoke时将它作为入参,而不是使用MethodInterceptor.intercept的入参 obj,
    // 但是,不推荐这么做,因为这样将无法享受到 cglib 代理类执行快的优势。
    //此处的o是指代理类(子类),不是目标类(基类)。method是代理类的method,objects是参数
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println(method.getName());
        System.out.println("调用前");
        Object result=methodProxy.invokeSuper(o,objects);
        System.out.println("调用后");
        return result;
    }
@Test
public void test02(){
    Enhancer enhancer=new Enhancer();
    enhancer.setSuperclass(MyTest01.class);
    enhancer.setCallback(new LogInterceptor());
    MyTest01 myTest01=(MyTest01)enhancer.create();
    myTest01.setName("geng");
    myTest01.setPassword("1234");
    System.out.println(myTest01.myString());
}

image-20211015140858039

AOP:面向切面编程 从动态的角度考虑程序的运行过程 底层是使用了动态代理来进行实现

A Aspect:切面 给目标对象增加的功能叫做切面,一般为非业务方法

O Orient:面向

P Programming:

AOP实现框架,在Spring内部实现了aop规范,主要是在事务处理的时候进行使用,但是由于过于笨重很少使用。

aspectJ:一个专门实现AOP的开源框架,spring中也集成了aspectJ

常见术语:

image-20211015145608038

image-20211015145723924

spring中aspectJ实现可以:1、使用xml配置文件2、使用注解

1、@Before2、AfterReturning3、@Around4、AfterThrowing5、@After

切入点表达式:

image-20211015141629988

常用的通配符:

​ *:0至多个任意字符

​ ..:用在方法参数中,表示任意多个参数

​ 用在包名后,表示当前包及其子包

​ +: 用在类名后表示当前类及其子类

​ 用在接口后,表示当前接口和其实现类

image-20211015145301057

依赖:

<!--aspectj框架-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aspects</artifactId>
  <version>5.2.5.RELEASE</version>
</dependency>

简单的一个实现样例:

@Aspect
@Component
public class MyAspectJ {
    @Before(value = "execution(public void com.geng.service.MyService.doSome())")
    public void dosome(){
        System.out.println("这是前置通知");
    }
}
@Service("myService")
public class MyServiceImpl implements MyService {
    @Override
    public void doSome() {
        System.out.println("这是doSome方法");
    }
}
<context:component-scan base-package="com.geng"/>
<aop:aspectj-autoproxy/>
@Test
public void test01(){
    ApplicationContext applicationContext=(ApplicationContext) new ClassPathXmlApplicationContext("ApplicationContext.xml");
    MyService myService=(MyService)applicationContext.getBean("myService");
    myService.doSome();
}

@Before前置通知

属性:value为切入点表达式,表示切面执行的位置

特点:

1、在目标方法执行之前执行2、不会影响目标方法的执行结果

使用要求:

1、公共方法2、方法没有返回值3、方法只可以使用特定参数(JoinPoint 类型)

JoinPoint可以获取目标方法的有效信息(如果有多个参数,他必须在第一个位置处)

@Before(value = "execution(public void com.geng.service.MyService.doSome(..))")
public void dosome(JoinPoint joinPoint){
    for(Object o:joinPoint.getArgs()){
        System.out.println(o);
    }
    System.out.println(joinPoint.getKind()+" "+joinPoint.getSignature()+" "+joinPoint.getTarget()+" "+joinPoint.getThis());
    //getTarget和getThis返回值相同
    //会导致递归调用
    /*MyService myService=(MyService)joinPoint.getThis();
    myService.doSome(1,"peng");*/
    System.out.println("这是前置通知");
}

@AfterReturning后置通知

除了value属性还有一个returning属性表明返回值的参数名称

特点:在目标方法执行之后执行2、可以操作目标方法的返回值

使用要求:

1、公共方法2、方法没有返回值3、方法只可以使用特定参数(JoinPoint 类型)和一个Object类型的参数表示目标方法的返回值。

@AfterReturning(value = "execution(public * com.geng.service..doSome02(..))",returning = "result")
public void doSome2(JoinPoint joinPoint,Object result){
    Object temp=result;
    for(Object o:joinPoint.getArgs()){
        System.out.println(o);
    }
    if(result!=null){
        String res=(String)result;
        result=res.toLowerCase();
        System.out.println(result==temp+" "+(String)result);
        //因为String类型是不可以改变的,所以引用值已经改变了(temp!=result)
        //所以目标方法的返回值并没有改变
    }

@Around环绕通知

使用要求:

1、公共方法2、有一个返回值3、方法参数固定(ProceedingJoinPoint)

这个ProceedingJoinPoint参数其实是JoinPoint的子类

环绕通知其实和动态代理类似

ProceedingJoinPoint对象的proceed方法就是目标方法

@AfterThrowing异常通知

使用要求:

1、public 2、没有返回值3、方法参数只能是JoinPoint和Exception

属性:

Value:切入点表达式

Throwing:自定义变量,表示目标方法的抛出的异常对象,和方法参数名必须一致

在目标方法抛出异常时候执行,可以做目标方法的监控程序。相当于try catch finally中的catch

@After最终通知

使用要求:

1、public2、没有返回值3、方法参数只能为JoinPoint

总会执行,相当于try catch finally中的finally

@PointCut辅助注解

当使用@PointCut定义在一个方法的上面,此时这个方法的名称就是切入点表达式的别名了。在其他的通知中,value属性中就可以使用这个别名,代替切入点表达式了。当然一般情况下这个使用@PointCut注解的方法是没有代码的,他只是一个工具。

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class Aspectj {
    @Pointcut(value="execution(public * *..SomService.doSome(..))")
    public void pointcut(){

    }
    @Before(value="pointcut()")
    public void doLog1(){
        System.out.println("这是一个前置通知");
    }
    @After(value="pointcut()")
    public void doLog2(){
        System.out.println("这是一个后置通知");
    }
}
<aop:aspectj-autoproxy  proxy-target-class="true" />
<!--当proxy-target-class的值被设置成true的时候,就会执行CGLIB动态代理,无论存不存在接口-->
posted on 2021-10-15 19:18  gyp666  阅读(41)  评论(0)    收藏  举报