Spring基于AspectJ开发
AspecU是一个基于Java语言的AOP框架,它提供了强大的AOP功能。Spring 2.0以后,SpringAOP引入了对AspectJ的支持,并允许直接使用AspectJ进行编程,而iSpring自 身的AOP API也尽量与Aspecu保持- -致。 新版本的Spring框架,也建议使用AspecuJ来开发AOP.使用AspecuJ实现AOP有两种方式:
- -种是基于XML的声明式AspectJ,
- 另一种是基于注解的声明式AspecJ。
案例:
给HelloWorld接口的两个实现类的所有方法都加上了代理,代理内容:打印时间,
1、增加一个横切关注点,打印日志
2 只想织入接口中的某些方法
要求:基于xml 实现
- 编写目标角色
public interface HelloWorld {
void printHelloWorld();
void doPrint();
}
public class HelloWorldImpl1 implements HelloWorld {
@Override
public void printHelloWorld() {
System.out.println("执行HelloWorldImpl1.printHelloWorld");
}
@Override
public void doPrint() {
System.out.println("执行HelloWorldImpl1.doPrint");
}
}
public class HelloWorldImpl2 implements HelloWorld {
@Override
public void printHelloWorld() {
System.out.println("执行HelloWorldImpl2.doPrint");
}
@Override
public void doPrint() {
System.out.println("执行HelloWorldImpl2.doPrint");
}
}
- 编写切面类
//切面类
public class TimeHandler {
public void printTime(){
System.out.println("CurrentTime = " + System.currentTimeMillis());
}
}
//切面类
public class LogHandler {
public void LogBefore(){
System.out.println("Log before method");
}
public void LogAfter(){
System.out.println("Log after method");
}
}
- 编写配置文件
<?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: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="helloWorldImpl1" class="com.no5.aspectj.demoxml.HelloWorldImpl1"/>
<bean id="helloWorldImpl2" class="com.no5.aspectj.demoxml.HelloWorldImpl2"/>
<bean id="timeHandler" class="com.no5.aspectj.demoxml.TimeHandler"/>
<bean id="logHandler" class="com.no5.aspectj.demoxml.LogHandler"/>
<aop:config>
<aop:aspect id="log" ref="logHandler" order="2">
<aop:pointcut id="printLog" expression="execution(* com.no5.aspectj.demoxml.HelloWorld.printHelloWorld (..))"/>
<aop:before method="LogBefore" pointcut-ref="printLog"></aop:before>
<aop:after method="LogAfter" pointcut-ref="printLog"/>
</aop:aspect>
<aop:aspect id="time" ref="timeHandler" order="1">
<aop:pointcut id="printTime" expression="execution(* com.no5.aspectj.demoxml.HelloWorld.doPrint (..))"/>
<aop:before method="printTime" pointcut-ref="printTime"></aop:before>
<aop:after method="printTime" pointcut-ref="printTime"></aop:after>
</aop:aspect>
</aop:config>
</beans>
- 测试
public class Test {
public static void main(String[] args){
ApplicationContext ctx = new ClassPathXmlApplicationContext("com/no5/aspectj/demoxml/aop.xml");
HelloWorld hw1 = ctx.getBean("helloWorldImpl1",HelloWorld.class);
HelloWorld hw2 = ctx.getBean("helloWorldImpl2",HelloWorld.class);
hw1.printHelloWorld();
System.out.println();
hw1.doPrint();
System.out.println();
hw2.printHelloWorld();
System.out.println();
hw2.doPrint();
}
}
- 结果
基于XML的声明式AspectJ是指通过XML文件来定义切面、切入点及通知,所有的切面、切入点和通知都必须定义在>aop:config>元素内。
五大通知:
aop:after与aop:after-returning的区别
- 后置通知是在方法正常返回后执行的通知,如果方法没有正
常返-例如抛出异常,则后置通知不会执行。而最终通知无论
如何都会在目标方法调用过后执行,即使目标方法没有正常的
执行完成。 - 后置通知可以通过配置得到返回值,而最终通知无法得到。
- 最终通知也可以额外接收一个JoinPoint参数, 来获取目标对
象和目标方法相关信息,但一定要保证必须是第一个参 数。
●<aop:aspect.../>:配置切面。
●<aop:before../>:配置Before增强处理。
●<aop:after../> :配置After增强处理。
●<aop:after-returing../> :配置AfterReturing增强处理。
●<aop:after-throwing.../> :配置AfterThrowing增强处理
●<aop:around.../> :配置Around增强处理。
●<aop:pointcut.../ >:配置切入点。
案例:
- 目标类:
public interface UserDao {
public void addUser();
public void deleteUser();
}
//目标类
public class UserDaoImpl implements UserDao {
@Override
public void addUser() {
System.out.println("添加用户");
}
@Override
public void deleteUser() {
System.out.println("删除用户");
}
}
- 切面类
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
//切面类,在此类中编写通知
public class MyAspect {
//前置通知
public void myBefore(JoinPoint joinPoint){
System.out.println("前置通知,模拟执行权限检查...");
System.out.println("目标类是:" + joinPoint.getTarget());
System.out.println("被织入增强处理的目标方法为:"+joinPoint.getSignature().getName());
}
//后置通知
public void myAfterReturning(JoinPoint joinPoint){
System.out.println("后置通知,模拟执行权限检查...");
System.out.println("目标类是:" + joinPoint.getTarget());
System.out.println("被织入增强处理的目标方法为:"+joinPoint.getSignature().getName());
}
/**
* 环境通知
* @param proceedingJoinPoint 是JoinPoint子接口,表示可以执行目标方法
* @return 必须是Object的返回类型
* 必须接收一个参数,类型为proceedingJoinPoint
* @throws Throwable
*/
public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//开始
System.out.println("环境开始:执行目标方法之前,模拟开启事物...");
//执行当前目标方法
Object obj = proceedingJoinPoint.proceed();
//结束
System.out.println("环境结束:执行目标方法之后,模拟开启事物...");
return obj;
}
//异常通知
public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
System.out.println("异常通知:" + "出错了" + e.getMessage());
}
//最终通知
public void myAfter(){
System.out.println("最终通知:模拟方法结束后的释放资源...");
}
}
- 编写配置文件
<?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: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">
<!-- 1 目标类 -->
<bean id="userDao" class="com.no5.aspectj.xml.UserDaoImpl" />
<!-- 2 切面 -->
<bean id="myAspect" class="com.no5.aspectj.xml.MyAspect" />
<!-- 3 aop编程 -->
<aop:config>
<!-- 配置切面 -->
<aop:aspect ref="myAspect">
<!-- 3.1 配置切入点,通知最后增强哪些方法 -->
<aop:pointcut id="myPointCut" expression="execution(* com.no5.aspectj.xml.*.* (..))"/>
<!-- 3.2 关联通知Advice和切入点pointCut -->
<!-- 3.2.1 前置通知 -->
<aop:before method="myBefore" pointcut-ref="myPointCut" />
<!-- 3.2.2 后置通知,在方法返回之后执行,就可以获得返回值
returning属性:用于设置后置通知的第二个参数的名称,类型是Object -->
<aop:after-returning method="myAfterReturning"
pointcut-ref="myPointCut" returning="joinPoint" />
<!-- 3.2.3 环绕通知 -->
<aop:around method="myAround" pointcut-ref="myPointCut" />
<!-- 3.2.4 抛出通知:用于处理程序发生异常-->
<!-- * 注意:如果程序没有异常,将不会执行增强 -->
<!-- * throwing属性:用于设置通知第二个参数的名称,类型Throwable -->
<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>
基于注解的声明式AspecJ:
AspecJ注解描述:
以上面基于XML的案例案例用注解实现:
- 目标类:
//目标类
@Repository("userDao")
public class UserDaoImpl implements UserDao {
@Override
public void addUser() {
System.out.println("添加用户");
}
@Override
public void deleteUser() {
System.out.println("删除用户");
}
@Override
public boolean findUser() {
System.out.println("查找用户");
return true;
}
}
- 切面类:
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.JoinPoint;
/**
* 切面类,在此类中编写通知
*/
@Aspect
@Component
public class MyAspect {
//定义切入点表达式
@Pointcut("execution(* com.no5.aspectj.annotation.*.* (..))")
//使用一个返回值为void、方法体为空的方法来命名切入点
private void myPointCut(){}
//前置通知
@Before("myPointCut()")
public void myBefore(JoinPoint joinPoint){
System.out.print("前置通知,模拟执行权限检查...");
System.out.print("目标类是:" + joinPoint.getTarget());
System.out.println("被织入增强处理的目标方法为:"+joinPoint.getSignature().getName());
}
//后置通知
@AfterReturning(pointcut = "myPointCut()")
public void myAfterReturning(JoinPoint joinPoint){
System.out.print("后置通知,模拟记录日志...");
System.out.print("目标类是:" + joinPoint.getTarget());
System.out.println("被织入增强处理的目标方法为:"+joinPoint.getSignature().getName());
}
//后置通知
@AfterReturning(returning = "flag",pointcut = "execution(* 龙素娟229.no5.aspectj.annotation.*.* (..))")
public void myAfterReturning2(JoinPoint joinPoint,boolean flag){
System.out.print("后置通知,模拟执行权限检查...");
System.out.print("目标类是:" + joinPoint.getTarget());
System.out.println("被织入增强处理的目标方法为:"+joinPoint.getSignature().getName() + "返回值为:" +flag);
}
/**
* 环境通知(环绕通知)
* @param proceedingJoinPoint 是JoinPoint子接口,表示可以执行目标方法
* @return 必须是Object的返回类型
* 必须接收一个参数,类型为proceedingJoinPoint
* @throws Throwable
*/
@Around("myPointCut()")
public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//开始
System.out.println("环绕通知:执行目标方法之前,模拟开启事物...");
//执行当前目标方法
Object obj = proceedingJoinPoint.proceed();
//结束
System.out.println("环绕通知:执行目标方法之后,模拟开启事物...");
return obj;
}
//异常通知
@AfterThrowing(value = "myPointCut()",throwing = "e")
public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
System.out.println("异常通知:" + "出错了" + e.getMessage());
}
//最终通知
@After("myPointCut()")
public void myAfter(){
System.out.println("最终通知:模拟方法结束后的释放资源...");
}
}
- 配置文件:
<?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:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 指定搜索路径 -->
<!-- 使用context命名空间,扫描指定包下的Bean类,进行注解解析 -->
<context:component-scan base-package="com.no5.aspectj.annotation"/>
<!-- 启动基于注解的声明式AspectJ 支持 -->
<aop:aspectj-autoproxy />
</beans>
通知执行的顺序:
上一篇: Linux 守护进程原理及实例(Redis、Nginx)
下一篇: 引导过程和服务控制
推荐阅读
-
基于spring Boot的微信开发————AccessToken的缓存方案(一)
-
【基于EF Core的Code First模式的DotNetCore快速开发框架】完成对DB First代码生成的支持
-
基于jQuery的弹出框插件开发教程
-
基于asp.net的登录页面开发
-
Git 项目推荐 | 基于 laravel4.2 开发的多用户博客系统
-
使用Spring Boot和AspectJ实现方法跟踪基础结构
-
基于阿里云物联网的APP简单开发
-
基于PHP技术开发客服工单系统_php实例
-
DockerPHP: 基于Docker容器化PHP开发环境解决方案
-
《精通struts:基于mvc的Java web设计与开发》