乐趣区

关于spring:spring基础之四Spring-AOP介绍与使用

Spring AOP 介绍与应用

AOP:Aspect Oriented Programming 面向切面编程

OOP:Object Oriented Programming 面向对象编程

面向切面编程:基于 OOP 根底之上新的编程思维,OOP 面向的次要对象是类,而 AOP 面向的次要对象是切面,在解决日志、平安治理、事务管理等方面有十分重要的作用。AOP 是 Spring 中重要的外围点,尽管 IOC 容器没有依赖 AOP,然而 AOP 提供了十分弱小的性能,用来对 IOC 做补充。艰深点说的话就是在程序运行期间,将 某段代码动静切入 指定办法 指定地位 进行运行的这种编程形式。

1、AOP 的概念

为什么要引入 AOP?
Calculator.java

package com.mashibing.inter;
​
public interface Calculator {
​
 public int add(int i,int j);
​
 public int sub(int i,int j);
​
 public int mult(int i,int j);
​
 public int div(int i,int j);
}

MyCalculator.java

package com.mashibing.inter;
​
public class MyCalculator implements Calculator {public int add(int i, int j) {
 int result = i + j;
 return result;
 }
​
 public int sub(int i, int j) {
 int result = i - j;
 return result;
 }
​
 public int mult(int i, int j) {
 int result = i * j;
 return result;
 }
​
 public int div(int i, int j) {
 int result = i / j;
 return result;
 }
}

MyTest.java

public class MyTest {public static void main(String[] args) throws SQLException {MyCalculator myCalculator = new MyCalculator();
 System.out.println(myCalculator.add(1, 2));
 }
}

此代码非常简单,就是根底的 javase 的代码实现,此时如果须要增加日志性能应该怎么做呢,只能在每个办法中增加日志输入,同时如果须要批改的话会变得十分麻烦。

MyCalculator.java

package com.mashibing.inter;
​
public class MyCalculator implements Calculator {public int add(int i, int j) {System.out.println("add 办法开始执行,参数为:"+i+","+j);
 int result = i + j;
 System.out.println("add 办法开始实现后果为:"+result);
 return result;
 }
​
 public int sub(int i, int j) {System.out.println("sub 办法开始执行,参数为:"+i+","+j);
 int result = i - j;
 System.out.println("add 办法开始实现后果为:"+result);
 return result;
 }
​
 public int mult(int i, int j) {System.out.println("mult 办法开始执行,参数为:"+i+","+j);
 int result = i * j;
 System.out.println("add 办法开始实现后果为:"+result);
 return result;
 }
​
 public int div(int i, int j) {System.out.println("div 办法开始执行,参数为:"+i+","+j);
 int result = i / j;
 System.out.println("add 办法开始实现后果为:"+result);
 return result;
 }
}

能够思考将日志的解决形象进去,变成工具类来进行实现:

LogUtil.java

package com.mashibing.util;
​
import java.util.Arrays;
​
public class LogUtil {
​
 public static void start(Object ... objects){System.out.println("XXX 办法开始执行,应用的参数是:"+ Arrays.asList(objects));
 }
​
 public static void stop(Object ... objects){System.out.println("XXX 办法执行完结,后果是:"+ Arrays.asList(objects));
 }
}

MyCalculator.java

package com.mashibing.inter;
​
import com.mashibing.util.LogUtil;
​
public class MyCalculator implements Calculator {public int add(int i, int j) {LogUtil.start(i,j);
 int result = i + j;
 LogUtil.stop(result);
 return result;
 }
​
 public int sub(int i, int j) {LogUtil.start(i,j);
 int result = i - j;
 LogUtil.stop(result);
 return result;
 }
​
 public int mult(int i, int j) {LogUtil.start(i,j);
 int result = i * j;
 LogUtil.stop(result);
 return result;
 }
​
 public int div(int i, int j) {LogUtil.start(i,j);
 int result = i / j;
 LogUtil.stop(result);
 return result;
 }
}

依照上述形式形象之后,代码的确简略很多,然而大家应该曾经发现在输入的信息中并不蕴含具体的办法名称,咱们更多的是想要在程序运行过程中动静的获取办法的名称及参数、后果等相干信息,此时能够通过应用 动静代理 的形式来进行实现。

CalculatorProxy.java

package com.mashibing.proxy;
​
import com.mashibing.inter.Calculator;
​
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
​
/**
 * 帮忙 Calculator 生成代理对象的类
 */
