当前位置: 首页 > 图灵资讯 > 技术篇> 基于AspectJ的AOP注解式开发

基于AspectJ的AOP注解式开发

来源:图灵教育
时间:2023-06-12 09:23:23

1.Spring对AOP的实现包括以下三种方式:1061.13种方式106

●第一种方法:Spring框架结合AspectJ框架实现AOP,基于注释方法。

●第二种方法:Spring框架结合AspectJ框架实现AOP,基于XML方法。

●第三种方法:基于XML配置的Spring框架本身实现的AOP。

在实际开发中,是Spring+AspectJ实现AOP。因此,我们专注于学习第一种和第二种方法。

1.2什么是AspectJ?106

(Eclipse组织支持AOP的框架。AspectJ框架是独立于Spring框架的框架,Spring框架使用AspectJ)

AspectJ项目起源于帕洛阿尔托(PaloAlto)研究中心(缩写为PARC)。该中心由Xerox集团资助,Gregorkiczales领导,自1997年以来一直致力于AspectJ的开发,1998年首次发布给外部用户,2001年发布1.0release。为了促进AspectJ技术和社区的发展,PARC于2003年3月正式将AspectJ项目移交给Eclipse组织,因为AspectJ的发展和关注度远远超出了PARC的预期,他们无法继续保持其发展。

2.准备工作107

使用Spring+AspectJ的AOP需要引入以下依赖:

<!--spring context依赖--><dependency>  <groupId>org.springframework</groupId>  <artifactId>spring-context</artifactId>  <version>6.0.0-M2</version></dependency><!--spring aop依赖--><dependency>  <groupId>org.springframework</groupId>  <artifactId>spring-aop</artifactId>  <version>6.0.0-M2</version></dependency><!--spring 依赖aspects--><dependency>  <groupId>org.springframework</groupId>  <artifactId>spring-aspects</artifactId>  <version>6.0.0-M2</version></dependency>

在Spring配置文件中添加context命名空间和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 http://www.springframework.org/schema/aop/spring-aop.xsd"></beans>
3.基于AspectJ的AOP注释开发108

实现步骤

