欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring基于AspectJ开发

程序员文章站 2022-05-02 07:55:30
...

AspecU是一个基于Java语言的AOP框架,它提供了强大的AOP功能。Spring 2.0以后,SpringAOP引入了对AspectJ的支持,并允许直接使用AspectJ进行编程,而iSpring自 身的AOP API也尽量与Aspecu保持- -致。 新版本的Spring框架,也建议使用AspecuJ来开发AOP.使用AspecuJ实现AOP有两种方式:

  • -种是基于XML的声明式AspectJ,
  • 另一种是基于注解的声明式AspecJ。

案例:
给HelloWorld接口的两个实现类的所有方法都加上了代理,代理内容:打印时间,
1、增加一个横切关注点,打印日志
2 只想织入接口中的某些方法
要求:基于xml 实现

  • 编写目标角色
public interface HelloWorld {
    void printHelloWorld();
    void doPrint();
}
public class HelloWorldImpl1 implements HelloWorld {
    @Override
    public void printHelloWorld() {
        System.out.println("执行HelloWorldImpl1.printHelloWorld");
    }

    @Override
    public void doPrint() {
        System.out.println("执行HelloWorldImpl1.doPrint");
    }
}
public class HelloWorldImpl2 implements HelloWorld {
    @Override
    public void printHelloWorld() {
        System.out.println("执行HelloWorldImpl2.doPrint");
    }

    @Override
    public void doPrint() {
        System.out.println("执行HelloWorldImpl2.doPrint");
    }
}
  • 编写切面类
//切面类
public class TimeHandler {
    public void printTime(){
        System.out.println("CurrentTime = " + System.currentTimeMillis());
    }
}
//切面类
public class LogHandler {
    public void LogBefore(){
        System.out.println("Log before method");
    }
    public void LogAfter(){
        System.out.println("Log after method");
    }
}
  • 编写配置文件
<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <bean id="helloWorldImpl1" class="com.no5.aspectj.demoxml.HelloWorldImpl1"/>
    <bean id="helloWorldImpl2" class="com.no5.aspectj.demoxml.HelloWorldImpl2"/>
    <bean id="timeHandler" class="com.no5.aspectj.demoxml.TimeHandler"/>
    <bean id="logHandler" class="com.no5.aspectj.demoxml.LogHandler"/>
    <aop:config>
        <aop:aspect id="log" ref="logHandler" order="2">
            <aop:pointcut id="printLog" expression="execution(* com.no5.aspectj.demoxml.HelloWorld.printHelloWorld (..))"/>
            <aop:before method="LogBefore" pointcut-ref="printLog"></aop:before>
            <aop:after method="LogAfter" pointcut-ref="printLog"/>
        </aop:aspect>
        <aop:aspect id="time" ref="timeHandler" order="1">
            <aop:pointcut id="printTime" expression="execution(* com.no5.aspectj.demoxml.HelloWorld.doPrint (..))"/>
            <aop:before method="printTime" pointcut-ref="printTime"></aop:before>
            <aop:after method="printTime" pointcut-ref="printTime"></aop:after>
        </aop:aspect>
    </aop:config>

</beans>
  • 测试
public class Test {
    public static void main(String[] args){
        ApplicationContext ctx = new ClassPathXmlApplicationContext("com/no5/aspectj/demoxml/aop.xml");
        HelloWorld hw1 = ctx.getBean("helloWorldImpl1",HelloWorld.class);
        HelloWorld hw2 = ctx.getBean("helloWorldImpl2",HelloWorld.class);
        hw1.printHelloWorld();
        System.out.println();
        hw1.doPrint();

        System.out.println();
        hw2.printHelloWorld();
        System.out.println();
        hw2.doPrint();
    }
}
  • 结果

Spring基于AspectJ开发
基于XML的声明式AspectJ是指通过XML文件来定义切面、切入点及通知,所有的切面、切入点和通知都必须定义在>aop:config>元素内。
Spring基于AspectJ开发

五大通知:

