Spring(二)——AOP

1.概念

面向切面编程 即 新加的代码不会影响原来的,像切入一样。

2.AspectJ导入包

com.springsoure.net.sf.cglib

com.springsoure.org.aopalliance

com.springsoure.org.aspectj.weaver

spring-aop

spring-aspects

3.五种通知

  • before:前置通知,在一个方法执行前被调用,@Before
  • after: 在方法执行之后调用的通知,无论方法执行是否成功,即最终通知,@After
  • after-returning: 仅当方法成功完成后执行的通知,@AfterReturning
  • after-throwing: 在方法抛出异常退出时执行的通知,@AfterThrowing
  • around: 在方法执行之前和之后调用的通知,@Around

实现条件,用@Aspect修饰通知(切面)类,被通知类和通知类 都用@Component修饰,在XML文件里用扫描包全部扫一下就好了

使用@Aspect注解需要添加aop命名空间,然后用<aop:aspectj-autoproxy /> 开启自动代理功能。

对于要织入的通知需要用上面5种注解之一来织入,对于同一个方法可以有多个通知,注解的属性value 需要指明 修饰符-返回值-哪个包下的哪个类的哪个方法名-参数类型。解析切入点,例如

@Before(value = "execution(public void com.atguigu.spring.aop.Dog.say(int))")

@before修饰的是切面类里要织入的方法,而不是被通知的方法。

这类切面编程需要加载配置文件,通过IOC来实现,不可以直接new对象。

看一下代码:

package com.atguigu.spring.aop;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class MyAOP {
    //将方法指定为前置通知,需要加这个属性
    @Before(value = "execution(public void com.atguigu.spring.aop.Dog.say(int))")
    public void before() {
        System.out.println("前置通知");
    }
}

切面类MyAOP,用@Aspect修饰,表明切面编程,@Componet修饰表示用了IOC技术,在配置文件里能加载。

package com.atguigu.spring.aop;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component
public class Dog {
    public void say(int x) {
        System.out.println("狗叫"+x+"次");
    }
}

被通知类Dog,用@Componet修饰表示用了IOC技术,在配置文件里能加载。

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
 4      
 5     xmlns:p="http://www.springframework.org/schema/p"
 6     xmlns:util="http://www.springframework.org/schema/util"
 7     xmlns:context="http://www.springframework.org/schema/context"
 8     xmlns:aop="http://www.springframework.org/schema/aop"
 9     xsi:schemaLocation="
10     http://www.springframework.org/schema/beans 
11      http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
12      
13      http://www.springframework.org/schema/util
14      http://www.springframework.org/schema/util/spring-util.xsd
15      
16      http://www.springframework.org/schema/context 
17      http://www.springframework.org/schema/context/spring-context-4.3.xsd
18      
19      http://www.springframework.org/schema/aop
20      http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
21      
22      ">
23      
24      <context:component-scan base-package="com.atguigu.spring.aop" ></context:component-scan>
25     <!-- 开启自动代理功能,即启动注解声明功能-->
26     <aop:aspectj-autoproxy /> 
27     
28     
29     
30 </beans>

配置文件aop.xml中第8,19,20行是aop命名空间需要配置的东西。24行是扫描包,26行是启动注解声明功能。

package com.atguigu.spring.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

    public static void main(String[] args) {
        Dog dog=new Dog();
        dog.say(11);
        ApplicationContext ac=new ClassPathXmlApplicationContext("aop.xml");
        Dog dog2=ac.getBean("dog",Dog.class);
        dog2.say(110);
    }
}
/*通知
狗叫11次
前置通知
狗叫110次
*/

测试类Test的main方法用new和IOC的方法执行 类 被前置通知的方法,效果不同,其他通知类似如此。

4.切入点表达式

一个通知方法作用于很多个类或者多个方法,可以用*匹配。

例如@Before(value = "execution(* com.atguigu.spring.aop.*.*(..))");

修饰符和返回值合并成1个*,不可以用2个,所有类可以用一个*,所有方法可以用一个*,所有参数类型用[..]匹配。

可重用的切入点表达式是 在切面类里 使用一个返回值为void,方法体为空的方法例如myPointCut()来命名切入点,被通知方法需要加上@Before("myPointCut()")这样的注解,可以通过加JoinPoint类型的参数来查看被通知的类是哪个,被通知的方法是哪个。

示例如下:

package com.atguigu.spring.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class MyAOP {
    @Pointcut(value = "execution(* com.atguigu.spring.aop.*.*(..))")
    private void myPointCut() {
    }

    @Before(value = "myPointCut()")
    public void before() {
        System.out.println("这是前置通知");
    }
    
    @AfterThrowing(value="myPointCut()",throwing="e")
    public void afterThrowing(JoinPoint joinPoint,Throwable e) {
        System.out.println("被通知的类是:"+joinPoint.getTarget());
        System.out.println("被通知的方法是:"+joinPoint.getSignature().getName());
        System.out.println("异常了,这是异常通知"+e.getMessage());
    }
}

获取参数名:Object[] args=joinPoint.getArgs();

对于异常通知,参数类型也可以是Exception或者具体异常类型,但是要匹配。

对于环绕通知,参数类型必须是ProceedingJoinPoint,它是JoinPoint的子类。

5.切面优先级

对于同一连接点应用不止一个切面类时,除非明确确定,否则他们的优先级是不确定的。可以通过@Order(x)注解来指定,对于x越小的,优先级越高

6.通过XML配置文件声明切面类和通知方法

即不同在Java文件里写@Aspect,@Before之类的注解,全都在XML里写,例如

    <aop:config>
        <aop:aspect ref="MyAOP">
            <aop:pointcut expression="execution(* com.atguigu.spring.aop.*.*(..))" id="myPointCut"/>
                <aop:before method="myBefore" pointcut-ref="myPointCut"/>
        </aop:aspect>
        
    </aop:config>

看着都麻烦,还是用注解吧。


学习资源:B站尚硅谷Spring视频。

原文地址:https://www.cnblogs.com/shoulinniao/p/12737739.html