AspectJ开发及其两种实现方法

JAVAEE实践系列

1. Spring入门程序
2. 依赖注入实现方法
3. Bean基于注解方式的装配
4.AspectJ开发(AOP框架)


前言

AspectJ是基于java语言的AOP框架,它提供了强大的AOP功能。
使用AspectJ实现AOP的方法有两种:①基于XML的声明式AspectJ②基于注解的声明式AspectJ
接下来的内容对这两种开发方式进行讲解。

要使用到的AOP术语

  1. AOP:即面向切面编程,是oop(面向对象编程)的一种补充。AOP采取横向抽取机制,将分散在各个方法中的重复代码提取出来,然后在程序编译或者运行时,再将这些提取出来的代码应用到需要执行的地方
  2. Aspect(切面):是指封装的用于横向插入系统功能的类(如事务,日志等),需要在配置文件中通过元素指定。
  3. JoinPoint(连接点):是指程序执行过程中的某个阶段点,它实际上是对象的一个操作,例如方法的调用和异常的抛出,在spring AOP中,连接点就是方法的调用
  4. PointCut(切入点):是指切面和程序流程交叉点,即那些需要处理的连接点,通常在程序中,切入点是指类或方法名。
  5. A的vice(通知/增强处理):是指aop框架在特定的切入点执行的增强处理,即在定义好的切入点处要执行的程序代码,可以理解为切面类的代码,是切面类的具体实现。
  6. Target Object(目标对象):是指所有被通知的对象,也称被增强的对象。如果aop框架是动态的aop实现,那么该对象就是一个被代理的对象
  7. Proxy(代理):是指通知应用到目标对象之后,被动态创建的对象(代理对象)。
  8. Weaving(织入):将切面代码插入到目标对象上,从而生成代理对象的过程。

一、基于XML的声明式AspectJ

1. 概念

  1. 基于XML的声明式AspectJ:通过xml文件来定义切面、切入点及通知,切面、切入点及通知都必须定义在<aop:config>元素内。
  2. 配置切面<aop:aspect>:
属性名称描述
id切面的唯一标识名称
ref引用普通的Spring Bean
  1. 配置切入点<aop:pointcut>:在<aop:config>中定义是全局切入点,在<aop:aspect>中定义是该切面的切入点。
属性名称描述
id切入点的唯一标识名称
expression指定切入点关联的切入点表达式
  1. 配置通知:前置通知:<aop:before>,后置通知:<aop:after-returning>,环绕通知:<aop:around>,异常通知:<aop:after-throwing>,最终通知:<aop:after>
    注意区别:后置通知只有正常情况会执行,异常通知在任何情况下都要执行。
属性名称描述
pointcut切入点表达式
pointcut-ref指定一个已经存在的切入点名称
method指定一个方法名,将切面bean中的该方法转换为增强处理
throwing只对<aop:after-throwing>有效,指定一个形参名,异常通知方法可以通过该形参访问目标方法中所抛出的异常
returning只对<aop:after-returning>有效,指定一个形参名,后置通知方法可以通过该形参访问目标方法的返回值

2. 步骤及截图

1. 导入AspectJ框架相关的jar包

在这里插入图片描述

2.创建web项目,创建jdk包,编写userDao接口以及其实现类,定义两个方法并实现

在这里插入图片描述
在这里插入图片描述

3. 创建aspectj.xml包,创建切面类MyAspect,在类中分别定义不同类型的通知。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4. 在aspectj.xml包中创建配置文件applicationContext.xml,编写相关配置在这里插入图片描述

在这里插入图片描述

5. 在aspectj.xml包中创建测试类TestXmlAspectj

在这里插入图片描述

二、基于注解的声明式AspectJ

1. 概念

@Aspect,@PointCut,@Before,@AfterReturning,@Aroud,@AftertThrowing,@After分别对应配置中的内容

2. 步骤及截图

1. 创建aspectJ.annotation包,将前面切面类MyAspect复制过来,加以修改

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. 修改jdk包中的目标类userDaoImpl

在这里插入图片描述

3. 在aspectJ.annotation包中,创建配置文件applicationContext

在这里插入图片描述

4. 在aspectJ.annotation包中,创建测试类TestAnnotation,只修改配置文件的类路径即可

在这里插入图片描述

三、输出结果

在这里插入图片描述

四、整体代码

1. 接口类:jdk包中UserDao.java

package com.itheima.jdk;

public interface UserDao {
	public void addUser();
	public void deleteUser();
}

2. 实现类:jdk包中UserDaoImpl.java

①基于xml

package com.itheima.jdk;

public class UserDaoImpl implements UserDao {

	@Override
	public void addUser() {
		System.out.println("添加用户");
	}

