JAVAEE实践系列
1. Spring入门程序2. 依赖注入实现方法
3. Bean基于注解方式的装配
4.AspectJ开发(AOP框架)
AspectJ开发(AOP框架)
前言
AspectJ是基于java语言的AOP框架,它提供了强大的AOP功能。使用AspectJ实现AOP的方法有两种:①基于XML的声明式AspectJ②基于注解的声明式AspectJ
接下来的内容对这两种开发方式进行讲解。
要使用到的AOP术语
- AOP:即面向切面编程,是oop(面向对象编程)的一种补充。AOP采取横向抽取机制,将分散在各个方法中的重复代码提取出来,然后在程序编译或者运行时,再将这些提取出来的代码应用到需要执行的地方。
- Aspect(切面):是指封装的用于横向插入系统功能的类(如事务,日志等),需要在配置文件中通过元素指定。
- JoinPoint(连接点):是指程序执行过程中的某个阶段点,它实际上是对象的一个操作,例如方法的调用和异常的抛出,在spring AOP中,连接点就是方法的调用。
- PointCut(切入点):是指切面和程序流程交叉点,即那些需要处理的连接点,通常在程序中,切入点是指类或方法名。
- A的vice(通知/增强处理):是指aop框架在特定的切入点执行的增强处理,即在定义好的切入点处要执行的程序代码,可以理解为切面类的代码,是切面类的具体实现。
- Target Object(目标对象):是指所有被通知的对象,也称被增强的对象。如果aop框架是动态的aop实现,那么该对象就是一个被代理的对象。
- Proxy(代理):是指通知应用到目标对象之后,被动态创建的对象(代理对象)。
- Weaving(织入):将切面代码插入到目标对象上,从而生成代理对象的过程。
一、基于XML的声明式AspectJ
1. 概念
- 基于XML的声明式AspectJ:通过xml文件来定义切面、切入点及通知,切面、切入点及通知都必须定义在<aop:config>元素内。
- 配置切面<aop:aspect>:
属性名称 | 描述 |
---|---|
id | 切面的唯一标识名称 |
ref | 引用普通的Spring Bean |
- 配置切入点<aop:pointcut>:在<aop:config>中定义是全局切入点,在<aop:aspect>中定义是该切面的切入点。
属性名称 | 描述 |
---|---|
id | 切入点的唯一标识名称 |
expression | 指定切入点关联的切入点表达式 |
- 配置通知:前置通知:<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();
}
}
五、结语
好好学习!天天向上!快考试了,加油!