第6章 Spring_AspectJ实现AOP

6.1 什么是AspectJ

对于AOP的这种编程思想,有很多框架或者组件进行了实现,spring实现AOP就是其中的一种。

AspectJ也实现了AOP,而且实现方式更为简单,使用起来更为方便,所以spring将AspectJ对于AOP的实现引入了自己的框架。

AspectJ是一个面向切面的框架,它定义了AOP的一些语法,有一个专门的字节码生成器来生成遵守java规范的class文件。

AspectJ的通知类型:

  • 前置通知
  • 后置通知
  • 环绕通知
  • 异常通知
  • 最终通知:无论程序是否正常执行,最终通知的代码会得到执行,类似于try...catch...finally

6.2 切入点表达式

切入点表达式作用:标识切面织入到哪些类的哪些方法当中。

  • 语法格式如下:
public boolean com.steven.spring.service.impl.StudentService.addStudent(Student student) execution(
modifiers-pattern? //访问权限匹配 如public、protected 问号代表可以省略
ret-type-pattern //返回值类型匹配
declaring-type-pattern? //全限定性类名
name-pattern(param-pattern) //方法名(参数名)
throws-pattern? //抛出异常类型
)

注意:中间以空格隔开,有问号的属性可以省略

  • 特殊符号
a: * 代表0到多个任意字符
b: .. 放在方法参数中 ,代表任意个参数 ,放在包名后面表示当前包及其所有子包路径
c: + 放在类名后,表示当前类及其子类,放在接口后,表示当前接口及其实现类
  • 例如:
a:execution(public * *(..))   表示任意的public方法

b:execution(* set*(..))	      表示任意包含以set字符开头的方法

c:execution(* com.steven.spring.service.impl.*.*(..)) 表示com.steven.spring.service.impl的任意类的任意方法

d:execution(* com.steven.spring.service..*.*(..)) 表示com.steven.spring.service包下面的所有方法以及所有子包下面的所有方法

e:execution(* com.steven.spring.service.IStudentService+.*(..))		

f:execution(* add(String,int))  带包任意返回类型的add方法 有两个参数,类型分别为String,int

g:execution(* add(String,*))



6.3 AspectJ+Spring的环境搭建

  • 引入jar包

    经测试,可以不用另外加上spring-aspects-4.2.1.RELEASE.jar。但aspectjweaver一定不能少。

  • 引入aop的约束

6.4 Aspect的配置方式

6.4.1 基于xml的方式

实现步骤:

  • 编写切面类
  • 在切面类里面定义各种通知的实现方法
  • 在配置文件里面对aop进行配置 ,详见applicationContext.xml

实例:

1)实体类

package com.steven.spring.sysmanage.entity;

/**
* 学生实体类
* @author Administrator
*
*/
public class Student implements java.io.Serializable{ private static final long serialVersionUID = -3875695558042397898L; private Integer id;
private String name;
private Integer age; public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
} }

2)服务类接口

package com.steven.spring.sysmanage.service;

import java.util.List;

import com.steven.spring.sysmanage.entity.Student;

/**
* 用于对外提供学生服务类的增删改查接口
* @author Administrator
*
*/
public interface IStudentService { public boolean addStudent(Student student); public boolean delStudent(Integer studentId); public boolean updateStudent(Student student); public List<Student> getStudentList(); }

3)服务类

package com.steven.spring.sysmanage.service.impl;