	@Override
	public void deleteUser() {
		System.out.println("删除用户");
	}

}

②基于注解

package com.itheima.jdk;

import org.springframework.stereotype.Repository;

@Repository("userDao")
public class UserDaoImpl implements UserDao {

	@Override
	public void addUser() {
		System.out.println("添加用户");
	}

	@Override
	public void deleteUser() {
		System.out.println("删除用户");
	}

}

3. 切面类:aspectJ包中MyAspect.java

①基于xml

package com.itheima.aspectj.xml;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

//切面类,在该类中编写通知
public class MyAspect {
	//1. 前置通知
	public void myBefore(JoinPoint joinPoint) {
		System.out.print("前置通知:模拟执行权限检查");
		System.out.print("目标类是:"+joinPoint.getTarget());
		System.out.println(",被织入增强处理的目标方法为:"+joinPoint.getSignature().getName());
	}
	//2. 后置通知
	public void myAfterReturning(JoinPoint joinPoint) {
		System.out.print("后置通知:模拟记录日志...");
		System.out.println(",被织入增强处理的目标方法为:"+joinPoint.getSignature().getName());
	}
	/**
	 * 3. 环绕通知
	 * ProceedingJoinPoint是JoinPoint的子接口,表示可以执行目标方法
	 * ①必须是Object返回类型
	 * ②必须接收一个参数,类型是ProceedingJoinPoint
	 * ③必须throws Throwable
	 * 
	 */
	public Object myAround(ProceedingJoinPoint proceedingjoinPoint) 
	throws Throwable{
		//开始
		System.out.print("环绕开始:执行目标方法之前,模拟开启事务...");
		//执行当前目标方法
		Object obj = proceedingjoinPoint.proceed();
		//结束
		System.out.println("环绕结束:执行目标方法之后,模拟关闭事务...");
		//返回一个Object类型的返回值,且必须得写throws Throwable
		return obj;
	}
	//异常通知
	public void myAfterThrowing(JoinPoint joinPoint,Throwable e) {
		System.out.print("异常通知:出错了"+e.getMessage());
		System.out.println(",被织入增强处理的目标方法为:"+joinPoint.getSignature().getName());
	}
	//最终通知
	public void myAfter() {
		System.out.print("最终通知:模拟方法结束后释放资源");
	}
}

②基于注解

package com.itheima.aspectj.annotation;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

//切面类
@Aspect
@Component
public class MyAspect {
	/*
	 * 因为基于xml中,每一个方法都要写id,pointCut-ref(属于哪个切入点的名称)
	 * 所以在注解中要提前写切入点方法以及切入点表达式,其余方法的注解中要写切入点名称
	 * */
	//定义切入点表达式(在切入点的方法myPointCut()上面标注)
	@Pointcut("execution(* com.itheima.jdk.*.*(..))")
	//使用一个返回值为void、方法体为空的方法来命名切入点
	private void myPointCut(){}
	//1. 前置通知
	@Before("myPointCut()")
	public void myBefore(JoinPoint joinPoint) {
		System.out.print("前置通知:模拟执行权限检查");
		System.out.print("目标类是:"+joinPoint.getTarget());
		System.out.println(",被织入增强处理的目标方法为:"+joinPoint.getSignature().getName());
	}
	//2. 后置通知
	@AfterReturning("myPointCut()")
	public void myAfterReturning(JoinPoint joinPoint) {
		System.out.print("后置通知:模拟记录日志...");
		System.out.println(",被织入增强处理的目标方法为:"+joinPoint.getSignature().getName());
	}
	/**
	 * 3. 环绕通知
	 * ProceedingJoinPoint是JoinPoint的子接口,表示可以执行目标方法
	 * ①必须是Object返回类型
	 * ②必须接收一个参数,类型是ProceedingJoinPoint
	 * ③必须throws Throwable
	 * 
	 */
	@Around("myPointCut()")
	public Object myAround(ProceedingJoinPoint proceedingjoinPoint) 
	throws Throwable{
		//开始
		System.out.print("环绕开始:执行目标方法之前,模拟开启事务...");
		//执行当前目标方法
		Object obj = proceedingjoinPoint.proceed();
		//结束
		System.out.println("环绕结束:执行目标方法之后,模拟关闭事务...");
		//返回一个Object类型的返回值,且必须得写throws Throwable
		return obj;
	}
	//异常通知
	@AfterThrowing(value="myPointCut()",throwing="e")
	public void myAfterThrowing(JoinPoint joinPoint,Throwable e) {
		System.out.print("异常通知:出错了"+e.getMessage());
		System.out.println(",被织入增强处理的目标方法为:"+joinPoint.getSignature().getName());
	}
	//最终通知
	@After("myPointCut()")
	public void myAfter() {
		System.out.print("最终通知:模拟方法结束后释放资源");
	}
}

