基于@AspectJ注解进行AOP编程

一. 基于注解的AOP变成开发步骤

  • 原始对象
  • 额外功能
  • 切入点
  • 组装切面

二. 开发步骤

2.1 创建带有@Aspect的切面类

package com.baizhiedu.aspectj;


import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

/**
 * 通过切面类定义额外功能,@Around注解,同时也定义了切入点,around当中的参数
 */

/**
 * @Aspect注解:
 * 表示这是一个切面类:额外功能+切入点
 * 作用等同于:实现MethodInterceptor接口,重写invoke方法
 */

/**
 * @Aroud注解:
 * 相当有之前的额外功能,around()等同于invoke()
 */


@Aspect
public class MyAspectJ {

    @Around("execution(* login(..))")
    public Object arround(ProceedingJoinPoint proceedingJoinPoint){
        try {
            System.out.println("注解额外功能...");
            Object proceed = proceedingJoinPoint.proceed();
            return proceed;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }

}

@Aspect注解:表示这是一个切面类,切面类需要包含:切入点+额外功能

2.2 添加额外功能

之前,我们创建的额外功能,是基于MethodInterceptor接口,实现该接口,重写接口中的invoke(),在invoke方法中实现了原始方法的运行+额外功能的编写;
如今,我们需要在需要额外功能的方法上,添加@Around注解,然后方法参数:ProceddingJoinPoint,这里就相当于之前invoke方法中的MethodInvocation.

