基于@AspectJ注解进行AOP编程

2020-06-15  本文已影响0人  NisyCoding

一. 基于注解的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>
上一篇下一篇

猜你喜欢

热点阅读