SSH笔记-AOP(基于注解方式配置AOP通知)

1、AOP(Aspect-Oriented Programming, 面向切面编程): 是一种新的方法论, 是对传统 OOP(Object-Oriented Programming, 面向对象编程) 的补充

2、AOP 的主要编程对象是切面(aspect), 而切面模块化横切关注点

3、在应用 AOP 编程时, 仍然需要定义公共功能, 但可以明确的定义这个功能在哪里, 以什么方式应用, 并且不必修改受影响的类. 这样一来横切关注点就被模块化到特殊的对象(切面)里

4、AOP 的好处:
①每个事物逻辑位于一个位置, 代码不分散, 便于维护和升级
②业务模块更简洁, 只包含核心业务代码

5、AOP术语:
①切面(Aspect): 横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象
②通知(Advice): 切面必须要完成的工作,即:标注有某种注解的简单的 Java 方法
③目标(Target): 被通知的对象
④代理(Proxy): 向目标对象应用通知之后创建的对象
⑤连接点(Joinpoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。
⑥切点(pointcut):每个类都拥有多个连接点,即AOP的扫描规则。例如 处理逻辑类的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件

6、在 Spring2.0 以上版本中, 可以使用基于 AspectJ 注解或基于 XML 配置的 AOP(AspectJ比Spring原生的AOP注解好用)

7、基于注解方式配置AOP通知
(1)使用Aspectj来配置Spring的AOP环境,Spring自己也有AOP框架,不过Aspectj好一点

(2)需要的jar包:aspectjrt.jar、aspectjtools.jar、aspectjweaver.jar、org.aspectj.matcher.jar

(3)基于注解的方式配置aop
①配置文件中加入aop命名空间,即:在spring配置文件的命名空间里面勾选aop
②配置文件中添加<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
③把横切关注点代码“beforeMethod()”抽象到切面类“LoggingAspect”中
④把切面类使用@Component加入IOC容器中
⑤切面内需要加入@Aspect注解
⑥在类中声明通知,即:在方法前加入(一个关注点可声明多个注解)

①前置通知(方法执行前执行):@Before
②后置通知(方法执行后执行):@After
③返回通知(方法返回后执行):@AfterRunning、
④异常通知(抛出异常后执行):@AfterThrowing
⑤环绕通知(围绕着方法执行):@Around
⑦为注解添加切入点表达式
⑧在关注点方法的属性加入JoinPoint函数,用于获取相关值

(4)AspectJ使用步骤:

① 添加AspectJ 类库(jar包)
② 将 aop Schema 添加到 <beans> 根元素中
③ 在 Spring IOC 容器中启用 AspectJ 注解支持,即配置文件<beans>中添加<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
④ 使用AOP的类(切面)的类名前面配置@Aspect

(5)AspectJ 支持 5 种类型的通知注解:
@Before: 前置通知, 在方法执行之前执行
@After: 后置通知, 在方法执行之后执行
@AfterRunning: 返回通知, 在方法返回结果之后执行
@AfterThrowing: 异常通知, 在方法抛出异常之后
@Around: 环绕通知, 围绕着方法执行

8、文件
①TestMain.java:测试类
②AOPoperations.java:方法接口
③AOPopeartionsImpl.java:实现接口和接口方法的类
④LoggingAspect.java:优先级较低的切面类
⑤LoggingAspect2.java:优先级较高的切面类
⑥PointCut.java:存放代表可重用的切点表达式的方法的类
⑦annotationNotificationContext.xml:配置方法

9、TestMain.java

package com.demo.sshtest.aop2.annotationNotification;

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

public class TestMain {

    public static void main(String[] args) {
        //创建spring的IOC容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("annotationNotificationContext.xml");
        //从IOC容器中获取bean实例(这里获取的是接口类的实例,而不是实现接口的类的实例)
        AOPoperations aoPopeartions = (AOPoperations)applicationContext.getBean(AOPoperations.class);
        //使用bean
        String result = aoPopeartions.addA("strsdlkfadkljklj");
        System.out.println("-------------");
        String result2 = aoPopeartions.addB("weefweewf");
        System.out.println("-------------");
        try {
            String result3 = aoPopeartions.addCal("1","2a");
        } catch (Exception e) {
        }
    }
}

10、AOPoperations.java

package com.demo.sshtest.aop2.annotationNotification;

public interface AOPoperations {

    public String addA(String str);
    public String addB(String str);
    public String addCal(String str1,String str2) throws Exception;
}

11、AOPopeartionsImpl.java

package com.demo.sshtest.aop2.annotationNotification;

import org.springframework.stereotype.Component;

@Component
public class AOPopeartionsImpl implements AOPoperations{

    public String addA(String str) {
        String result = str+"_AAAAAAAAAA";
        return result;
    }

    public String addB(String str) {
        String result = str+"_BBBBBBBBBB";
        return result;
    }

    public String addCal(String str1, String str2) throws Exception{
        String result = "";
        result = "" + (Double.parseDouble(str1) + Double.parseDouble(str2));
        return result;
    }

}

12、LoggingAspect.java

package com.demo.sshtest.aop2.annotationNotification;


import java.util.Arrays;
import java.util.List;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

//把LoggingAspect类声明为切面
/*
1、把该类放入IOC容器
2、用@Aspect声明切面
*/
@Order(2)
@Aspect
@Component
public class LoggingAspect {

    /*
        切入点表达式写法:"execution(控制权限 包名.类名.方法名(参数类型))"
        例如:@Before(public com.demo.sshtest.aop2.annotationNotification.AOPoperations.addC(String,String))
        其中,控制权限、类名、方法名、参数类型可以用"*"代替,如:(* com.demo.sshtest.aop2.annotationNotification.*.*(*,*))
        另外,参数类型还可以用".."代表任意类型和任意数量的参数参数类型
    */

    //声明前置通知:在目标方法开始之前执行
    @Before("com.demo.sshtest.aop2.annotationNotification.PointCut.pointcutexpression()")
    public void beforeMethod(JoinPoint joinPoint){
        //通过在@Before标注的方法的参数中添加JoinPoint来获取当前调用的方法名及其传入的值
        //获取方法名     :joinPoint.getSignature().getName()
        //获取传入方法的值  :joinPoint.getArgs()
        String methodname = joinPoint.getSignature().getName();
        List<Object> values = Arrays.asList(joinPoint.getArgs());
        System.out.println("Begin run method "+methodname+" and the value is:"+values);
    }

    //声明后置通知:在目标方法执行之后执行,无论是否发生异常
    //后置通知不能获取方法返回的值
    @After("com.demo.sshtest.aop2.annotationNotification.PointCut.pointcutexpression()")
    public void afterMethod(JoinPoint joinPoint){
        //通过在@After标注的方法的参数中添加JoinPoint来获取当前调用的方法名及其传入的值
        //获取方法名     :joinPoint.getSignature().getName()
        //获取传入方法的值  :joinPoint.getArgs()
        String methodname = joinPoint.getSignature().getName();
        List<Object> values = Arrays.asList(joinPoint.getArgs());
        System.out.println("End run method "+methodname+" and the value is:"+values);
    }

    //声明返回通知:在目标方法返回结果之后执行
    @AfterReturning(value="com.demo.sshtest.aop2.annotationNotification.PointCut.pointcutexpression()",returning="result")
    public void afterReturnMethod(JoinPoint joinPoint,Object result){
        //通过在@AfterReturning标注的方法的参数中添加JoinPoint来获取当前调用的方法名及其返回的值
        //获取方法名     :joinPoint.getSignature().getName()
        //获取传入方法的值  :joinPoint.getArgs()
        String methodname = joinPoint.getSignature().getName();
        System.out.println("run method "+methodname+" and Return:"+result);
    }

    //声明异常返通知:在目标方法抛出异常之后执行
    //可以访问异常对象,且指定出现指定异常的执行通知代码,即afterThrowing()中的Exception
    //配置中@AfterThrowing的throwing值要和afterThrowing中Exception的参数名一致
    //如果想要只在@AfterThrowing中输出定制的异常信息,则需要在对应方法里面直接使用throw来抛出异常,而不是用try catch,因为catch会完成完整的异常捕获,异常捕获完成后@AfterThrowing不会运行
    @AfterThrowing(value="com.demo.sshtest.aop2.annotationNotification.PointCut.pointcutexpression()",throwing="exception")
    public void afterThrowingMethod(JoinPoint joinPoint,Exception exception){
        String methodname = joinPoint.getSignature().getName();
        System.out.println(methodname+" throw an exception:"+exception.getMessage());
    }

    //声明环绕通知:在前置通知执行前后运行,后置通知前执行,在异常通知执行后,则不执行
    //环绕通知需要携带ProceedingJoinPoint类型的参数
    //环绕通知类似于动态代理全过程:ProceedingJoinPoint可以决定是否执行目标方法
    //环绕通知必须有返回值,返回值为目标方法返回值
    @Around("com.demo.sshtest.aop2.annotationNotification.PointCut.pointcutexpression()")
    public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint){
        Object result = null;
        try{
        String methodname = proceedingJoinPoint.getSignature().getName();
        System.out.println(methodname+" aroundMethod...before note....");
        result = proceedingJoinPoint.proceed();
        System.out.println(methodname+" aroundMethod...after note...."+result);
        }catch(Throwable e){
            throw new RuntimeException();
        }
        return result;
    }
}