Spring基于AspectJ开发
aop:after与aop:after-returning的区别

  • 后置通知是在方法正常返回后执行的通知,如果方法没有正
    常返-例如抛出异常,则后置通知不会执行。而最终通知无论
    如何都会在目标方法调用过后执行,即使目标方法没有正常的
    执行完成。
  • 后置通知可以通过配置得到返回值,而最终通知无法得到。
  • 最终通知也可以额外接收一个JoinPoint参数, 来获取目标对
    象和目标方法相关信息,但一定要保证必须是第一个参 数。

●<aop:aspect.../>:配置切面。
●<aop:before../>:配置Before增强处理。
●<aop:after../> :配置After增强处理。
●<aop:after-returing../> :配置AfterReturing增强处理。
●<aop:after-throwing.../> :配置AfterThrowing增强处理
●<aop:around.../> :配置Around增强处理。
●<aop:pointcut.../ >:配置切入点。

案例:

  • 目标类:
public interface UserDao {
    public void addUser();
    public void deleteUser();
}
//目标类
public class UserDaoImpl implements UserDao {
    @Override
    public void addUser() {
        System.out.println("添加用户");
    }

    @Override
    public void deleteUser() {
        System.out.println("删除用户");
    }
}
  • 切面类
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

//切面类,在此类中编写通知
public class MyAspect {
    //前置通知
    public void myBefore(JoinPoint joinPoint){
        System.out.println("前置通知,模拟执行权限检查...");
        System.out.println("目标类是:" + joinPoint.getTarget());
        System.out.println("被织入增强处理的目标方法为:"+joinPoint.getSignature().getName());
    }
    //后置通知
    public void myAfterReturning(JoinPoint joinPoint){
        System.out.println("后置通知,模拟执行权限检查...");
        System.out.println("目标类是:" + joinPoint.getTarget());
        System.out.println("被织入增强处理的目标方法为:"+joinPoint.getSignature().getName());
    }

    /**
     * 环境通知
     * @param proceedingJoinPoint 是JoinPoint子接口,表示可以执行目标方法
     * @return 必须是Object的返回类型
     * 必须接收一个参数,类型为proceedingJoinPoint
     * @throws Throwable
     */
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        //开始
        System.out.println("环境开始:执行目标方法之前,模拟开启事物...");
        //执行当前目标方法
        Object obj = proceedingJoinPoint.proceed();
        //结束
        System.out.println("环境结束:执行目标方法之后,模拟开启事物...");
        return obj;
    }
    //异常通知
    public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
        System.out.println("异常通知:" + "出错了" + e.getMessage());
    }
    //最终通知
    public void myAfter(){
        System.out.println("最终通知:模拟方法结束后的释放资源...");
    }
}
  • 编写配置文件
<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 1 目标类 -->
    <bean id="userDao" class="com.no5.aspectj.xml.UserDaoImpl" />
    <!-- 2 切面 -->
    <bean id="myAspect" class="com.no5.aspectj.xml.MyAspect" />
    <!-- 3 aop编程 -->
    <aop:config>
        <!-- 配置切面 -->
        <aop:aspect ref="myAspect">
            <!-- 3.1 配置切入点,通知最后增强哪些方法 -->
            <aop:pointcut  id="myPointCut" expression="execution(* com.no5.aspectj.xml.*.* (..))"/>
            <!-- 3.2 关联通知Advice和切入点pointCut -->
            <!-- 3.2.1 前置通知 -->
            <aop:before method="myBefore" pointcut-ref="myPointCut" />
            <!-- 3.2.2 后置通知,在方法返回之后执行,就可以获得返回值
             returning属性:用于设置后置通知的第二个参数的名称,类型是Object -->
            <aop:after-returning method="myAfterReturning"
                                 pointcut-ref="myPointCut" returning="joinPoint" />
            <!-- 3.2.3 环绕通知 -->
            <aop:around method="myAround" pointcut-ref="myPointCut" />
            <!-- 3.2.4 抛出通知:用于处理程序发生异常-->
            <!-- * 注意:如果程序没有异常,将不会执行增强 -->
            <!-- * throwing属性:用于设置通知第二个参数的名称,类型Throwable -->
            <aop:after-throwing method="myAfterThrowing"
                                pointcut-ref="myPointCut" throwing="e" />
            <!-- 3.2.5 最终通知:无论程序发生任何事情,都将执行 -->
            <aop:after method="myAfter" pointcut-ref="myPointCut" />
        </aop:aspect>
    </aop:config>
