Spring AOP(面向切面编程) 是什么?
????????面向切面编程(AOP) 和面向对象编程(OOP)类似,也是一种编程模式/编程思想。
????????有效减少了系统见的重复代码,达到了模块间的松耦合目的。
????????AOP的全程是"Aspect Oriented Programming",即面向切面编程,它将业务逻辑的各个部分进行隔离,使开发人员再编写业务逻辑时可以专心于核心业务,从而提高了开发效率。
????????程序需要---1.业务需求----需要实现的程序的具体核心功能---添加用户
? ? ? ? ? ? ? ? ? ? ? ? ? 2.系统需求---实现的程序的辅助功能---------记录系统运行日志
????????AOP采取横向抽取机制,取代了传统的纵向继承体系的重复性代码,其应用主要体现在事务处理,日志管理,权限控制,异常处理等方面
????????目前最流行的AOP框架有两个,分别为Spring AOP和AspectJ。
????????Spring AOP 使用纯Java实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式【CGLlB动态代理】向目标类植入增强的代码。
????????AspectJ 是一个基于 Java 语言的 AOP 框架,从 Spring 2.0 开始,Spring AOP 引入了对 AspectJ 的支持。AspectJ 扩展了 Java 语言,提供了一个专门的编译器,在编译时提供横向代码的植入。
????????为了更好地理解 AOP,就需要对 AOP 的相关术语有一些了解,这些专业术语主要包含 Joinpoint、Pointcut、Advice、Target、Weaving、Proxy 和 Aspect,它们的含义如下表所示。
Joinpoint(连接点) | 指那些被拦截到的点,在 Spring 中,可以被动态代理拦截目标类的方法。 | Pointcut(切入点) | 指要对哪些 Joinpoint 进行拦截,即被拦截的连接点。 | Advice(通知) | 指拦截到 Joinpoint 之后要做的事情,即对切入点增强的内容。 | Target(目标) | 指代理的目标对象。 | Weaving(植入) | 指把增强代码应用到目标上,生成代理对象的过程。 | Proxy(代理) | 指生成的代理对象。 | Aspect(切面) | 切入点和通知的结合。? |
Advice(通知) | 指拦截到 Joinpoint 之后要做的事情,即对切入点增强的内容。 |
org.springframework.aop.MethodBeforeAdvice(前置通知)?? ? 在方法之前自动执行的通知称为前置通知,可以应用于权限管理等功能。
org.springframework.aop.AfterReturningAdvice(后置通知) 在方法之后自动执行的通知称为后置通知,可以应用于关闭流、上传文件、删除临时文件等功能。
org.aopalliance.intercept.MethodInterceptor(环绕通知) 在方法前后自动执行的通知称为环绕通知,可以应用于日志、事务管理等功能。
org.springframework.aop.ThrowsAdvice(异常通知) 在方法抛出异常时自动执行的通知称为异常通知,可以应用于处理异常记录日志等功能。
Spring使用AspectJ开发AOP:基于XML和基于Annotation
基于XML【Spring核心配置文件】
package com.spring.demo1.impl;
import com.spring.demo1.StudentService;
public class StudentServiceImpl implements StudentService {
//Joinpoint(连接点)
@Override
public void insertStudent() {
System.out.println("实现添加学生信息的业务方法--insert");
}
//Joinpoint(连接点)
@Override
public void updateStudent() {
int a=10/0;
System.out.println("实现修改学生信息的业务方法--update");
}
//Joinpoint(连接点)
@Override
public void deleteStudent() {
System.out.println("实现删除学生信息的业务方法--delete");
}
//Joinpoint(连接点)
@Override
public void selectStudent() {
System.out.println("实现查询学生信息的业务方法--select");
}
}
package com.spring.demo1.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
//系统需求功能实现类
public class MyAspect {
//系统需求功能实现方法
public void saveLog(){
System.out.println("记录用户的操作日志");
}
//实现环绕通知的具体方法
public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
saveLog();//开始
Object obj = proceedingJoinPoint.proceed(); // 执行当前目标方法
saveLog();//结束
return obj;
}
//异常通知实现方法
public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
System.out.println("异常通知"+"出错了"+e.getMessage());
}
}
<?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
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 创建目标类对象 -->
<bean id="studentservice" class="com.spring.demo1.impl.StudentServiceImpl"></bean>
<!-- 创建系统需求功能实现类 -->
<bean id="myaspect" class="com.spring.demo1.aspect.MyAspect"></bean>
<!-- aop配置 -->
<!-- proxy-target-class="true" 设置aop使用CGLIB代理模式 -->
<aop:config proxy-target-class="true">
<!-- 引入系统需求功能实现类 -->
<aop:aspect ref="myaspect">
<!-- 配置切入点 -->
<!--id:切入点名称-->
<!-- expression:切入点表达式 -->
<aop:pointcut id="point1" expression="execution(* com.spring.demo1.impl.StudentServiceImpl.insertStudent(..))"/>
<!-- 配置前置通知 -->
<aop:before method="saveLog" pointcut-ref="point1"></aop:before>
</aop:aspect>
<aop:aspect ref="myaspect">
<!-- 配置切入点 -->
<aop:pointcut id="point2" expression="execution(* com.spring.demo1.impl.StudentServiceImpl.deleteStudent(..))"/>
<aop:after method="saveLog" pointcut-ref="point2"></aop:after>
</aop:aspect>
<aop:aspect ref="myaspect">
<!-- 配置切入点 -->
<aop:pointcut id="point3" expression="execution(* com.spring.demo1.impl.StudentServiceImpl.selectStudent())"/>
<aop:around method="myAround" pointcut-ref="point3"></aop:around>
</aop:aspect>
<aop:aspect ref="myaspect">
<!-- 配置切入点 -->
<aop:pointcut id="point4" expression="execution(* com.spring.demo1.impl.StudentServiceImpl.updateStudent(..))"/>
<aop:after-throwing method="myAfterThrowing" pointcut-ref="point4" throwing="e"></aop:after-throwing>
</aop:aspect>
</aop:config>
</beans>
基于Annotation
package com.spring.demo1;
import org.springframework.stereotype.Component;
@Component("student")
public class StudentServiceImpl {
public void insertStudent(){
System.out.println("添加学生信息的业务方法---insertStudent");
}
public void uodateStudent(){
System.out.println("修改学生信息的业务方法---updateStudent");
}
public void deleteStudent(){
System.out.println("删除学生信息的业务方法---deleteStudent");
}
public void selectStudent(){
System.out.println("查询学生信息的业务方法---selectStudent");
int a=10/0;
}
}
package com.spring.demo1;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component("myAspect")
@Aspect
public class MyAspect {
@Pointcut("execution(* com.spring.demo1.StudentServiceImpl.insertStudent())")
public void point1(){}
@Pointcut("execution(* com.spring.demo1.StudentServiceImpl.uodateStudent())")
public void point2(){}
@Pointcut("execution(* com.spring.demo1.StudentServiceImpl.deleteStudent())")
public void point3(){}
@Pointcut("execution(* com.spring.demo1.StudentServiceImpl.selectStudent())")
public void point4(){}
public void saveLog(){
System.out.println("记录系统运行日志");
}
//测试前置通知
@Before("point1()")
public void myBefore(){
saveLog();
}
//测试后置通知
@After("point2()")
public void myafter(){
saveLog();
}
//测试环绕通知
@Around("point3()")
public Object myAround(ProceedingJoinPoint proceedingJoinPoin) throws Throwable{
saveLog();
Object obj=proceedingJoinPoin.proceed();
saveLog();
return obj;
}
//测试异常通知
@AfterThrowing(value = "point4()",throwing = "e")
public void myAfterThrowing(Throwable e){
System.out.println("异常通知"+"出错了"+e.getMessage());
}
}
<?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">
<!-- 扫面包含com.spring.demo1包下的所有注解 -->
<context:component-scan base-package="com.spring.demo1"></context:component-scan>
<!-- 使切面开始自动代理 -->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>
package com.spring.demo1;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestMain {
public static void main(String[] args) {
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
StudentServiceImpl studentService=ac.getBean("student",StudentServiceImpl.class);
//studentService.insertStudent();
//测试后置通知
//studentService.uodateStudent();
//测试环绕通知
//studentService.deleteStudent();
//测试异常通知
studentService.selectStudent();
}
}
切入点表达式
? ? ? ? execution(modifiers-pattern?? ?? ?????????????????????????ret-type-pattern? ?????????????????????????? ?declaring-type-pattern?? ?????????????????????????? ?name-pattern(param-pattern) ?? ? ? ? ? ? ? ? ? ????????? ?? ?throws-pattern?)
这里的问号标识当前项可以有也可以没有,其中各项的语义如下:
? ? ? ? ? ? ? ???modifiers-pattern:方法的可见性,如public ,protected;
?????????????????ret-type-pattern : 方法的返回值类型,如int ,void等;
????????????? ? ?declaring-type-pattern:方法所在类的全路径名,如com.spring.demo1;
? ? ? ? ? ? ? ???name-pattern:方法名类型,如StudentService();
? ? ? ? ? ? ? ???param-pattern:方法的参数类型,如java.lang.String;
? ? ? ? ? ? ?? ? throws-pattern:方法抛出的异常类型,如java.lang.Exception;
如下是一个使用execution表达式的例子:
? ? ? ?execution(public * com.spring.service.BusinessObject.businessService(java.lang.String,..))
? ? ? ? 上述切点表达式将会匹配使用public修饰,返回值为任意类型,并且是com.spring.service.BusinessObject类中名称为businessService的方法,方法可以有多个参数,但是第一个参数必须是java.lang.String类型的方法。
? ? ? ? *通配符,该通配符主要用于匹配单个单词,或者是以某个此为前缀[insert*]或后缀[*Student]的单词
? ? ? ? execution(* com.spring.service.BusinessObject.*())
? ? ? ? 表示返回值为任意类型,在com.spring.service.BusinessObject类中,并且参数个数为零的方法
? ? ? ? execution(* com.spring.service.Business*.*())
? ? ? ? 表示返回值为任意类型,在com.spring.service包中,以Business为前缀的类,并且是类中参数个数为零的方法
? ? ? ? ..(两个点)通配符,该通配符表示0个或多个项,主要用于declaring-type-pattern和param-pattern中。
? ? ? ? 如果用于declaring-type-pattern中,则表示匹配当前包及其子包,如果用于param-pattern中,则表示匹配0个或多个参数。
? ? ? ?execution(* com.spring.service..*.businessService())
? ? ? ? 表示匹配返回值为任意类型,并且是com.spring.service包及其子包下的任意类的名称为businessService的方法,而且该方法不能有任何参数
????????execution(* com.spring.service.BusinessObject.businessService(java.lang.String,..))
????????表示匹配返回值为任意类型,并且是com.spring.service.BusinessObject类中的,名称为businessService的方法,而且该方法有第一个参数,并且第一个参数是java.lang.String类型。
|