13、LoggingAspect2.java

package com.demo.sshtest.aop2.annotationNotification;


import java.util.Arrays;
import java.util.List;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

//关于切点优先级,可以使用@Order()来设定,括号中的数值越小,优先级越高

@Order(1)
@Aspect
@Component
public class LoggingAspect2 {


    //声明前置通知:在目标方法开始之前执行
    @Before("com.demo.sshtest.aop2.annotationNotification.PointCut.pointcutexpression()")
    public void beforeMethod2(JoinPoint joinPoint){
        String methodname = joinPoint.getSignature().getName();
        List<Object> values = Arrays.asList(joinPoint.getArgs());
        System.out.println("LoggingAspect2-->Begin run method "+methodname+" and the value is:"+values);
    }

    //声明后置通知:在目标方法执行之后执行,无论是否发生异常
    @After("com.demo.sshtest.aop2.annotationNotification.PointCut.pointcutexpression()")
    public void afterMethod2(JoinPoint joinPoint){
        String methodname = joinPoint.getSignature().getName();
        List<Object> values = Arrays.asList(joinPoint.getArgs());
        System.out.println("LoggingAspect2-->End run method "+methodname+" and the value is:"+values);
    }
}

14、PointCut.java

package com.demo.sshtest.aop2.annotationNotification;