public class CalculatorProxy {
​
 /**
 *
 *  为传入的参数对象创立一个动静代理对象
 * @param calculator 被代理对象
 * @return
 */
 public static Calculator getProxy(final Calculator calculator){
​
​
 // 被代理对象的类加载器
 ClassLoader loader = calculator.getClass().getClassLoader();
 // 被代理对象的接口
 Class<?>[] interfaces = calculator.getClass().getInterfaces();
 // 办法执行器,执行被代理对象的指标办法
 InvocationHandler h = new InvocationHandler() {
 /**
 *  执行指标办法
 * @param proxy 代理对象,给 jdk 应用,任何时候都不要操作此对象
 * @param method 以后将要执行的指标对象的办法
 * @param args 这个办法调用时外界传入的参数值
 * @return
 * @throws Throwable
 */
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 // 利用反射执行指标办法, 指标办法执行后的返回值
//                System.out.println("这是动静代理执行的办法");
 Object result = null;
 try {System.out.println(method.getName()+"办法开始执行,参数是:"+ Arrays.asList(args));
 result = method.invoke(calculator, args);
 System.out.println(method.getName()+"办法执行实现,后果是:"+ result);
 } catch (Exception e) {System.out.println(method.getName()+"办法出现异常:"+ e.getMessage());
 } finally {System.out.println(method.getName()+"办法执行完结了......");
 }
 // 将后果返回回去
 return result;
 }
 };
 Object proxy = Proxy.newProxyInstance(loader, interfaces, h);
 return (Calculator) proxy;
 }
}

咱们能够看到这种形式更加灵便,而且不须要在业务办法中增加额定的代码,这才是罕用的形式。如果想谋求完满的同学,还能够应用上述的日志工具类来欠缺。

LogUtil.java

package com.mashibing.util;
​
import java.lang.reflect.Method;
import java.util.Arrays;
​
public class LogUtil {
​
 public static void start(Method method, Object ... objects){//        System.out.println("XXX 办法开始执行,应用的参数是:"+ Arrays.asList(objects));
 System.out.println(method.getName()+"办法开始执行,参数是:"+ Arrays.asList(objects));
 }
​
 public static void stop(Method method,Object ... objects){//        System.out.println("XXX 办法执行完结,后果是:"+ Arrays.asList(objects));
 System.out.println(method.getName()+"办法开始执行,参数是:"+ Arrays.asList(objects));
​
 }
​
 public static void logException(Method method,Exception e){System.out.println(method.getName()+"办法出现异常:"+ e.getMessage());
 }
 
 public static void end(Method method){System.out.println(method.getName()+"办法执行完结了......");
 }
}

CalculatorProxy.java

package com.mashibing.proxy;
​
import com.mashibing.inter.Calculator;
import com.mashibing.util.LogUtil;
​
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
​
/**
 * 帮忙 Calculator 生成代理对象的类
 */
public class CalculatorProxy {
​
 /**
 *
 *  为传入的参数对象创立一个动静代理对象
 * @param calculator 被代理对象
 * @return
 */
 public static Calculator getProxy(final Calculator calculator){
​
​
 // 被代理对象的类加载器
 ClassLoader loader = calculator.getClass().getClassLoader();
 // 被代理对象的接口
 Class<?>[] interfaces = calculator.getClass().getInterfaces();
 // 办法执行器,执行被代理对象的指标办法
 InvocationHandler h = new InvocationHandler() {
 /**
 *  执行指标办法
 * @param proxy 代理对象,给 jdk 应用,任何时候都不要操作此对象
 * @param method 以后将要执行的指标对象的办法
 * @param args 这个办法调用时外界传入的参数值
 * @return
 * @throws Throwable
 */
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 // 利用反射执行指标办法, 指标办法执行后的返回值
//                System.out.println("这是动静代理执行的办法");
 Object result = null;
 try {LogUtil.start(method,args);
 result = method.invoke(calculator, args);
 LogUtil.stop(method,args);
 } catch (Exception e) {LogUtil.logException(method,e);
 } finally {LogUtil.end(method);
 }
 // 将后果返回回去
 return result;
 }
 };
 Object proxy = Proxy.newProxyInstance(loader, interfaces, h);
 return (Calculator) proxy;
 }
}

很多同学看到上述代码之后可能感觉曾经十分完满了,然而要阐明的是,这种动静代理的实现形式调用的是 jdk 的根本实现,如果须要代理的指标对象没有实现任何接口,那么是无奈为他创立代理对象的,这也是致命的缺点。而在 Spring 中咱们能够不编写上述如此简单的代码,只须要利用 AOP,就可能轻轻松松实现上述性能,当然,Spring AOP 的底层实现也依赖的是动静代理。