3.1第一步:定义目标类别和方法
package com.powernode.spring6.service;import org.springframework.stereotype.Service;/** * AOP注释开发基于AspectJ  108 * 业务流程 **/@Service("userService")public class UserService { // 目标类    public void login(){ // 目标方法        System.out.println(”该系统正在进行身份认证...");    }//    public void logout(){//        System.out.println(退出系统...");//    }}
3.2第二步:定义切面类别
package com.powernode.spring6.service;import org.aspectj.lang.annotation.*;import org.springframework.stereotype.Component;/** * AOP注释开发基于AspectJ  108 * 切面 **/@Component("logAspect") //这是纳入spring容器管理的@Aspect // 需要用@Aspect注释来标记切面类。public class LogAspect { // 切面}
3.3第三步:Springbean管理包括目标类和切面类

添加到目标类Orderservice上@Component注释

添加切面类Myaspect@Component注释

3.4第四步:在spring配置文件中添加组建扫描
<?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 http://www.springframework.org/schema/aop/spring-aop.xsd"><!--    组件扫描  108-->    <context:component-scan base-package="com.powernode.spring6.service"/></beans>
3.5第五步:在切面类中添加通知
package com.powernode.spring6.service;import org.aspectj.lang.annotation.*;import org.springframework.stereotype.Component;/** * AOP注释开发基于AspectJ  108 * 切面 **/@Component("logAspect") //这是纳入spring容器管理的@Aspect // 需要用@Aspect注释来标记切面类。public class LogAspect { // 切面    // 切面 = 通知 + 切点    // 通知是增强,是具体编写的增强代码    // 这里通知Advice以方法的形式出现。public class LogAspect { // 切面    // 切面 = 通知 + 切点    // 通知是增强,是具体编写的增强代码    // Advice以方法的形式出现在这里。(因为代码可以写在方法中)    // @Before注释标记的方法是前置通知。       public void 增强(){ //通知        System.out.println(”我是一个通知,我是一个增强代码...");    }}
3.6第六步:在通知中添加切点表达式
package com.powernode.spring6.service;import org.aspectj.lang.annotation.*;import org.springframework.stereotype.Component;/** * AOP注释开发基于AspectJ  108 * 切面 **/@Component("logAspect") //这是纳入spring容器管理的@Aspect // 需要用@Aspect注释来标记切面类。public class LogAspect { // 切面    // 切面 = 通知 + 切点    // 通知是增强,是具体编写的增强代码    // 这里通知Advice以方法的形式出现。public class LogAspect { // 切面    // 切面 = 通知 + 切点    // 通知是增强,是具体编写的增强代码    // Advice以方法的形式出现在这里。(因为代码可以写在方法中)    // @Before注释标记的方法是前置通知。    @Before("execution(* com.powernode.spring6.service.UserService.*(..))") //切点    public void 增强(){ //通知        System.out.println(”我是通知,我是一个增强代码...");    }}

注释@Before表示前置通知。

3.7第七步:在spring配置文件中使用自动代理
<?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 http://www.springframework.org/schema/aop/spring-aop.xsd"><!--    组件扫描  108-->    <context:component-scan base-package="com.powernode.spring6.service"/>    <!--    组件扫描  108-->    <context:component-scan base-package="com.powernode.spring6.service"/>    <!--自动代理打开aspectj  108 -->    <!--扫描spring容器时,检查该类是否有@aspect注释,如果有,则为该类生成代理对象。-->    <!--        proxy-target-class="true"  表示强制使用CGLIB动态代理        proxy-target-class="false" 这是默认值,表示界面使用JDK动态代理,而CGLIB动态代理。     -->    <aop:aspectj-autoproxy proxy-target-class="true"/></beans>

打开自动代理后,任何带有@Aspect注释的bean都会生成代理对象。

proxy-target-class="true表示采用cglib动态代理。

proxy-target-class="false“表示使用jdk动态代理。默认值是false。即使写成false,cglib生成代理也会在没有接口的情况下自动选择。

测试程序:

//基于AspectJ的AOP注释开发  108    @Test    public void testBefore(){        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");        UserService userService = applicationContext.getBean("userService", UserService.class);        userService.login();    }

基于AspectJ的AOP注解式开发_spring

4.切点表达式专题109

OrderService类也要增强

package com.powernode.spring6.service;import org.springframework.stereotype.Service;/** * AOP注释开发基于AspectJ  109 * 配合研究切点表达式 **/@Service("orderService")public class OrderService { // 目标类    // 目标方法    public void generate(){        System.out.println(”系统正在生成订单...");        /*if (1 == 1) {           throw new RuntimeException(运行时异常);        }*/    }}

修改切点的表达式如下

@Before("execution(* com.powernode.spring6.service..*(..))") //切点    public void 增强(){ //通知        System.out.println(”我是一个通知,我是一个增强代码...");    }

测试

//基于AspectJ的AOP注释开发  108    @Test    public void testBefore(){        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");        UserService userService = applicationContext.getBean("userService", UserService.class);        userService.login();        userService.logout();        ///要OrderService也被加强  109        OrderService orderService = applicationContext.getBean("orderService", OrderService.class);        orderService.generate();    }

基于AspectJ的AOP注解式开发_spring_02

5.通知类型110

通知类型包括:

●前置通知:@Before目标方法实施前的通知

●后置通知:@Afterrreturning目标方法实施后的通知

●环绕通知:@Around目标方法前添加通知,实施目标方法后添加通知。

●异常通知:@Afterthrowing发生异常后执行的通知

●最终通知:@After放在finally句块中的通知

接下来,编写程序来测试这些通知的执行顺序:

package com.powernode.spring6.service;import org.springframework.stereotype.Service;/** * AOP注释开发基于AspectJ  109 * 配合研究切点表达式 **/@Service("orderService")public class OrderService { // 目标类    // 目标方法    public void generate(){        System.out.println(”系统正在生成订单...");        /*if (1 == 1) {           throw new RuntimeException(运行时异常);        }*/    }}
package com.powernode.spring6.service;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.Signature;import org.aspectj.lang.annotation.*;import org.springframework.core.annotation.Order;import org.springframework.stereotype.Component;/** * AOP注释开发基于AspectJ  108 * 切面 **/@Component("logAspect") //这是纳入spring容器管理的@Aspect // 需要用@Aspect注释来标记切面类。public class LogAspect { // 切面    // 切面 = 通知 + 切点    // 通知是增强,是具体编写的增强代码    // 这里通知Advice以方法的形式出现。public class LogAspect { // 切面    // 切面 = 通知 + 切点    // 通知是增强,是具体编写的增强代码    // Advice以方法的形式出现在这里。(因为代码可以写在方法中)    // @Before注释标记的方法是前置通知。    /*@Before("execution(* com.powernode.spring6.service..*(..))") //切点    public void 增强(){ //通知        System.out.println(”我是通知,我是一个增强代码...");    }*/            // 前置通知    110    @Before("execution(* com.powernode.spring6.service..*(..))") //切点    public void beforeAdvice(JoinPoint joinPoint){        System.out.println(“前置通知”);     }    // 后置通知   110    @AfterReturning("execution(* com.powernode.spring6.service..*(..))") //切点    public void afterReturningAdvice(JoinPoint joinPoint){        System.out.println(“后置通知”);    }    // 环绕通知(环绕是最大的通知,在前通知之前,在后通知之后。)  110    @Around("execution(* com.powernode.spring6.service..*(..))")    public void aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {        // 前面的代码        System.out.println(前环绕);        // 执行目标        joinPoint.proceed(); // 执行目标        // 后面的代码        System.out.println(“后环绕”;    }    // 异常通知    @AfterThrowing("execution(* com.powernode.spring6.service..*(..))")    public void afterThrowingAdvice(){        System.out.println(异常通知);    }    // 最终通知 (finally句块中的通知)    @After("execution(* com.powernode.spring6.service..*(..))")    public void afterAdvice(){        System.out.println(“最终通知”);    }}
//测试  108public class SpringAOPTest {    //基于AspectJ的AOP注释开发  108    @Test    public void testBefore(){        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");        /*UserService userService = applicationContext.getBean("userService", UserService.class);        userService.login();        userService.logout();*/        ///要OrderService也被加强  109        OrderService orderService = applicationContext.getBean("orderService", OrderService.class);        orderService.generate();    }}
6.截面顺序111

众所周知,业务流程中不一定只有一个截面,有的可以控制事务,有的可以记录日志,有的可以控制安全。如果有多个截面,如何控制顺序:用@order注释来识别切面类,为@order注释的value指定一个整数数字,数字越小,优先级越高。

再次定义一个切面类,如下:

package com.powernode.spring6.service;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.springframework.core.annotation.Order;import org.springframework.stereotype.Component;//基于AspectJ的AOP注释开发//安全截面   111@Aspect@Component@Order(0) ///截面通知的顺序是,数字越小,优先级越高  111public class SecurityAspect {    //通知+切点    @Before("execution(* com.powernode.spring6.service..*(..))") //切点    public void beforeAdvice(){        System.out.println(前置通知:安全...");    }}

基于AspectJ的AOP注解式开发_AOP_04

7.通用切点112

上述代码的缺点是:

第一:切点表达式重复写了很多次,没有重复使用。

第二:如果要修改切点表达式,需要修改很多地方,很难维护。

可以这样做:单独定义切点表达式,并将其引入所需位置。如下:

///定义通用切点表达式   112    @Pointcut("execution(* com.powernode.spring6.service..*(..))")    public void 通用切点()()){        // 这种方法只是一个标记,方法名是随意的,方法体不需要写任何代码。    }

案例如下

package com.powernode.spring6.service;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.Signature;import org.aspectj.lang.annotation.*;import org.springframework.core.annotation.Order;import org.springframework.stereotype.Component;/** * AOP注释开发基于AspectJ  108 * 切面 **/@Component("logAspect") //这是纳入spring容器管理的@Aspect // 需要用@Aspect注释来标记切面类。@Order(2)public class LogAspect { // 切面    // 切面 = 通知 + 切点    // 通知是增强,是具体编写的增强代码    // 这里通知Advice以方法的形式出现。(因为代码可以在方法中写)    // @Before注释标记的方法是前置通知。    /*@Before("execution(* com.powernode.spring6.service..*(..))") //切点    public void 增强(){ //通知        System.out.println(”我是通知,我是一个增强代码...");    }*/     ///定义通用切点表达式   112    @Pointcut("execution(* com.powernode.spring6.service..*(..))")    public void 通用切点()()){        // 这种方法只是一个标记,方法名随意,方法体不需要写任何代码。    }    // 前置通知    110    @Before(通用切点()    //@Before("execution(* com.powernode.spring6.service..*(..))") //切点    public void beforeAdvice(JoinPoint joinPoint){        System.out.println(“前置通知”);        // JoinPointt这个Join joinPoint,当Spring容器调用此方法时,它会自动传输.        // 我们可以直接使用。使用这个JoinPointntt 为什么joinPoint?使用这个JoinPointntt 为什么joinPoint?        // Signature signature = joinPoint.getSignature(); 获取目标方法的签名。        // 一种方法的具体信息可以通过方法签名获得。        // 获取目标方法的方法名称。        //System.out.println(目标方法的方法名称:" + joinPoint.getSignature().getName());    }    // 后置通知   110    @AfterReturning(通用切点()    //@AfterReturning("execution(* com.powernode.spring6.service..*(..))") //切点    public void afterReturningAdvice(JoinPoint joinPoint){        System.out.println(“后置通知”);    }    // 环绕通知(环绕是最大的通知,在前通知之前,在后通知之后。)  110    @Around(通用切点()    //@Around("execution(* com.powernode.spring6.service..*(..))")    public void aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {        // 前面的代码        System.out.println(前环绕);        // 执行目标        joinPoint.proceed(); // 执行目标        // 后面的代码        System.out.println(“后环绕”;    }    // 异常通知  110    @AfterThrowing(通用切点()    //@AfterThrowing("execution(* com.powernode.spring6.service..*(..))")    public void afterThrowingAdvice(){        System.out.println(异常通知);    }    // 最终通知 (finally句块中的通知)   110    @After(通用切点()    //@After("execution(* com.powernode.spring6.service..*(..))")    public void afterAdvice(){        System.out.println(“最终通知”);    }}
package com.powernode.spring6.service;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.springframework.core.annotation.Order;import org.springframework.stereotype.Component;//基于AspectJ的AOP注释开发//安全截面   111@Aspect@Component@Order(0) ///截面通知的顺序是,数字越小,优先级越高  111public class SecurityAspect {    //通知+切点    //@Before("execution(* com.powernode.spring6.service..*(..))") //切点    ///使用LogAspect切面类中定义的  通用切点()()) 表达式方法   112    ///需要引入注册 com.powernode.spring6.service.LogAspect    @Before("com.powernode.spring6.service.LogAspect.通用切点()())")    public void beforeAdvice(){        System.out.println(前置通知:安全...");    }}
//基于AspectJ的AOP注释开发  108    @Test    public void testBefore(){        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");        /*UserService userService = applicationContext.getBean("userService", UserService.class);        userService.login();        userService.logout();*/        ///要OrderService也被加强  109        OrderService orderService = applicationContext.getBean("orderService", OrderService.class);        orderService.generate();    }

基于AspectJ的AOP注解式开发_xml_05

8.小知识点113

JoinPointjoinPoint在Spring容器调用此方法时自动传输。113

我们可以直接使用它。用这个JoinPointjoinpoint做什么?

Signaturesignature=joinPoint.getSignature();//获取目标方法的签名。

一种方法的具体信息可以通过方法签名获得。

获取目标方法的方法名称。例如

System.out.println(目标方法的方法名称:"+joinPoint.getSignature().getName());

事实上,参数JoinPointjoinPoint不仅限于环绕通知

9.全注释开发1144

写一个类,用大量的注释代替spring的配置文件,spring配置文件消失了,如下所示

package com.powernode.spring6.service;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.EnableAspectJAutoProxy;//全注解开发   114@Configuration //代表这一类,spring配置文件@ComponentScan({"com.powernode.spring6.service"})// 组件扫描@EnableAspectJAutoProxy(proxyTargetClass = true)///自动代理publictj打开aspectj class Spring6Config {}

测试

//全注解开发  114    @Test    public void testNoXml(){        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Spring6Config.class);        OrderService orderService = applicationContext.getBean("orderService", OrderService.class);        orderService.generate();    }

基于AspectJ的AOP注解式开发_xml_06