4. 配置文件:aspectJ包中applicationContext.xml

①基于xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
        <!-- 1. 目标类 -->
        <bean id="userDao" class="com.itheima.jdk.UserDaoImpl"></bean>
        <!-- 2. 切面类 -->
        <bean id="myAspect" class="com.itheima.aspectj.xml.MyAspect"></bean>
        <!-- 3. aop编程 -->
       	<aop:config>
       		<!-- 配置切面,ref:属于哪个切面类 -->
       		<aop:aspect ref="myAspect">
       			<!-- 3.1 配置切入点,编写切入点关联的切入点表达式(针对哪个包中哪些方法)-->
       			<aop:pointcut id="myPointCut" expression="execution(* com.itheima.jdk.*.*(..))"/>
       			<!-- 3.2 配置通知 -->
       			<!-- 3.2.1 前置通知,method是指定切面类中的该方法转换为增强处理,
       			pointcut-ref指定属于哪个切入点 -->
       			<aop:before method="myBefore" pointcut-ref="myPointCut"/>
       			<!-- 3.2.2 后置通知,在方法返回之后执行,就可以获得返回值
       			returning属性:指定一个形参名,通过该形参可以获得目标方法的返回值 -->
       			<aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut"
       			returning="returnVal"/>
       			<!-- 3.2.3 环绕通知 -->
       			<aop:around method="myAround" pointcut-ref="myPointCut"/>
       			<!-- 3.2.4 异常通知
       			throwing属性:指定一个形参名,通过该形参可以获得目标方法所抛出的异常-->
       			<aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/>
       			<!-- 3.2.5 最终通知 -->
       			<aop:after method="myAfter" pointcut-ref="myPointCut"/>
       		</aop:aspect>
       	</aop:config>
</beans>

①基于注解

<?xml version="1.0" encoding="UTF-8"?>
<!-- aop基于xml中命名空间要改aop,基于注解时要改context -->
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.3.xsd">
        <!-- 指定需要扫描的包,使注解生效(因为还需要aspect.xml的包,所以不能写aspect.annotation) -->
        <context:component-scan base-package="com.itheima"/>
        <!-- 启动基于注解的声明式aspectJ支持 -->
        <aop:aspectj-autoproxy/>
</beans>

5. 测试类:aspectJ包中TsetXmlAspectJ.java

①基于xml

package com.itheima.aspectj.xml;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.itheima.jdk.UserDao;

public class TsetXmlAspectJ {

	public static void main(String[] args) {
		String xmlPath = "com/itheima/aspectj/xml/applicationContext.xml";
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
		UserDao userDao = (UserDao) applicationContext.getBean("userDao");
		userDao.addUser();
	}

}

①基于注解

package com.itheima.aspectj.annotation;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.itheima.jdk.UserDao;

public class TestAnnotation {
	public static void main(String[] args) {
		String xmlPath = "com/itheima/aspectj/annotation/applicationContext.xml";
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
		UserDao userDao = (UserDao) applicationContext.getBean("userDao");
		userDao.addUser();
	}
}


五、结语

好好学习!天天向上!快考试了,加油!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
AspectJ是一种基于Java语言的面向切面编程(AOP)的扩展。它通过在编译期间将切面代码织入到目标代码中,实现了对横切关注点的模块化和重用。 AspectJ实现原理主要包括以下几个步骤: 1. 编译器扩展:AspectJ通过扩展Java编译器,添加了对切面语法的支持。在编译Java源代码时,AspectJ编译器会解析切面文件中的切点和通知,并将其转换为字节码文件。 2. 切点匹配:AspectJ使用切点表达式来定义切点,切点表达式可以根据方法的签名、注解、访问修饰符等条件来匹配目标代码中的连接点。AspectJ编译器会根据切点表达式在目标代码中找到匹配的连接点。 3. 通知织入:一旦找到匹配的连接点,AspectJ会将切面中定义的通知代码织入到目标代码中。通知可以分为前置通知、后置通知、环绕通知等,它们会在目标代码执行前、后或者替代目标代码执行。 4. 字节码增强:AspectJ通过修改目标代码的字节码来实现通知的织入。它会在目标代码中插入切面代码,并调整字节码中的跳转指令,以确保通知的正确执行顺序。 5. 运行时支持:AspectJ还提供了运行时的支持,包括切面实例的创建和管理、连接点的动态绑定、通知的执行等。在程序运行时,AspectJ会根据切面的定义和连接点的匹配情况来决定是否执行通知。 总的来说,AspectJ通过编译器扩展和字节码增强的方式,将切面代码织入到目标代码中,实现了对横切关注点的模块化和重用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值