AOP 的外围概念及术语

  • 切面(Aspect): 指关注点模块化,这个关注点可能会横切多个对象。事务管理是企业级 Java 利用中无关横切关注点的例子。在 Spring AOP 中,切面能够应用通用类基于模式的形式(schema-based approach)或者在一般类中以 @Aspect 注解(@AspectJ 注解形式)来实现。

  • 连接点(Join point): 在程序执行过程中某个特定的点,例如某个办法调用的工夫点或者解决异样的工夫点。在 Spring AOP 中,一个连接点总是代表一个办法的执行。

  • 告诉(Advice): 在切面的某个特定的连接点上执行的动作。告诉有多种类型,包含“around”,“before”and“after”等等。告诉的类型将在前面的章节进行探讨。许多 AOP 框架,包含 Spring 在内,都是以拦截器做告诉模型的,并保护着一个以连接点为核心的拦截器链。

  • 切点(Pointcut): 匹配连接点的断言。告诉和切点表达式相关联,并在满足这个切点的连接点上运行(例如,当执行某个特定名称的办法时)。切点表达式如何和连接点匹配是 AOP 的外围:Spring 默认应用 AspectJ 切点语义。

  • 引入(Introduction): 申明额定的办法或者某个类型的字段。Spring 容许引入新的接口(以及一个对应的实现)到任何被告诉的对象上。例如,能够应用引入来使 bean 实现 IsModified接口,以便简化缓存机制(在 AspectJ 社区,引入也被称为外部类型申明(inter))。

  • 指标对象(Target object): 被一个或者多个切面所告诉的对象。也被称作被告诉(advised)对象。既然 Spring AOP 是通过运行时代理实现的,那么这个对象永远是一个被代理(proxied)的对象。

  • AOP 代理(AOP proxy):AOP 框架创立的对象,用来实现切面契约(aspect contract)(包含告诉办法执行等性能)。在 Spring 中,AOP 代理能够是 JDK 动静代理或 CGLIB 代理。

  • 织入(Weaving): 把切面连贯到其它的应用程序类型或者对象上,并创立一个被被告诉的对象的过程。这个过程能够在编译时(例如应用 AspectJ 编译器)、类加载时或运行时中实现。Spring 和其余纯 Java AOP 框架一样,是在运行时实现织入的。

AOP 的告诉类型
  • 前置告诉(Before advice): 在连接点之前运行但无奈阻止执行流程进入连接点的告诉(除非它引发异样)。

  • 后置返回告诉(After returning advice): 在连接点失常实现后执行的告诉(例如,当办法没有抛出任何异样并失常返回时)。

  • 后置异样告诉(After throwing advice): 在办法抛出异样退出时执行的告诉。

  • 后置告诉(总会执行)(After (finally) advice): 当连接点退出的时候执行的告诉(无论是失常返回还是异样退出)。

  • 盘绕告诉(Around Advice): 盘绕连接点的告诉,例如办法调用。这是最弱小的一种告诉类型,。盘绕告诉能够在办法调用前后实现自定义的行为。它能够抉择是否继续执行连接点或间接返回自定义的返回值又或抛出异样将执行完结。

AOP 的利用场景
  • 日志治理

  • 权限认证

  • 安全检查

  • 事务管制

2、Spring AOP 的简略配置

在上述代码中咱们是通过动静代理的形式实现日志性能的,然而比拟麻烦,当初咱们将要应用 spring aop 的性能实现此需要,其实艰深点说的话,就是把 LogUtil 的工具类换成另外一种实现形式。

1、增加 pom 依赖
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
 <dependency>
 <groupId>cglib</groupId>
 <artifactId>cglib</artifactId>
 <version>3.3.0</version>
 </dependency>
 <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
 <dependency>
 <groupId>org.aspectj</groupId>
 <artifactId>aspectjweaver</artifactId>
 <version>1.9.5</version>
 </dependency>
 <!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
 <dependency>
 <groupId>aopalliance</groupId>
 <artifactId>aopalliance</artifactId>
 <version>1.0</version>
 </dependency>
 <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
 <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-aspects</artifactId>
 <version>5.2.3.RELEASE</version>
 </dependency>
2、编写配置
  • 将指标类和切面类退出到 IOC 容器中,在对应的类上增加组件注解

    
    *   给 LogUtil 增加 @Component 注解
        
    *   给 MyCalculator 增加 @Service 注解
        
    *   增加主动扫描的配置
        
        <!-- 别忘了增加 context 命名空间 -->
        <context:component-scan base-package="com.mashibing"></context:component-scan>
        
  • 设置程序中的切面类

    
    *   在 LogUtil.java 中增加 @Aspect 注解
        
  • 设置切面类中的办法是什么时候在哪里执行