    @Around("暂时这里不需要填写")
    public Object arround(ProceedingJoinPoint proceedingJoinPoint){
        try {
            System.out.println("注解额外功能...");
            Object proceed = proceedingJoinPoint.proceed();
            return proceed;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }

2.3 配置切入点

@Around("execution(* login(..))")

2.4 初始化切面对象.MyAspect

    <bean class="com.baizhiedu.aspectj.MyAspectJ" id="around"></bean>

2.5 重点:告诉spring,我们是基于注解开发的

<!--    告诉spring,我们是基于注解开发的-->
    <aop:aspectj-autoproxy/>

三. 测试结果:

注解额外功能...
UserServiceImpl.login
UserServiceImpl.register

四. 完成代码:

4.1 UserService

package com.baizhiedu.aspectj;

import com.baizhiedu.proxy.User;

public interface UserService {

    void login(String name, int age);

    void register(User user);
}

4.2 UserServiceImpl

package com.baizhiedu.aspectj;


import com.baizhiedu.proxy.User;

/**
 * 原始对象
 */
public class UserServiceImpl implements UserService {


    public void login(String name, int age) {
        System.out.println("UserServiceImpl.login");
    }

    public void register(User user) {
        System.out.println("UserServiceImpl.register");
    }
}

4.3 MyAspect切面类:

package com.baizhiedu.aspectj;


import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

/**
 * 通过切面类定义额外功能,@Around注解,同时也定义了切入点,around当中的参数
 */

/**
 * @Aspect注解:
 * 表示这是一个切面类:额外功能+切入点
 * 作用等同于:实现MethodInterceptor接口,重写invoke方法
 */

/**
 * @Aroud注解:
 * 相当有之前的额外功能,around()等同于invoke()
 */


@Aspect
public class MyAspectJ {

    @Around("execution(* login(..))")
    public Object arround(ProceedingJoinPoint proceedingJoinPoint){
        try {
            System.out.println("注解额外功能...");
            Object proceed = proceedingJoinPoint.proceed();
            return proceed;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }

}

4.4 ApplicationContext.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:p="http://www.springframework.org/schema/p"
       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="userService" class="com.baizhiedu.aspectj.UserServiceImpl"></bean>

    <bean class="com.baizhiedu.aspectj.MyAspectJ" id="around"></bean>

<!--    告诉spring,我们是基于注解开发的-->
    <aop:aspectj-autoproxy/>

</beans>

4.5 Test测试文件

package com.baizhiedu.aspectj;

import com.baizhiedu.proxy.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

    public static void main(String[] args) {
        ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext7.xml");
        UserService userService = (UserService) ctx.getBean("userService");
        userService.login("Nisy",20);
        userService.register(new User());
    }
}

5. 基于注解的AOP编程注意事项:

5.1 细节1:切入点复用

一个切面类中,有可能出现一个log的额外功能,一个事务等多个额外功能,此时我们怎么办?死办法就是,如下:

package com.baizhiedu.aspectj;


import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

/**
 * 通过切面类定义额外功能,@Around注解,同时也定义了切入点,around当中的参数
 */

/**
 * @Aspect注解:
 * 表示这是一个切面类:额外功能+切入点
 * 作用等同于:实现MethodInterceptor接口,重写invoke方法
 */

/**
 * @Aroud注解:
 * 相当有之前的额外功能,around()等同于invoke()
 */


@Aspect
public class MyAspectJ {
   @Around("execution(* login(..))")
   public Object arround(ProceedingJoinPoint proceedingJoinPoint){
        try {
            System.out.println("注解额外功能...");
            Object proceed = proceedingJoinPoint.proceed();
            return proceed;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }

    @Around("execution(* login(..))")
    public Object arround1(ProceedingJoinPoint proceedingJoinPoint){
        try {
            System.out.println("事务...");
            Object proceed = proceedingJoinPoint.proceed();
            return proceed;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }

}

观察以上代码,我们发现,代码出现了冗余,该怎么办呢?怎么进行代码复用呢?
答:基于注解@JonintCut定义切入点方法.没有代码块,没有返回值

    @Pointcut("execution(* login(..))")
    public void pointCut(){}
package com.baizhiedu.aspectj;


import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

/**
 * 通过切面类定义额外功能,@Around注解,同时也定义了切入点,around当中的参数
 */

/**
 * @Aspect注解:
 * 表示这是一个切面类:额外功能+切入点
 * 作用等同于:实现MethodInterceptor接口,重写invoke方法
 */

/**
 * @Aroud注解:
 * 相当有之前的额外功能,around()等同于invoke()
 */


@Aspect
public class MyAspectJ {

    //切入点复用:在切面类中定义一个函数,上面@PointCut注解,通过这种方式,定义切入点表达式,后续更加有利于切入点复用
    @Pointcut("execution(* login(..))")
    public void pointCut(){}


//    @Around("execution(* login(..))")
    @Around(value = "pointCut()")
    public Object arround(ProceedingJoinPoint proceedingJoinPoint){
        try {
            System.out.println("注解额外功能...");
            Object proceed = proceedingJoinPoint.proceed();
            return proceed;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }

    @Around(value = "pointCut()")
//    @Around("execution(* login(..))")
    public Object arround1(ProceedingJoinPoint proceedingJoinPoint){
        try {
            System.out.println("事务...");
            Object proceed = proceedingJoinPoint.proceed();
            return proceed;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }

}

结果显示:

C:\Tools\jdk1.8\bin\java.exe -javaagent:D:\Browser-DownLoad\ideaIU-2019.3.win\lib\idea_rt.jar=56822:D:\Browser-DownLoad\ideaIU-2019.3.win\bin -Dfile.encoding=UTF-8 -classpath C:\Tools\jdk1.8\jre\lib\charsets.jar;C:\Tools\jdk1.8\jre\lib\deploy.jar;C:\Tools\jdk1.8\jre\lib\ext\access-bridge-64.jar;C:\Tools\jdk1.8\jre\lib\ext\cldrdata.jar;C:\Tools\jdk1.8\jre\lib\ext\dnsns.jar;C:\Tools\jdk1.8\jre\lib\ext\jaccess.jar;C:\Tools\jdk1.8\jre\lib\ext\jfxrt.jar;C:\Tools\jdk1.8\jre\lib\ext\localedata.jar;C:\Tools\jdk1.8\jre\lib\ext\nashorn.jar;C:\Tools\jdk1.8\jre\lib\ext\sunec.jar;C:\Tools\jdk1.8\jre\lib\ext\sunjce_provider.jar;C:\Tools\jdk1.8\jre\lib\ext\sunmscapi.jar;C:\Tools\jdk1.8\jre\lib\ext\sunpkcs11.jar;C:\Tools\jdk1.8\jre\lib\ext\zipfs.jar;C:\Tools\jdk1.8\jre\lib\javaws.jar;C:\Tools\jdk1.8\jre\lib\jce.jar;C:\Tools\jdk1.8\jre\lib\jfr.jar;C:\Tools\jdk1.8\jre\lib\jfxswt.jar;C:\Tools\jdk1.8\jre\lib\jsse.jar;C:\Tools\jdk1.8\jre\lib\management-agent.jar;C:\Tools\jdk1.8\jre\lib\plugin.jar;C:\Tools\jdk1.8\jre\lib\resources.jar;C:\Tools\jdk1.8\jre\lib\rt.jar;E:\myproject\spring5.x\target\classes;D:\WorkTools\apache-maven-3.2.3\repository\org\springframework\spring-context\5.1.4.RELEASE\spring-context-5.1.4.RELEASE.jar;D:\WorkTools\apache-maven-3.2.3\repository\org\springframework\spring-beans\5.1.4.RELEASE\spring-beans-5.1.4.RELEASE.jar;D:\WorkTools\apache-maven-3.2.3\repository\org\springframework\spring-core\5.1.4.RELEASE\spring-core-5.1.4.RELEASE.jar;D:\WorkTools\apache-maven-3.2.3\repository\org\springframework\spring-jcl\5.1.4.RELEASE\spring-jcl-5.1.4.RELEASE.jar;D:\WorkTools\apache-maven-3.2.3\repository\org\springframework\spring-expression\5.1.4.RELEASE\spring-expression-5.1.4.RELEASE.jar;D:\WorkTools\apache-maven-3.2.3\repository\org\springframework\spring-webmvc\5.1.14.RELEASE\spring-webmvc-5.1.14.RELEASE.jar;D:\WorkTools\apache-maven-3.2.3\repository\org\springframework\spring-web\5.1.14.RELEASE\spring-web-5.1.14.RELEASE.jar;D:\WorkTools\apache-maven-3.2.3\repository\org\slf4j\slf4j-log4j12\1.7.25\slf4j-log4j12-1.7.25.jar;D:\WorkTools\apache-maven-3.2.3\repository\org\slf4j\slf4j-api\1.7.25\slf4j-api-1.7.25.jar;D:\WorkTools\apache-maven-3.2.3\repository\log4j\log4j\1.2.17\log4j-1.2.17.jar;D:\WorkTools\apache-maven-3.2.3\repository\mysql\mysql-connector-java\5.1.48\mysql-connector-java-5.1.48.jar;D:\WorkTools\apache-maven-3.2.3\repository\org\springframework\spring-aop\4.3.27.RELEASE\spring-aop-4.3.27.RELEASE.jar;D:\WorkTools\apache-maven-3.2.3\repository\org\aspectj\aspectjrt\1.9.5\aspectjrt-1.9.5.jar;D:\WorkTools\apache-maven-3.2.3\repository\org\aspectj\aspectjweaver\1.9.5\aspectjweaver-1.9.5.jar com.baizhiedu.aspectj.Test
2020-06-16 12:38:00 DEBUG ClassPathXmlApplicationContext:590 - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@6d9c638
2020-06-16 12:38:01 DEBUG XmlBeanDefinitionReader:395 - Loaded 3 bean definitions from class path resource [applicationContext7.xml]
2020-06-16 12:38:01 DEBUG DefaultListableBeanFactory:213 - Creating shared instance of singleton bean 'org.springframework.aop.config.internalAutoProxyCreator'
2020-06-16 12:38:01 DEBUG DefaultListableBeanFactory:213 - Creating shared instance of singleton bean 'userService'
2020-06-16 12:38:01 DEBUG ReflectiveAspectJAdvisorFactory:250 - Found AspectJ method: public java.lang.Object com.baizhiedu.aspectj.MyAspectJ.arround(org.aspectj.lang.ProceedingJoinPoint)
2020-06-16 12:38:01 DEBUG ReflectiveAspectJAdvisorFactory:250 - Found AspectJ method: public java.lang.Object com.baizhiedu.aspectj.MyAspectJ.arround1(org.aspectj.lang.ProceedingJoinPoint)
2020-06-16 12:38:01 DEBUG AnnotationAwareAspectJAutoProxyCreator:521 - Creating implicit proxy for bean 'userService' with 0 common interceptors and 3 specific interceptors
2020-06-16 12:38:01 DEBUG JdkDynamicAopProxy:118 - Creating JDK dynamic proxy: target source is SingletonTargetSource for target object [com.baizhiedu.aspectj.UserServiceImpl@366647c2]
2020-06-16 12:38:01 DEBUG DefaultListableBeanFactory:213 - Creating shared instance of singleton bean 'around'
注解额外功能...
事务...
UserServiceImpl.login
UserServiceImpl.register

5.2 细节2:动态代理的创建方式:

我们可以在测试类中,打一个端点,看一下,我们拿到的对象,在默认情况下是基于哪一种动态代理呢?下面就验证一下:


image.png

默认情况下,AOP底层的动态代理是基于:"JDK动态代理"

5.2.1 我们可以改变AOP默认的动态代理吗?

可以的,我们在基于注解的情况下,可以在applicationContext.xml配置文件中修改:

<!--    告诉spring,我们是基于注解开发的  proxy-target-class为true,就是把jdk换成cglib代理-->
    <aop:aspectj-autoproxy proxy-target-class="true"/>

5.2.2 如果我们没有基于注解开发,那怎么办呢?

//proxy-target-class="true 就是把jdk动态代理换成cglib动态代理
    <aop:config proxy-target-class="true">
<!--        配置切入点-->
<!--        <aop:pointcut id="pc" expression="execution(* *(..))"/>-->
<!--        <aop:pointcut id="pc" expression="execution(* com.baizhiedu.proxy.UserServiceImpl.login(..))"/>-->
        <aop:pointcut id="pc" expression="@annotation(com.baizhiedu.proxy.Log)"/>
        <!--        配置切面 整合什么? 整合额外功能-->
        <aop:advisor advice-ref="arroud" pointcut-ref="pc"/>
    </aop:config>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351