import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

public class PointCut {

    //关于可重用的切点表达式
    //1、先创建一个void类型的空方法,方法名任意,不需要写任何参数和代码
    //2、为步骤一中的方法添加@Pointcut()注解
    //3、把需要重用的切点表达式写到@Pointcut()中
    //4、调用时,在需要调用的注解标签内写上该方法的包名+类名+方法名,如果跟该方法在同一个包类,则可以省略包名,如果在同一个类里面,则可以省略包名和类名
    //调用方法例子:@Before("com.demo.sshtest.aop2.frontnotification.PointCut.pointcutexpression()")
    @Pointcut("execution(* com.demo.sshtest.aop2.annotationNotification.AOPoperations.*(..))")
    public void pointcutexpression(){}
}

15、annotationNotificationContext.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:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    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-4.3.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

    <context:component-scan base-package="com.demo.sshtest.aop2.annotationNotification"></context:component-scan>

    <!-- 使AspjectJ注解起作用:自动为匹配的类生成代理对象 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>

16、注意事项:
①实现接口的类需要使用@Component、@Controller、@Respository、@Service标签注释
②配置文件中要配置扫描所有需要的类
③设置切点优先级,可以使用@Order()来设定,括号中的数值越小,优先级越高
④切点类不仅需要使用@Aspect注释还要使用@Component、@Controller、@Respository、@Service标签注释
⑤在@Before、@After、@AfterRunning、@AfterThrowing、@Around的括号里面能直接配置切点表达式或者存着切点表达式的类的方法
⑥切点表达式格式为:execution(类修饰类型.包名.类名.方法名(..)),其中方法名后面括号的“..”代表省略方法参数
⑦从IOC容器中获取bean实例(获取的是接口类的实例,而不是实现接口的类的实例)

17、项目目录(只看com.demo.sshtest.apo2.annotationNotification和annotationNotification.xml)
项目目录

18、demo
https://download.csdn.net/download/qq_22778717/10476407

猜你喜欢

转载自blog.csdn.net/qq_22778717/article/details/80679843
今日推荐