JAVA aop 实现的几种方式,以及原理解析

  • Post author:
  • Post category:java





本文主要讲解,AOP的几种不同实现方式,以及几种实现方式之间的关联,最后在补充AOP实现机制和原理



AOP术语,知识点



切点 Poincut

指具体的切口 , 譬如指定的具体包、层级、类、方法 execution(* cn.burcher.service.UserServiceImpl.*(..));
或者是 指定的注解@annotation(log)



通知 Advice

切面的具体实现(增强),通知有五中不同的形式,决定了在不同的时间段做具体的实现,又或者说增强



切面 Aspect

 通知和切点的结合,通知和切点共同定义了切面的全部内容,它是干什么的,什么时候在哪执行



织入(Weaving)

把切面加入程序代码的过程。切面在指定的连接点被织入到目标对象中,在目标对象的生命周期里有多个点可以进行织入:

  1. 编译期:切面在目标类编译时被织入,这种方式需要特殊的编译器
  2. 类加载期:切面在目标类加载到JVM时被织入,这种方式需要特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码
  3. 运行期:切面在应用运行的某个时刻被织入,一般情况下,在织入切面时,AOP容器会为目标对象动态创建一个代理对象,Spring AOP就是以这种方式织入切面的。



实现方式



通用环境

业务接口

package cn.burcher.service;

public interface UserService {
    void add();
    void delete();
    void update();
    void query();
}

业务实现类

package cn.burcher.service;

public class UserServiceImpl implements UserService{
    public void add() {
        System.out.println("添加了一个用户");
    }

    public void delete() {
        System.out.println("删除了一个用户");
    }

    public void update() {
        System.out.println("更新了一个用户");
    }

    public void query() {
        System.out.println("查询了一个用户");
    }
}



1.使用spring提供的接口


前置通知:MethodBeforeAdvice

后置通知:AfterReturningAdvice

前置通知

package cn.burcher.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class LogBefore implements MethodBeforeAdvice {
    
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getSimpleName()+"的"+method.getName()+"方法执行了");
    }
}

后置通知

package cn.burcher.log;

import org.springframework.aop.AfterAdvice;
import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class LogAfter implements AfterReturningAdvice {

    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getSimpleName()+"的"+method.getName()+"方法执行了");
    }
}

在beans.xml注册(注意:使用aop需要导入配置)

<bean id="logBefore" class="cn.burcher.log.LogBefore"/>
<bean id="logAfter" class="cn.burcher.log.LogAfter"/>

<bean id="userService" class="cn.burcher.service.UserServiceImpl"/>

<!--aop配置类-->
<aop:config>
	<!--定义一个切入点-->
    <aop:pointcut id="point" expression="execution(* cn.burcher.service.UserServiceImpl.*(..))"/>
    <!--对切入点 执行 -->
    <aop:advisor advice-ref="logAfter" pointcut-ref="point" />
    <aop:advisor advice-ref="logBefore" pointcut-ref="point" />
</aop:config>

结果测试

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
    }
}
结果:
UserServiceImpl的add方法执行了
添加了一个用户
UserServiceImpl的add方法执行了



2.使用自定义切面类

编写自定义切面类

package cn.burcher.pointcut;

public class Log {
    public void before(){
        System.out.println("方法执行前");
    }

    public void after(){
        System.out.println("方法执行后");
    }
}

在beans.xml中配置

<bean id="log" class="cn.burcher.pointcut.Log"/>
<aop:config>
    <aop:pointcut id="point" expression="execution(* cn.burcher.service.UserServiceImpl.*(..))"/>
    <aop:aspect ref="log">
        <aop:after method="after" pointcut-ref="point"/>
        <aop:before method="before" pointcut-ref="point"/>
    </aop:aspect>
</aop:config>

测试类不变,再次执行

结果:
方法执行前
添加了一个用户
方法执行后



3.使用注解实现切面

@Aspect 相当于aop:config

@Before相当于aop:before

package cn.burcher.pointcut;

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


@Aspect
public class AnnotationPointcut {

    @Before("execution(* cn.burcher.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("========方法执行前=========");
    }


    @After("execution(* cn.burcher.service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("========方法执行后=========");
    }

    @Around("execution(* cn.burcher.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("---------环绕前---------");
        pjp.proceed();
        System.out.println("---------环绕后---------");
    }
}


测试类不改,再次执行


---------环绕前---------
========方法执行前=========
添加了一个用户
========方法执行后=========
---------环绕后---------



4.注解 + aop 实现动态切面

通过上面的几个例子,都简单的知道了Aop的实现方式,但是上面三种也有缺陷,都是通过固定的层级来做切面,没有实现我们真正想要的,通过加注解动态实现aop,下面展示如何通过注解+aop实现动态配置

声明切面注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Log {
    String value() default "";
}

配置切面,声明注解为连接点,而不是具体的包层级

@Aspect
@Component
public class AnnotationAop {

    @Pointcut(value = "@annotation(log)", argNames = "log")
    public void pointcut(Log log) {
    }

    @Around(value = "pointcut(log)", argNames = "joinPoint,log")
    public Object around(ProceedingJoinPoint joinPoint, Log log) throws Throwable {
        try {
            System.out.println(log.value());
            System.out.println("around");
            return joinPoint.proceed();
        } catch (Throwable throwable) {
            throw throwable;
        } finally {
            System.out.println("around");
        }
    }
}
  @Before("@annotation(com.jiuxian.annotation.Log)")
  public void before(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Log log = method.getAnnotation(Log.class);
        System.out.println("注解式拦截 " + log.value());
    }

Service 方法实现测试

public interface UserService {

    String save(String user);

    void testAnnotationAop();
}


@Service
public class UserServiceImpl implements UserService {

    @Override
    public String save(String user) {
        System.out.println("保存用户信息");
        if ("a".equals(user)) {
            throw new RuntimeException();
        }
        return user;
    }

    @Log(value = "test")
    @Override
    public void testAnnotationAop() {
        System.out.println("testAnnotationAop");
    }
}

参考文章 :



版权声明:本文为qq_39355721原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。