这篇文章的目标是对上一篇文章无关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()) ...
剖析一下运行后果,能够得出如下重要论断:
- 从Spring Ioc容器中获取到的UserService对象曾经不是原对象了,而是代理对象com.sun.proxy.$Proxy34
- 发现代理对象是JDK Proxy实现的,这是因为咱们的UserService类实现了IUserService接口,具备JDK Proxy的实现条件,如果咱们去掉UserService的IUserService接口、或者在配置类中关上proxyTargetClass选项:@EnableAspectJAutoProxy(proxyTargetClass = true),都能够更换为Cglib形式生成代理对象
- 一个办法上同时加了多了Advice之后的失效程序:around(执行指标办法前)-> before -> around执行指标办法 -> after ->after returning。
- 额定的,篇幅起因咱们就不减少测试代码了: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 – 概念
发表回复