乐趣区

关于java:Spring-FrameWork从入门到NB-Spring-AOP实战

这篇文章的目标是对上一篇文章无关 AOP 概念的复习和坚固,通过实战的形式。

引入依赖

首先须要引入 Spring Aspect 依赖

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.5.RELEASE</version>
        </dependency>

@EnableAspectJAutoProxy

先申明一下,例子都应用注解形式实现,不必 xml 配置的形式了,其实区别不大,然而自己不喜爱 xml 配置的形式。

Configuration 文件中退出 @EnableAspectJAutoProxy 注解。

@Configuration()
@ComponentScan(value={"springAop"})
@EnableAspectJAutoProxy
@PropertySource("classpath:application.properties")
@EnableAsync
public class MyConfiguration {}

业务类

筹备一个业务类 UserService,指标就是为了实现 AOP 性能,所以业务类不须要实现任何业务逻辑:

public interface IUserService {public boolean addUser();
    public boolean deleteUser();
    public boolean updateUser();}

IUserService 的实现类 UserService,为了验证 AfterThrowing 类的 Advice,咱们将 updateUser 办法间接抛出异样,模仿业务类抛异样的场景:

package springAop;

import org.springframework.stereotype.Component;

@Component
public class UserService implements IUserService{
    @Override
    public boolean addUser() {System.out.println("add user in userService");
        return true;
    }

    @Override
    public boolean deleteUser() {System.out.println("delete user in userService");
        return false;
    }

    @Override
    public boolean updateUser() {throw new RuntimeException("cant be updated...");
    }
}

目前为止波及到的 Spring AOP 的概念:指标对象,也就是 Target object,本例中指标对象就是 UserService。

编写切面

编写一个用于记录日志的切面,咱们须要一步步意识切面类中波及到的 AOP 概念或术语。

上面用于解决日志切面的类 LogManagement 就是一个 Aspect:

@Aspect
@Component
public class LogManagement {}

对于 Spring AOP 来说,切面必须注入到 Spring IoC 容器中,所以也必须加 @Component 注解,以及,表明本人是切面类的 @Aspect 注解。

上面咱们给切面类加 pointcut:

    @Pointcut("execution(* springAop.UserService.add*())")
    public void addUserPointcut(){}

@Pointcut 注解示意其作用的办法 addUserPointcut 是一个切点,在满足切点定义的连贯条件的状况下会被 Spring AOP 调用到,去执行相应的 Advice。

@Pointcut 注解的参数:”execution( springAop.UserService.add())” 定义切点表达式,通过表达式指定以后切点与什么 JointPoint 关联,咱们这里指定为 SpringAop.UserService 类的以 add 结尾的办法,后面的 * 匹配 JointPoint 办法的返回值,* 示意任意返回值。

通过 @Pointcut 注解,咱们详单与定义了 AOP 的 JointPoint 和 Pointcut 两个个性。

接下来须要定义 Advices 了。

临时只定义一个 Before Advice:

@Before(value="addUserPointcut()")
    public void beforeLog(JoinPoint jp){System.out.println("before"+jp+"advice...");
    }

通过 @Before 注解指定,匹配到咱们后面定义好的 addUserPointcut。咱们在后面的文章中说过,除了 Around 类型的 Advice 能够参加指标对象的办法调用之外,其余类型的 Advice 只能加强办法调用、然而没有方法参加指标对象的办法调用,指标对象的办法调用交给 AOP 框架实现。

尽管没有方法参加指标对象的办法调用,然而 @Before Advice 能够有参数 JoinPoint,给 Advice 一个机会,能晓得以后 AOP 的加强调用匹配到的是哪一个 JoinPoint,正如咱们例子中看到的,咱们在 Advice 办法 beforeLog 中加了这个 JointPoint 参数。

筹备一下启动类:

public class App {public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfiguration.class);
        IUserService us = (IUserService)  applicationContext.getBean("userService");
//        UserService us = (UserService)  applicationContext.getBean("userService");
        System.out.println(us.getClass());
        us.addUser();
        us.deleteUser();
        try {us.updateUser();
        }catch (Exception e){}

运行启动类,看一下成果。

before execution(boolean springAop.IUserService.addUser()) advice...
add user in userService

Before Advice 曾经失效了。

其余类型的 Advice

当初咱们减少其余类型的 Advices:

package springAop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.jar.JarOutputStream;

@Aspect
@Component
public class LogManagement {@Pointcut("execution(* springAop.UserService.add*())")
    public void addUserPointcut(){}
    @Pointcut("execution(* springAop.UserService.update*())")
    public void updateUserPointcut(){}

    @Before(value="addUserPointcut()")
    public void beforeLog(JoinPoint jp){System.out.println("before"+jp+"advice...");
    }
    @After(value="addUserPointcut()")
    public void afterLog(JoinPoint jp){System.out.println("after"+jp+"advice......");
    }
    @Around(value="addUserPointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable{System.out.println("This is around" +pjp+",before process...");
        Object retVal=pjp.proceed();
        System.out.println("the return value after proceed is :"+retVal);
        System.out.println("This is around" +pjp+",after process...");
        return retVal;
    }