import java.util.ArrayList;
import java.util.List; import com.steven.spring.sysmanage.entity.Student;
import com.steven.spring.sysmanage.service.IStudentService; /**
* 用于对外提供学生服务类的增删改查实现
* @author Administrator
*
*/
public class StudentService implements IStudentService{ @Override
public boolean addStudent(Student student) {
//System.out.println("进行权限验证"); System.out.println("执行增加功能"); //System.out.println("进行日志记录");
return true;
} @Override
public boolean delStudent(Integer studentId) {
//System.out.println("进行权限验证");
int i= 1/0;
System.out.println("执行删除功能");
//System.out.println("进行日志记录"); return true; } @Override
public boolean updateStudent(Student student) {
//System.out.println("进行权限验证");
System.out.println("执行修改功能");
//System.out.println("进行日志记录"); return true; } @Override
public List<Student> getStudentList() {
//System.out.println("进行权限验证");
System.out.println("执行查询功能");
//System.out.println("进行日志记录");
return new ArrayList<Student>(); } }

4)定义通知

package com.steven.spring.sysmanage.aspect;

import org.aspectj.lang.ProceedingJoinPoint;

/**
* 基于xml配置方式实现aspectJ 定义通知
* @author chenyang
*
*/
public class MyAspect {
//定义前置通知
public void beforeAdvice(){
System.out.println("这是一个前置通知,应该在目标方法之前打印出来");
} //定义后置通知
public void afterAdvice(){
System.out.println("这是一个后置通知,应该在目标方法之后打印出来");
} //定义后置通知,包含返回值
public void afterAdvice(Object result){
System.out.println("这是一个后置通知,应该在目标方法之后打印出来;目标方法的返回值为:" + result.toString());
} //定义环绕通知
public void aroundAdvice(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("这是一个环绕通知,应该在目标方法之前打印出来");
Object result = pjp.proceed();
System.out.println("这是一个环绕通知,应该在目标方法之后打印出来");
} //定义异常通知
public void afterThrowingAdvice(){
System.out.println("这是一个异常通知,应该在目标方法出现异常之后打印出来");
} //定义异常通知,包含返回值
public void afterThrowingAdvice(Exception ex){
System.out.println("这是一个异常通知,应该在目标方法出现异常之后打印出来,异常为:" + ex);
} //定义最终通知
public void lastAdvice(){
System.out.println("这是一个最终通知,无论如何都会执行");
} }

5)配置文件

<?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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 注册目标类 -->
<bean id="studentService" class="com.steven.spring.sysmanage.service.impl.StudentService"></bean> <!-- 注册切面(通知) -->
<bean id="myAspect" class="com.steven.spring.sysmanage.aspect.MyAspect"></bean> <!-- AspectJ的aop配置 -->
<aop:config>
<aop:pointcut expression="execution(* add*(..))" id="beforePointCut"/>
<aop:pointcut expression="execution(* update*(..))" id="afterPointCut"/>
<aop:pointcut expression="execution(* get*(..))" id="aroundPointCut"/>
<aop:pointcut expression="execution(* del*(..))" id="afterThrowingPointCut"/>
<aop:pointcut expression="execution(* del*(..))" id="lastPointCut"/> <aop:aspect ref="myAspect">
<aop:before method="beforeAdvice" pointcut-ref="beforePointCut"/> <aop:after-returning method="afterAdvice" pointcut-ref="afterPointCut" />
<!-- 方法参数必须是全路径类名 返回值参数名必须与方法中参数名一致 -->
<aop:after-returning method="afterAdvice(java.lang.Object)"
pointcut-ref="afterPointCut" returning="result" /> <aop:around method="aroundAdvice" pointcut-ref="aroundPointCut"/> <aop:after-throwing method="afterThrowingAdvice" pointcut-ref="afterThrowingPointCut"/>
<aop:after-throwing method="afterThrowingAdvice(java.lang.Exception)"
pointcut-ref="afterThrowingPointCut" throwing="ex" /> <aop:after method="lastAdvice" pointcut-ref="lastPointCut"/>
</aop:aspect>
</aop:config> </beans>

6)测试

package com.steven.spring.sysmanage.test;

import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; import com.steven.spring.sysmanage.entity.Student;
import com.steven.spring.sysmanage.service.IStudentService; public class AspectXmlTest {
private ApplicationContext ac = null;
private IStudentService studentService = null; @Before
public void init(){
ac = new ClassPathXmlApplicationContext("applicationContext.xml");
studentService = (IStudentService) ac.getBean("studentService");
} //测试前置通知
@Test
public void testBeforeAdvice(){
studentService.addStudent(new Student());
}
//测试后置通知
@Test
public void testAfterAdvice(){
studentService.updateStudent(new Student());
}
//测试环绕通知
@Test
public void testAroundAdvice(){
studentService.getStudentList();
}
//测试异常通知
@Test
public void testAfterThrowingAdvice(){
studentService.delStudent(1);
}
//测试最终通知
@Test
public void testLastAdvice(){
studentService.delStudent(1);
}
}