</beans>

基于注解的声明式AspecJ:
AspecJ注解描述:
Spring基于AspectJ开发

以上面基于XML的案例案例用注解实现:

  • 目标类:
//目标类
@Repository("userDao")
public class UserDaoImpl implements UserDao {
    @Override
    public void addUser() {
        System.out.println("添加用户");
    }

    @Override
    public void deleteUser() {
        System.out.println("删除用户");
    }

    @Override
    public boolean findUser() {
        System.out.println("查找用户");
        return true;
    }

}
  • 切面类:
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.JoinPoint;
/**
 * 切面类,在此类中编写通知
 */
@Aspect
@Component
public class MyAspect {


    //定义切入点表达式
    @Pointcut("execution(* com.no5.aspectj.annotation.*.* (..))")
    //使用一个返回值为void、方法体为空的方法来命名切入点
    private void myPointCut(){}
    //前置通知
     @Before("myPointCut()")
    public void myBefore(JoinPoint joinPoint){
        System.out.print("前置通知,模拟执行权限检查...");
        System.out.print("目标类是:" + joinPoint.getTarget());
        System.out.println("被织入增强处理的目标方法为:"+joinPoint.getSignature().getName());
    }
    //后置通知
    @AfterReturning(pointcut = "myPointCut()")
    public void myAfterReturning(JoinPoint joinPoint){
        System.out.print("后置通知,模拟记录日志...");
        System.out.print("目标类是:" + joinPoint.getTarget());
        System.out.println("被织入增强处理的目标方法为:"+joinPoint.getSignature().getName());
    }
    //后置通知
    @AfterReturning(returning = "flag",pointcut = "execution(* 龙素娟229.no5.aspectj.annotation.*.* (..))")
    public void myAfterReturning2(JoinPoint joinPoint,boolean flag){
        System.out.print("后置通知,模拟执行权限检查...");
        System.out.print("目标类是:" + joinPoint.getTarget());
        System.out.println("被织入增强处理的目标方法为:"+joinPoint.getSignature().getName() + "返回值为:" +flag);
    }
    /**
     * 环境通知(环绕通知)
     * @param proceedingJoinPoint 是JoinPoint子接口,表示可以执行目标方法
     * @return 必须是Object的返回类型
     * 必须接收一个参数,类型为proceedingJoinPoint
     * @throws Throwable
     */
    @Around("myPointCut()")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        //开始
        System.out.println("环绕通知:执行目标方法之前,模拟开启事物...");
        //执行当前目标方法
        Object obj = proceedingJoinPoint.proceed();
        //结束
        System.out.println("环绕通知:执行目标方法之后,模拟开启事物...");
        return obj;
    }
    //异常通知
    @AfterThrowing(value = "myPointCut()",throwing = "e")
    public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
        System.out.println("异常通知:" + "出错了" + e.getMessage());
    }
    //最终通知
    @After("myPointCut()")
    public void myAfter(){
        System.out.println("最终通知:模拟方法结束后的释放资源...");
    }

}
  • 配置文件:
<?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/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 指定搜索路径 -->
    <!-- 使用context命名空间,扫描指定包下的Bean类,进行注解解析 -->
    <context:component-scan base-package="com.no5.aspectj.annotation"/>
    <!-- 启动基于注解的声明式AspectJ 支持 -->
    <aop:aspectj-autoproxy />
</beans>

通知执行的顺序:
Spring基于AspectJ开发

相关标签: J2EE