1、引入依赖

    1. <!-- 引入spring-context -->
    2. <dependency>
    3. <groupId>org.springframework</groupId>
    4. <artifactId>spring-context</artifactId>
    5. <version>5.1.6.RELEASE</version>
    6. <!-- 引入spring-aspects -->
    7. <dependency>
    8. <groupId>org.springframework</groupId>
    9. <artifactId>spring-aspects</artifactId>
    10. <version>5.1.6.RELEASE</version>
    11. </dependency>
    12. <dependency>
    13. <groupId>org.springframework</groupId>
    14. <artifactId>spring-aop</artifactId>
    15. <version>5.1.6.RELEASE</version>
    16. </dependency>
    17. <dependency>
    18. <groupId>org.aspectj</groupId>
    19. <artifactId>aspectjrt</artifactId>
    20. <version>1.8.3 </version>
    21. </dependency>
    22. <dependency>
    23. <groupId>org.aspectj</groupId>
    24. <artifactId>aspectjweaver</artifactId>
    25. <version>1.8.3 </version>
    26. </dependency>

    2、创建目标对象

    1. // 目标对象,UserService的实现类
    2. public class UserServiceImpl implements UserService{
    3. @Override
    4. public void register(User user){
    5. }
    6. @Override
    7. public boolean login(String name,String password){
    8. return true;
    9. }
    10. }
    11. // 在xml中配置
    12. <bean id="userService" class="xx.UserServiceImpl"/>

    3、定义通知类(添加额外功能):实现相应的接口使之成为相应的通知类

    • MethodBeforeAdvice:前置通知
    • AfterAdvice:后置通知
    • AfterReturningAdvice:有异常不执行,方法会因异常而结束,无返回值
    • ThrowsAdvice:异常通知
    • MethodInterceptor:环绕通知
      1. // 在目标方法执行之前运行额外功能
      2. public class Before implements MethodBeforeAdvice{
      3. /**
      4. * 参数解析:
      5. * Method:额外功能所增加给的那个原始方法,如本例为register,login
      6. * Object[]:额外功能所增加给的那个原始方法的参数,如String name,String password
      7. * Object:额外功能所增加给的那个原始对象
      8. */
      9. @Override
      10. public void before(Method method,Object[] args,Object target)
      11. Throws Throwable{
      12. System.out.println("--before--");
      13. }
      14. }
      15. // 配置
      16. <bean id="before" class="xx.Before"/>
      环绕通知接口的使用:
      1. // MethodInterceptor接口
      2. public class Around implements MethodInterceptor {
      3. /**
      4. * invoke方法的作用:额外功能写在invoke中,可以在目标方法的前、后、执行前后运行
      5. * 参数:MethodInvocation:额外功能所增加给的目标方法
      6. * 目标方法运行:Object ret = methodInvocation.proceed();
      7. * 返回值:目标方法执行后的返回值
      8. * @param methodInvocation
      9. * @return
      10. * @throws Throwable
      11. */
      12. @Override
      13. public Object invoke(MethodInvocation methodInvocation) throws Throwable {
      14. // 前置额外功能
      15. System.out.println("前置");
      16. Object ret = null;
      17. try {
      18. // 返回值
      19. ret = methodInvocation.proceed();
      20. // 后置额外功能
      21. System.out.println("后置");
      22. } catch (Throwable throwable){
      23. System.out.println("原始方法抛出异常,执行额外功能");
      24. throwable.printStackTrace();
      25. }
      26. return ret;
      27. }
      28. }

    4、引入 aop 命名空间:

    1. <beans xmlns:aop="http://www.springframework.org/schema/aop"
    2. xsi:schemaLocation="http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    3. </beans>

    5、定义切入点:

    切点表达式:根据表达式通配匹配切入点

    • 修饰符 返回值 包 方法名(参数表)

    注意事项:

    • 参数列表中的参数类需要使用全限定类名
    • * *.UserServiceImpl()*.匹配单级包
      * *..UserServiceImpl()*..匹配多级包
    • execution(* top.songfang.*.*(..)):第一个.*匹配当前包下(不包括子包)任意类,第二个*.匹配类中任意方法
      execution(* top.songfang..*.*(..)):第一个.*匹配包下(包括子包)任意类,第二个*.匹配类中任意方法

    切入点函数:

    1. execution:功能最全
    2. args:匹配参数,args(String,..)
    3. within:进行类、包切入点表达式的匹配,within(*..UserServiceImpl)
    4. @annotation:为具有特殊注解的类进行切入
    1. <aop:pointcut id="" expression="@annotation(注解定义的全限定名)"/>

    切入点的逻辑运算:整合多个切入点配合工作

    • and:同时满足两个表达式,注意不能用于同种类型的切入点函数
    • or:只要满足一个切入点表达式,可以使用同种类型的切入函数
    1. 匹配参数:execution(* *(top.songfang.entity.User))
    2. 匹配方法名:
      • 无参:execution(* save())
      • 任意参数:execution(* save(..))
    3. 匹配返回值类型:execution(top.songfang.entity.User *(..))
    4. 匹配类名:execution(* top.songfang.UserService.*(..))
    5. 匹配包名:execution(* top.songfang.*.*(..))
    6. 匹配包名、以及子包名:execution(* top.songfang..*.*(..))

      1. <!-- 编制 -->
      2. <aop:config>
      3. <!-- 切点定义 -->
      4. <!-- execution(修饰符 返回值 包.类.方法名(参数列表)) -->
      5. <aop:pointcut id="myPointerCut" expression="execution(* order(..))"/>
      6. <!-- 组装切面 -->
      7. <aop:advisor advice-ref="before" pointcut-ref="myPointerCut"/>
      8. </aop:config>

    6、组装切面:

    1. <aop:config>
    2. <aop:pointcut id="p" expression="execution (* *.add(..))"/>
    3. <aop:aspect ref="pojoAdvice">
    4. <aop:before method="before" pointcut-ref="p"></aop:before>
    5. <!--通过设置returning来将返回值传递给通知-->
    6. <aop:after-returning method="after" pointcut-ref="p" returning="returnval"/>
    7. <aop:around method="around" pointcut-ref="p"/>
    8. <!--通过设置returning来将异常对象传递给通知-->
    9. <aop:after-throwing method="afterThrowing" pointcut-ref="p" throwing="e"/>
    10. </aop:aspect>
    11. </aop:config>

    7、jdk 动态代理和 cglib 动态代理:

    • 目标业务类如果有接口则用JDK代理,没有接口则用CGLIB代理
    • 可以通过设置 来明确规定使用CGLIB代理