6.4.2 基于注解的方式

注解方式有一定的侵入性(在代码中加入原本不属于代码的部分)。

实现步骤:

  • 编写切面类,加上@Aspect注解
  • 实现各种通知,在实现通知的方法上加上通知的注解以及切入点表达式的注解
  • 在配置文件注册切面,且加上aspectJ的自动代理

实例:

在6.4.1实例的基础上更改通知类和配置文件,最后在测试类中更改配置文件名称即可。

1)通知类

package com.steven.spring.sysmanage.aspect;

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; /**
* 基于xml配置方式来实现AspectJ 定义通知
* @author Administrator
*
*/
@Aspect
public class MyAspectAnnotation {
//定义前置通知
@Before(value="execution(* add*(..))")
public void beforeAdvice(){
System.out.println("这是一个前置通知,应该在目标方法之前打印出来");
} //定义后置通知
@AfterReturning(value="execution(* update*(..))")
public void afterAdvice(){
System.out.println("这是一个后置通知,应该在目标方法之后打印出来");
} //定义后置通知包含返回值
@AfterReturning(value="execution(* update*(..))",returning="result")
public void afterAdvice(Object result){
System.out.println("这是一个后置通知,应该在目标方法之后打印出来");
System.out.println("后置通知得到目标方法的返回值="+result.toString());
} //定义环绕通知
@Around(value="execution(* get*(..))")
public void aroundAdvice(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("这是一个环绕通知,应该在目标方法之前打印出来");
Object result = pjp.proceed();
System.out.println("这是一个环绕通知,应该在目标方法之后打印出来");
} //定义异常通知
@AfterThrowing(value="execution(* del*(..))")
public void afterThrowingAdvice(){
System.out.println("这是一个异常通知,应该在目标方法出现异常时候打印出来");
} //定义异常通知
@AfterThrowing(value="execution(* del*(..))",throwing="ex")
public void afterThrowingAdvice(Exception ex){
System.out.println("这是一个异常通知,应该在目标方法出现异常时候打印出来 ex="+ex);
} //定义最终通知 finally
@After(value="execution(* del*(..))")
public void lastAdvice(){
System.out.println("这是一个最终通知,无论如何会执行 ");
} }

2)配置文件

<?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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--注册studentService -->
<bean id = "studentService" class = "com.steven.spring.sysmanage.service.impl.StudentService">
</bean> <!--注册切面 -->
<bean id= "myAspect" class = "stevenm.steven.spring.sysmanage.aspect.MyAspectAnnotation"></bean> <aop:aspectj-autoproxy/>
</beans>

[Spring+SpringMVC+Mybatis]框架学习笔记(六):Spring_AspectJ实现AOP的更多相关文章

  1. [Spring+SpringMVC+Mybatis]框架学习笔记(六):事务

    第7讲 事务 7.1 事务的概念 事务是一系列作为一个逻辑单元来执行的操作集合. 它是数据库维护数据一致性的单位,它讲数据库从一个一致状态,转变为新的另外一个一致状态.说的简单一点就是:如果一组处理步 ...

  2. Spring+SpringMVC+MyBatis集成学习笔记【一】

    一,首先要清楚,SpringMVC其实就是Spring的一个组件       例如我们知道Spring中有类似于,AOP TX等等类似的组件,所以SpringMVC其实就是Spring的一个组件,是S ...

  3. Spring+SpringMVC+MyBatis深入学习及搭建(十六)——SpringMVC注解开发(高级篇)

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7085268.html 前面讲到:Spring+SpringMVC+MyBatis深入学习及搭建(十五)——S ...

  4. Spring+SpringMVC+MyBatis深入学习及搭建(六)——MyBatis关联查询

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6923464.html 前面有将到:Spring+SpringMVC+MyBatis深入学习及搭建(五)--动 ...

  5. Spring+SpringMVC+MyBatis深入学习及搭建(十七)——SpringMVC拦截器

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7098753.html 前面讲到:Spring+SpringMVC+MyBatis深入学习及搭建(十六)--S ...

  6. Spring+SpringMVC+MyBatis深入学习及搭建(二)——MyBatis原始Dao开发和mapper代理开发

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6869133.html 前面有写到Spring+SpringMVC+MyBatis深入学习及搭建(一)——My ...

  7. Spring+SpringMVC+MyBatis深入学习及搭建(三)——MyBatis全局配置文件解析

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6874672.html 前面有写到Spring+SpringMVC+MyBatis深入学习及搭建(二)——My ...

  8. Spring+SpringMVC+MyBatis深入学习及搭建(七)——MyBatis延迟加载

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6953005.html 前面讲到:Spring+SpringMVC+MyBatis深入学习及搭建(六)——My ...

  9. Spring+SpringMVC+MyBatis深入学习及搭建(八)——MyBatis查询缓存

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6956206.html 前面讲到:Spring+SpringMVC+MyBatis深入学习及搭建(七)——My ...

  10. Spring+SpringMVC+MyBatis深入学习及搭建(十一)——SpringMVC架构

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6985816.html 前面讲到:Spring+SpringMVC+MyBatis深入学习及搭建(十)--My ...

