[Spring+SpringMVC+Mybatis]框架学习笔记(六):Spring_AspectJ实现AOP

第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包

    ZOBT0J.png

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

  • 引入aop的约束

    ZOB779.png

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>
posted @ 2019-07-08 15:39  Steven0325  阅读(188)  评论(0编辑  收藏  举报