package com.mashibing.util;
    ​
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    ​
    import java.lang.reflect.Method;
    import java.util.Arrays;
    ​
    @Component
    @Aspect
    public class LogUtil {
    ​
     /*
     设置上面办法在什么时候运行
     @Before: 在指标办法之前运行:前置告诉
     @After: 在指标办法之后运行:后置告诉
     @AfterReturning: 在指标办法失常返回之后:返回告诉
     @AfterThrowing: 在指标办法抛出异样后开始运行:异样告诉
     @Around: 盘绕:盘绕告诉
    ​
     当编写完注解之后还须要设置在哪些办法上执行,应用表达式
     execution(拜访修饰符  返回值类型 办法全称)
     */
     @Before("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
     public static void start(){//        System.out.println("XXX 办法开始执行,应用的参数是:"+ Arrays.asList(objects));
    //        System.out.println(method.getName()+"办法开始执行,参数是:"+ Arrays.asList(objects));
     System.out.println("办法开始执行,参数是:");
     }
    ​
     @AfterReturning("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
     public static void stop(){//        System.out.println("XXX 办法执行完结,后果是:"+ Arrays.asList(objects));
    //        System.out.println(method.getName()+"办法执行完结,后果是:"+ Arrays.asList(objects));
     System.out.println("办法执行实现,后果是:");
    ​
     }
    ​
     @AfterThrowing("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
     public static void logException(){//        System.out.println(method.getName()+"办法出现异常:"+ e.getMessage());
     System.out.println("办法出现异常:");
     }
    ​
     @After("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
     public static void end(){//        System.out.println(method.getName()+"办法执行完结了......");
     System.out.println("办法执行完结了......");
     }
    }
  • 开启基于注解的 aop 的性能

<?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:context="http://www.springframework.org/schema/context"
     xmlns:aop="http://www.springframework.org/schema/aop"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context.xsd
     http://www.springframework.org/schema/aop
     https://www.springframework.org/schema/aop/spring-aop.xsd
    ">
     <context:component-scan base-package="com.mashibing"></context:component-scan>
     <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    </beans>
3、测试
MyTest.java

 import com.mashibing.inter.Calculator;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.support.ClassPathXmlApplicationContext;
 public class MyTest {public static void main(String[] args){ApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
 Calculator bean = context.getBean(Calculator.class);
 bean.add(1,1);
 }
}

spring AOP 的动静代理形式是 jdk 自带的形式,容器中保留的组件是代理对象 com.sun.proxy.$Proxy 对象

4、通过 cglib 来创立代理对象
MyCalculator.java

package com.mashibing.inter;
​
import org.springframework.stereotype.Service;
​
@Service
public class MyCalculator {public int add(int i, int j) {
 int result = i + j;
 return result;
 }
​
 public int sub(int i, int j) {
 int result = i - j;
 return result;
 }
​
 public int mult(int i, int j) {
 int result = i * j;
 return result;
 }
​
 public int div(int i, int j) {
 int result = i / j;
 return result;
 }
}

MyTest.java

public class MyTest {public static void main(String[] args){ApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
        MyCalculator bean = context.getBean(MyCalculator.class);
        bean.add(1,1);
        System.out.println(bean);
        System.out.println(bean.getClass());
    }
}

能够通过 cglib 的形式来创立代理对象,此时不须要实现任何接口,代理对象是

class com.mashibing.inter.MyCalculator$$EnhancerBySpringCGLIB$$1f93b605 类型

综上所述:在 spring 容器中,如果有接口,那么会应用 jdk 自带的动静代理,如果没有接口,那么会应用 cglib 的动静代理。动静代理的实现原理,后续会具体讲。

留神:
1、切入点表达式

在应用表达式的时候,除了之前的写法之外,还能够应用通配符的形式:

*:

1、匹配一个或者多个字符

execution(public int com.mashibing.inter.Myalculator.(int,int))

2、匹配任意一个参数,

execution(public int com.mashibing.inter.MyCalculator.(int,))

3、只能匹配一层门路,如果我的项目门路下有多层目录,那么 * 只能匹配一层门路

4、权限地位不能应用 *,如果想示意全副权限,那么不写即可

execution( com.mashibing.inter.MyCalculator.(int,*))

..:

1、匹配多个参数,任意类型参数

execution( com.mashibing.inter.MyCalculator.(..))

2、匹配任意多层门路

execution( com.mashibing..MyCalculator.(..))

在写表达式的时候,能够有 N 多种写法,然而有一种最偷懒和最准确的形式:

最偷懒的形式:execution( (..)) 或者 execution( .*(..))

最准确的形式:execution(public int com.mashibing.inter.MyCalculator.add(int,int))

除此之外,在表达式中还反对 &&、||、!的形式

&&:两个表达式同时

execution(public int com.mashibing.inter.MyCalculator.(..)) && execution( .(int,int) )

||:任意满足一个表达式即可

execution(public int com.mashibing.inter.MyCalculator.(..)) && execution( .(int,int) )

!:只有不是这个地位都能够进行切入

&&:两个表达式同时

execution(public int com.mashibing.inter.MyCalculator.*(..))

2、告诉办法的执行程序

在之前的代码中告诉的执行程序:

1、失常执行:@Before—>@After—>@AfterReturning

2、异样执行:@Before—>@After—>@AfterThrowing

3、获取办法的详细信息

在下面的案例中,咱们并没有获取 Method 的详细信息,例如办法名、参数列表等信息,想要获取的话其实非常简单,只须要增加 JoinPoint 参数即可。

LogUtil.java

package com.mashibing.util;
​
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
​
import java.util.Arrays;
​
@Component
@Aspect
public class LogUtil {
​
 @Before("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
 public static void start(JoinPoint joinPoint){Object[] args = joinPoint.getArgs();
 String name = joinPoint.getSignature().getName();
 System.out.println(name+"办法开始执行,参数是:"+ Arrays.asList(args));
 }
​
 @AfterReturning("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
 public static void stop(JoinPoint joinPoint){String name = joinPoint.getSignature().getName();
 System.out.println(name+"办法执行实现,后果是:");
​
 }
​
 @AfterThrowing("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
 public static void logException(JoinPoint joinPoint){String name = joinPoint.getSignature().getName();
 System.out.println(name+"办法出现异常:");
 }
​
 @After("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
 public static void end(JoinPoint joinPoint){String name = joinPoint.getSignature().getName();
 System.out.println(name+"办法执行完结了......");
 }
}

刚刚只是获取了办法的信息,然而如果须要获取后果,还须要增加另外一个办法参数,并且通知 spring 应用哪个参数来进行后果接管

LogUtil.java

 @AfterReturning(value = "execution( public int com.mashibing.inter.MyCalculator.*(int,int))",returning = "result")
 public static void stop(JoinPoint joinPoint,Object result){String name = joinPoint.getSignature().getName();
 System.out.println(name+"办法执行实现,后果是:"+result);
​
 }

也能够通过雷同的形式来获取异样的信息

LogUtil.java

    @AfterThrowing(value = "execution( public int com.mashibing.inter.MyCalculator.*(int,int))",throwing = "exception")
    public static void logException(JoinPoint joinPoint,Exception exception){String name = joinPoint.getSignature().getName();
        System.out.println(name+"办法出现异常:"+exception);
    }
4、spring 对通过办法的要求

spring 对于告诉办法的要求并不是很高,你能够任意扭转办法的返回值和办法的拜访修饰符,然而惟一不能批改的就是办法的参数,会呈现参数绑定的谬误,起因在于告诉办法是 spring 利用反射调用的,每次办法调用得确定这个办法的参数的值。

LogUtil.java

    @After("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
    private int end(JoinPoint joinPoint,String aa){//        System.out.println(method.getName()+"办法执行完结了......");
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"办法执行完结了......");
        return 0;
    }
5、表达式的抽取

如果在理论应用过程中,多个办法的表达式是统一的话,那么能够思考将切入点表达式抽取进去:

a、轻易生命一个没有实现的返回 void 的空办法

b、给办法上标注 @Potintcut 注解

package com.mashibing.util;

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

import java.util.Arrays;

@Component
@Aspect
public class LogUtil {@Pointcut("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
    public void myPoint(){}
    
    @Before("myPoint()")
    public static void start(JoinPoint joinPoint){Object[] args = joinPoint.getArgs();
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"办法开始执行,参数是:"+ Arrays.asList(args));
    }

    @AfterReturning(value = "myPoint()",returning = "result")
    public static void stop(JoinPoint joinPoint,Object result){String name = joinPoint.getSignature().getName();
        System.out.println(name+"办法执行实现,后果是:"+result);

    }

    @AfterThrowing(value = "myPoint()",throwing = "exception")
    public static void logException(JoinPoint joinPoint,Exception exception){String name = joinPoint.getSignature().getName();
        System.out.println(name+"办法出现异常:"+exception.getMessage());
    }

    @After("myPoint()")
    private int end(JoinPoint joinPoint){String name = joinPoint.getSignature().getName();
        System.out.println(name+"办法执行完结了......");
        return 0;
    }
}
6、盘绕告诉的应用
LogUtil.java

package com.mashibing.util;
​
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
​
import java.util.Arrays;
​
@Component
@Aspect
public class LogUtil {@Pointcut("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
 public void myPoint(){}
 
 /**
 * 盘绕告诉是 spring 中性能最弱小的告诉
 * @param proceedingJoinPoint
 * @return
 */
 @Around("myPoint()")
 public Object myAround(ProceedingJoinPoint proceedingJoinPoint){Object[] args = proceedingJoinPoint.getArgs();
 String name = proceedingJoinPoint.getSignature().getName();
 Object proceed = null;
 try {System.out.println("盘绕前置告诉:"+name+"办法开始,参数是"+Arrays.asList(args));
 // 利用反射调用指标办法,就是 method.invoke()
 proceed = proceedingJoinPoint.proceed(args);
 System.out.println("盘绕返回告诉:"+name+"办法返回,返回值是"+proceed);
 } catch (Throwable e) {System.out.println("盘绕异样告诉"+name+"办法出现异常,异样信息是:"+e);
 }finally {System.out.println("盘绕后置告诉"+name+"办法完结");
 }
 return proceed;
 }
}

总结:盘绕告诉的执行程序是优于一般告诉的,具体的执行程序如下:

盘绕前置 –> 一般前置 –> 指标办法执行 –> 盘绕失常完结 / 出现异常 –> 盘绕后置 –> 一般后置 –> 一般返回或者异样。

然而须要留神的是,如果呈现了异样,那么盘绕告诉会解决或者捕捉异样,一般异样告诉是接管不到的,因而最好的形式是在盘绕异样告诉中向外抛出异样。

7、多切面运行的程序

如果有多个切面要进行执行,那么程序是什么样的呢?

LogUtil.java

package com.mashibing.util;
​
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
​
import java.util.Arrays;
​
@Component
@Aspect
public class LogUtil {@Pointcut("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
 public void myPoint(){}
 @Before("myPoint()")
 public static void start(JoinPoint joinPoint){//        System.out.println("XXX 办法开始执行,应用的参数是:"+ Arrays.asList(objects));
//        System.out.println(method.getName()+"办法开始执行,参数是:"+ Arrays.asList(objects));
 Object[] args = joinPoint.getArgs();
 String name = joinPoint.getSignature().getName();
 System.out.println("Log:"+name+"办法开始执行,参数是:"+ Arrays.asList(args));
 }
​
 @AfterReturning(value = "myPoint()",returning = "result")
 public static void stop(JoinPoint joinPoint,Object result){//        System.out.println("XXX 办法执行完结,后果是:"+ Arrays.asList(objects));
//        System.out.println(method.getName()+"办法执行完结,后果是:"+ Arrays.asList(objects));
 String name = joinPoint.getSignature().getName();
 System.out.println("Log:"+name+"办法执行实现,后果是:"+result);
​
 }
​
 @AfterThrowing(value = "myPoint()",throwing = "exception")
 public static void logException(JoinPoint joinPoint,Exception exception){//        System.out.println(method.getName()+"办法出现异常:"+ e.getMessage());
 String name = joinPoint.getSignature().getName();
 System.out.println("Log:"+name+"办法出现异常:"+exception.getMessage());
 }
​
 @After("myPoint()")
 private int end(JoinPoint joinPoint){//        System.out.println(method.getName()+"办法执行完结了......");
 String name = joinPoint.getSignature().getName();
 System.out.println("Log:"+name+"办法执行完结了......");
 return 0;
 }
​
 /**
 * 盘绕告诉是 spring 中性能最弱小的告诉
 * @param proceedingJoinPoint
 * @return
 */
 //@Around("myPoint()")
 public Object myAround(ProceedingJoinPoint proceedingJoinPoint){Object[] args = proceedingJoinPoint.getArgs();
 String name = proceedingJoinPoint.getSignature().getName();
 Object proceed = null;
 try {System.out.println("盘绕前置告诉:"+name+"办法开始,参数是"+Arrays.asList(args));
 // 利用反射调用指标办法,就是 method.invoke()
 proceed = proceedingJoinPoint.proceed(args);
 System.out.println("盘绕返回告诉:"+name+"办法返回,返回值是"+proceed);
 } catch (Throwable e) {System.out.println("盘绕异样告诉"+name+"办法出现异常,异样信息是:"+e);
 }finally {System.out.println("盘绕后置告诉"+name+"办法完结");
 }
 return proceed;
 }
}

SecurityAspect.java

package com.mashibing.util;
​
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
​
import java.util.Arrays;
​
@Component
@Aspect
public class SecurityAspect {
​
 @Before("com.mashibing.util.LogUtil.myPoint()")
 public static void start(JoinPoint joinPoint){Object[] args = joinPoint.getArgs();
 String name = joinPoint.getSignature().getName();
 System.out.println("Security:"+name+"办法开始执行,参数是:"+ Arrays.asList(args));
 }
​
 @AfterReturning(value = "com.mashibing.util.LogUtil.myPoint()",returning = "result")
 public static void stop(JoinPoint joinPoint,Object result){String name = joinPoint.getSignature().getName();
 System.out.println("Security:"+name+"办法执行实现,后果是:"+result);
​
 }
​
 @AfterThrowing(value = "com.mashibing.util.LogUtil.myPoint()",throwing = "exception")
 public static void logException(JoinPoint joinPoint,Exception exception){String name = joinPoint.getSignature().getName();
 System.out.println("Security:"+name+"办法出现异常:"+exception.getMessage());
 }
​
 @After("com.mashibing.util.LogUtil.myPoint()")
 private int end(JoinPoint joinPoint){String name = joinPoint.getSignature().getName();
 System.out.println("Security:"+name+"办法执行完结了......");
 return 0;
 }
​
 /**
 * 盘绕告诉是 spring 中性能最弱小的告诉
 * @param proceedingJoinPoint
 * @return
 */
 //@Around("myPoint()")
 public Object myAround(ProceedingJoinPoint proceedingJoinPoint){Object[] args = proceedingJoinPoint.getArgs();
 String name = proceedingJoinPoint.getSignature().getName();
 Object proceed = null;
 try {System.out.println("盘绕前置告诉:"+name+"办法开始,参数是"+Arrays.asList(args));
 // 利用反射调用指标办法,就是 method.invoke()
 proceed = proceedingJoinPoint.proceed(args);
 System.out.println("盘绕返回告诉:"+name+"办法返回,返回值是"+proceed);
 } catch (Throwable e) {System.out.println("盘绕异样告诉"+name+"办法出现异常,异样信息是:"+e);
 }finally {System.out.println("盘绕后置告诉"+name+"办法完结");
 }
 return proceed;
 }
}

在 spring 中,默认是依照切面名称的字典程序进行执行的,然而如果想本人扭转具体的执行程序的话,能够应用 @Order 注解来解决,数值越小,优先级越高。

LogUtil.java

package com.mashibing.util;
​
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
​
import java.util.Arrays;
​
@Component
@Aspect
@Order(2)
public class LogUtil {@Pointcut("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
 public void myPoint(){}
 @Before("myPoint()")
 public static void start(JoinPoint joinPoint){//        System.out.println("XXX 办法开始执行,应用的参数是:"+ Arrays.asList(objects));
//        System.out.println(method.getName()+"办法开始执行,参数是:"+ Arrays.asList(objects));
 Object[] args = joinPoint.getArgs();
 String name = joinPoint.getSignature().getName();
 System.out.println("Log:"+name+"办法开始执行,参数是:"+ Arrays.asList(args));
 }
​
 @AfterReturning(value = "myPoint()",returning = "result")
 public static void stop(JoinPoint joinPoint,Object result){//        System.out.println("XXX 办法执行完结,后果是:"+ Arrays.asList(objects));
//        System.out.println(method.getName()+"办法执行完结,后果是:"+ Arrays.asList(objects));
 String name = joinPoint.getSignature().getName();
 System.out.println("Log:"+name+"办法执行实现,后果是:"+result);
​
 }
​
 @AfterThrowing(value = "myPoint()",throwing = "exception")
 public static void logException(JoinPoint joinPoint,Exception exception){//        System.out.println(method.getName()+"办法出现异常:"+ e.getMessage());
 String name = joinPoint.getSignature().getName();
 System.out.println("Log:"+name+"办法出现异常:"+exception.getMessage());
 }
​
 @After("myPoint()")
 private int end(JoinPoint joinPoint){//        System.out.println(method.getName()+"办法执行完结了......");
 String name = joinPoint.getSignature().getName();
 System.out.println("Log:"+name+"办法执行完结了......");
 return 0;
 }
​
 /**
 * 盘绕告诉是 spring 中性能最弱小的告诉
 * @param proceedingJoinPoint
 * @return
 */
 //@Around("myPoint()")
 public Object myAround(ProceedingJoinPoint proceedingJoinPoint){Object[] args = proceedingJoinPoint.getArgs();
 String name = proceedingJoinPoint.getSignature().getName();
 Object proceed = null;
 try {System.out.println("盘绕前置告诉:"+name+"办法开始,参数是"+Arrays.asList(args));
 // 利用反射调用指标办法,就是 method.invoke()
 proceed = proceedingJoinPoint.proceed(args);
 System.out.println("盘绕返回告诉:"+name+"办法返回,返回值是"+proceed);
 } catch (Throwable e) {System.out.println("盘绕异样告诉"+name+"办法出现异常,异样信息是:"+e);
 }finally {System.out.println("盘绕后置告诉"+name+"办法完结");
 }
 return proceed;
 }
}

SecurityAspect.java

package com.mashibing.util;
​
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
​
import java.util.Arrays;
​
@Component
@Aspect
@Order(1)
public class SecurityAspect {
​
 @Before("com.mashibing.util.LogUtil.myPoint()")
 public static void start(JoinPoint joinPoint){Object[] args = joinPoint.getArgs();
 String name = joinPoint.getSignature().getName();
 System.out.println("Security:"+name+"办法开始执行,参数是:"+ Arrays.asList(args));
 }
​
 @AfterReturning(value = "com.mashibing.util.LogUtil.myPoint()",returning = "result")
 public static void stop(JoinPoint joinPoint,Object result){String name = joinPoint.getSignature().getName();
 System.out.println("Security:"+name+"办法执行实现,后果是:"+result);
​
 }
​
 @AfterThrowing(value = "com.mashibing.util.LogUtil.myPoint()",throwing = "exception")
 public static void logException(JoinPoint joinPoint,Exception exception){String name = joinPoint.getSignature().getName();
 System.out.println("Security:"+name+"办法出现异常:"+exception.getMessage());
 }
​
 @After("com.mashibing.util.LogUtil.myPoint()")
 private int end(JoinPoint joinPoint){String name = joinPoint.getSignature().getName();
 System.out.println("Security:"+name+"办法执行完结了......");
 return 0;
 }
​
 /**
 * 盘绕告诉是 spring 中性能最弱小的告诉
 * @param proceedingJoinPoint
 * @return
 */
 //@Around("myPoint()")
 public Object myAround(ProceedingJoinPoint proceedingJoinPoint){Object[] args = proceedingJoinPoint.getArgs();
 String name = proceedingJoinPoint.getSignature().getName();
 Object proceed = null;
 try {System.out.println("盘绕前置告诉:"+name+"办法开始,参数是"+Arrays.asList(args));
 // 利用反射调用指标办法,就是 method.invoke()
 proceed = proceedingJoinPoint.proceed(args);
 System.out.println("盘绕返回告诉:"+name+"办法返回,返回值是"+proceed);
 } catch (Throwable e) {System.out.println("盘绕异样告诉"+name+"办法出现异常,异样信息是:"+e);
 }finally {System.out.println("盘绕后置告诉"+name+"办法完结");
 }
 return proceed;
 }
}

如果须要增加盘绕告诉呢,具体的执行程序又会是什么程序呢?

因为盘绕告诉在进行增加的时候,是在切面层引入的,所以在哪个切面增加盘绕告诉,那么就会在哪个切面执行。

3、基于配置的 AOP 配置

之前咱们解说了基于注解的 AOP 配置形式,上面咱们开始讲一下基于 xml 的配置形式,尽管在当初的企业级开发中应用注解的形式比拟多,然而你不能不会,因而须要简略的进行配置,注解配置疾速简略,配置的形式共呢个欠缺。

1、将所有的注解都进行删除

2、增加配置文件

aop.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:aop="http://www.springframework.org/schema/aop"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans.xsd
 http://www.springframework.org/schema/context
 http://www.springframework.org/schema/context/spring-context.xsd
 http://www.springframework.org/schema/aop
 https://www.springframework.org/schema/aop/spring-aop.xsd
">
​
 <context:component-scan base-package="com.mashibing"></context:component-scan>
 <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
​
 <bean id="logUtil" class="com.mashibing.util.LogUtil2"></bean>
 <bean id="securityAspect" class="com.mashibing.util.SecurityAspect"></bean>
 <bean id="myCalculator" class="com.mashibing.inter.MyCalculator"></bean>
 <aop:config>
 <aop:pointcut id="globalPoint" expression="execution(public int com.mashibing.inter.MyCalculator.*(int,int))"/>
 <aop:aspect ref="logUtil">
 <aop:pointcut id="mypoint" expression="execution(public int com.mashibing.inter.MyCalculator.*(int,int))"/>
 <aop:before method="start" pointcut-ref="mypoint"></aop:before>
 <aop:after method="end" pointcut-ref="mypoint"></aop:after>
 <aop:after-returning method="stop" pointcut-ref="mypoint" returning="result"></aop:after-returning>
 <aop:after-throwing method="logException" pointcut-ref="mypoint" throwing="exception"></aop:after-throwing>
 <aop:around method="myAround" pointcut-ref="mypoint"></aop:around>
 </aop:aspect>
 <aop:aspect ref="securityAspect">
 <aop:before method="start" pointcut-ref="globalPoint"></aop:before>
 <aop:after method="end" pointcut-ref="globalPoint"></aop:after>
 <aop:after-returning method="stop" pointcut-ref="globalPoint" returning="result"></aop:after-returning>
 <aop:after-throwing method="logException" pointcut-ref="globalPoint" throwing="exception"></aop:after-throwing>
 <aop:around method="myAround" pointcut-ref="mypoint"></aop:around>
 </aop:aspect>
 </aop:config>
</beans>
退出移动版