随机推荐

  1. 我的第一个NPM包:panghu-planebattle-esm(胖虎飞机大战)使用说明

    好家伙,我的包终于开发完啦 欢迎使用胖虎的飞机大战包!! 为你的主页添加色彩 这是一个有趣的网页小游戏包,使用canvas和js开发 使用ES6模块化开发 效果图如下:  (觉得图片太sb的可以自己改 ...

  2. Network Science: 巴拉巴西网络科学阅读笔记

    前言: 最小生成树中Kruskal算法对应了统计物理中的著名模型invasion percolation.由此写了一篇文章:invasion percolation and global optimi ...

  3. Node.js躬行记(27)——接口管理

    在页面发生线上问题时,你要做的事情就是去查接口,响应数据是否正确,查接口的方法有两种: 第一种是在浏览器中打开地址,但是你必须得知道详细的 URL,并且有些页面还需要附带参数. 第二种是打开编辑器,启 ...

  4. springboot注解@PostContruct以及@Cacheable

    1.@PostContruct @PostConstruct和@PreDestroy两个注解被用来修饰一个非静态的void()方法. @PostConstruct 加该注解会在项目启动的时候执行该方法 ...

  5. 是时候,升级你的 Windows 了「GitHub 热点速览」

    不知道多少小伙伴用着 Windows 操作系统,可能会有一个烦恼是有时候操作系统过慢,因为众多拖慢 Windows 系统的组件.Atlas 作为一个修改版的 Windows 系统,能极大提高操作系统运 ...

  6. 音视频八股文(10)-- mp4结构

    介绍 mp4⽂件格式⼜被称为MPEG-4 Part 14,出⾃MPEG-4标准第14部分 .它是⼀种多媒体格式容器,⼴泛⽤于包装视频和⾳频数据流.海报.字幕和元数据等.(顺便⼀提,⽬前流⾏的视频编码格 ...

  7. 2022-05-19:给定一个数组arr,给定一个正数M, 如果arr[i] + arr[j]可以被M整除,并且i < j,那么(i,j)叫做一个M整除对。 返回arr中M整除对的总数量。 来自微软。

    2022-05-19:给定一个数组arr,给定一个正数M, 如果arr[i] + arr[j]可以被M整除,并且i < j,那么(i,j)叫做一个M整除对. 返回arr中M整除对的总数量. 来自 ...

  8. 【故障公告】博客站点一台阿里云负载均衡被DDoS攻击

    13:06 收到阿里云的电话与邮件通知,博客站点的一台阿里云负载均衡因被 DDoS 攻击被关进黑洞(所有访问被屏蔽),部分用户的访问受影响,由此给您带来麻烦,请您谅解. 您的IP:x.x.x.x 实例 ...

  9. 知识拷问:工作站和服务器哪个更适合做CST电磁仿真?

    通常大型企业都会具备工作站和服务器用以作为办公的支持,在大家做仿真分析时,我们一般建议大家更多地使用工作站,工作站要比服务器更适合做CST软件的仿真运算. 什么是服务器? 服务器是指在网络环境下运行相 ...

  10. Linux修改Python软链接

    Linux修改python软链接 0. 适用场景及基础知识 适用场景: 有些自带的是python3命令,嫌输入太麻烦,可以修改成python命令 有些自带是python是python2,想修改成pyt ...