    @AfterReturning(value="addUserPointcut()")
    public void afterRreturning(JoinPoint jp) throws Throwable{System.out.println("This is after returning" +jp+"...");
    }

    @AfterThrowing(value="updateUserPointcut()")
    public void afterThrowing(JoinPoint jp) throws Throwable{System.out.println("This is after throwing" +jp+"...");
    }

}

运行启动类:

class com.sun.proxy.$Proxy34
 This is around execution(boolean springAop.IUserService.addUser()) ,before process...
before execution(boolean springAop.IUserService.addUser()) advice...
add user in userService
the return value after proceed is :true
 This is aroundexecution(boolean springAop.IUserService.addUser()) ,after process...
 after execution(boolean springAop.IUserService.addUser()) advice......
 This is after returningexecution(boolean springAop.IUserService.addUser()) ...
delete user in userService
 This is after throwingexecution(boolean springAop.IUserService.updateUser()) ...

剖析一下运行后果,能够得出如下重要论断:

  1. 从 Spring Ioc 容器中获取到的 UserService 对象曾经不是原对象了,而是代理对象 com.sun.proxy.$Proxy34
  2. 发现代理对象是 JDK Proxy 实现的,这是因为咱们的 UserService 类实现了 IUserService 接口,具备 JDK Proxy 的实现条件,如果咱们去掉 UserService 的 IUserService 接口、或者在配置类中关上 proxyTargetClass 选项:@EnableAspectJAutoProxy(proxyTargetClass = true),都能够更换为 Cglib 形式生成代理对象
  3. 一个办法上同时加了多了 Advice 之后的失效程序:around(执行指标办法前)-> before -> around 执行指标办法 -> after ->after returning。
  4. 额定的,篇幅起因咱们就不减少测试代码了:After 能够获取到 JointPoint 的任何返回(调用胜利或产生异样),而 AferRetuning 只能获取到失常返回。

如果咱们的一个办法有多个切面类失效的话,执行程序就会相似于 FilterChain 的剥洋葱的形式。

AfterThrowing

与其余类型的 Advice 不同的是,某些状况下,咱们在 AfterThrowing Advice 下可能想晓得业务类具体抛出了什么样的异样,从而在 AOP Advice 中针对不同类型的异样做有针对性的解决。

AOP 的 AfterThrowing Advice 提供了这个能力,批改下面例子中的 AfterThrowing Advice:

    @AfterThrowing(value="updateUserPointcut()",throwing="ex")
    public void afterThrowing(JoinPoint jp,Throwable ex){System.out.println("This is after throwing" +jp+"...");
        System.out.println(ex);
    }

从新执行启动类:

 This is after throwingexecution(boolean springAop.UserService.updateUser()) ...
java.lang.RuntimeException: cant be updated...

咱们在 UserService 的 updateUser 办法中抛出的异样,在 AOP Advice 中能够获取到,因而也就能够在 AOP 的 Advice 中做有针对性的解决。

此外,对异样的解决,比方捕捉还是抛出的最终决策,是由应用程序做成的,AOP 切面对于利用来说就是一个旁观者:对利用不做浸入式的解决(Around 除外,Around 具备浸入解决的能力,尽管不能扭转 JointPoint 的业务逻辑,然而能够扭转其运行后果、返回切面本人的后果)。

Advice 获取返回值

Aop 切面类的 after Advice 办法中能够获取到业务类的返回值,比方:

    @AfterReturning(value="addUserPointcut()",returning = "ret")
    public void afterRreturning(JoinPoint jp,Object ret) throws Throwable{System.out.println("This is after returning" +jp+"...");
        System.out.println("return :"+ret);
    }

执行启动类:

 This is after returningexecution(boolean        springAop.UserService.addUser()) ...
  return :true

Advice 参数解决

AOP 提供了切面类 Advice 中获取指标类 JointPoint 的参数的能力。

革新 IUserService 接口和 UserService 类,addUser 减少一个 userName 参数:

    @Override
    public boolean addUser(String userName) {System.out.println("add user in userService:"+userName);
        return true;
    }

革新切面类,Advice 接管参数:

 @Pointcut("execution(* springAop.UserService.add*(..)) && args(userName)")
    public void addUserPointcut(String userName){}

Advice 做相应的革新:

    @Before(value="addUserPointcut(userName)")
    public void beforeLog(JoinPoint jp,String userName){System.out.println("before"+jp+"advice..."+"and userName is :"+userName);
    }

执行启动类:

before execution(boolean springAop.UserService.addUser(String)) advice... and userName is :Zhang San

Advice 中获取到了应用层的 JointPoint 办法的参数。

OK,明天到此为止,通过实战形式重温了一遍 AOP 相干术语之后,有没有感觉到 AOP 的术语不再那么艰涩难懂了?

上一篇 Spring FrameWork 从入门到 NB – Spring AOP – 概念

退出移动版