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

本文主要讲解,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");
    }
}

参考文章 :

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java AOP(面向切面编程)实现原理是利用动态代理技术,通过在对象的方法执行前或执行后,或者在抛出异常时,通过切面切入到被代理方法中进行增强操作,实现业务逻辑与横切逻辑的分离,并使得横切逻辑在代码中得以复用。 例如,我们通过使用注解或 XML 文件配置切面,在程序运行时利用织入技术动态地将切面织入到被代理对象的方法中,然后在执行被代理方法之前或之后,执行切面中的操作,比如日志记录、异常处理、性能统计等。 这种实现方式可以通过 JDK 自带的动态代理或者 CGLIB 等第三方的字节码技术实现。在 JDK 动态代理方式下,AOP 框架会在运行时生成代理类,在代理类中实现切面逻辑,在代理类中调用被代理对象的方法。在 CGLIB 方式下,AOP 框架可以动态生成一个继承自被代理对象的子类,在子类中实现切面逻辑,并且也进行了重写被代理对象的方法。 注意,AOP实现原理并不是将切面代码插入到被代理对象的字节码中,而是通过字节码增强技术生成新的类来完成代码的织入操作,这样可以防止修改原有代码的安全问题,也可以保持代码的整洁性和可读性。 综上所述,Java AOP实现原理是通过动态代理或字节码增强技术,在被代理方法的前后或异常抛出时,在程序运行时动态地将切面织入到被代理对象的方法中,实现业务逻辑和横切逻辑的分离,提高代码的可重用性和可维护性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值