AOP(Aspect Oriented Programming)是通过动态代理模式,带来掌控各个对象操作到切面环境,管理包括日志、数据库事务等操作,让我们拥有可以在反射原有对象方法之前正常返回、异常返回事后插入自己的逻辑代码的能力,有时候甚至取代原始方法。在一些常用的流程中,比如数据库事务,AOP会提供默认的实现逻辑,也会提供一些简单的配置,我们就能比较方便地修改默认的实现,达到符合真实应用的效果,这样就可以大大降低开发的工作量,提高代码的可读性和可维护性,将开发集中在业务逻辑上。
正常执行sql流程:
1、打开通过数据库连接池获得数据库连接资源,并做一定的设置工作。
2、执行对应的sql语句,对数据进行操作。
3、如果sql语句执行过程中发生异常,回滚事务。
4、如果没有发生异常,提交事务。
5、关闭资源
AOP封装后的执行流程:
1、打开获取数据连接在before方法中完成。
2、执行SQL,采用反射的机制调用。当方法标注为@Transactional时,该方法启用数据库事务功能。
3、如果发生异常,回滚事务;如果没有发生异常,提交事务。整个事务管理AOP就完成了整个流程。
4、关闭数据库连接资源
面向切面编程的术语
AOP通过动态代理模式,掌控各个对象操作的切面环境,管理包括日志、数据库事务等操作,让我们拥有可以在反射原有对象方法之前正常返回、异常返回事后插入自己的逻辑代码的能力,有时候甚至取代原始方法。
切面(Aspect)
切面就是在一个怎么样的环境中工作。在切面中可以定义各类的通知、切点和引入等内容,然后Spring AOP会将其定义等内容织入到约定到流程中,在动态代理中可以把它理解成一个拦截器。
通知(Advice)
- 前置通知(before):在动态代理反射原有对象方法或者执行环绕通知前执行的通知功能。
- 后置通知(after):在动态代理反射原有对象方法或者执行环绕通知后执行的通知功能。无论是否抛出异常,它都会被执行。
- 返回通知(afterReturning):在动态代理反射原有对象方法或者执行环绕通知后正常返回(无异常)执行的通知功能。
- 异常通知(afterThrowing):在动态代理反射原有对象方法或者执行环绕通知产生异常后正常返回(无异常)执行的通知功能。
- 环绕通知(around):在动态代理中,它可以取代当前被拦截对象的方法,提供回调原有被拦截对象的方法。
引入(Introduction)
引入允许我们在现有到类里添加自定义的类和方法。
切点(Pointcut)
告诉Spring AOP在什么时候启动拦截并织入对应的流程中,并不是所有的开发都需要启动AOP
连接点(join point)
具体拦截的东西(方法...)
织入(Weaving)
织入是一个生成代理对象并将切面内容放入流程中的过程。Spring是以JDK和CGLIB动态代理来生成对象的。
Spring中使用AOP
使用@AspectJ注解开发Spring AOP
以某个类的某个方法作为连接点
打印角色接口
public interface RoleService {
public void printRole(Role role);
}
实现类
@Component
public class RoleServiceImpl implements RoleService {
@Override
public void printRole(Role role) {
System.out.println("id:"+role.getId()+",name:"+role.getName()+",node:"+role.getNote());
}
}
创建切面
对于动态代理的概念,它就如同一个拦截器,使用@Aspect注解一个类,SpringIoc容器就会认为这是一个切面了。
package com.wlz;
import org.aspectj.lang.annotation.*;
/**
* @author zhangzhiqiang
* @Description 创建切面
* @createTime 2019年03月06日 16:29:00
*/
@Aspect
public class RoleAspect {
/**
* 定义切点
* execution:代表执行方法的时候会触发
* *:代表任意返回类型的方法
* com.wlz.RoleServiceImpl:代表类的全限定名
* printRole:被拦截方法名称
* (..):任意的参数
*
*/
// @Before("execution(* com.wlz.RoleServiceImpl.printRole(..))")
// public void before(){
// System.out.println("before..");
// }
//
// @After("execution(* com.wlz.RoleServiceImpl.printRole(..))")
// public void after(){
// System.out.println("after..");
// }
//
// @AfterReturning("execution(* com.wlz.RoleServiceImpl.printRole(..))")
// public void afterReturning(){
// System.out.println("afterReturning..");
// }
//
// @AfterThrowing("execution(* com.wlz.RoleServiceImpl.printRole(..))")
// public void afterThrowing(){
// System.out.println("afterThrowing..");
// }
/**
* 定义切点
* 引入另一个注解@Pointcut
* 重复使用一个简易表达式去取代需要多次书写的复杂表达式
*/
@Pointcut("execution(* com.wlz.RoleServiceImpl.printRole(..))")
public void print(){
}
@Before("print()")
public void before(){
System.out.println("before..");
}
@After("print()")
public void after(){
System.out.println("after..");
}
@AfterReturning("print()")
public void afterReturning(){
System.out.println("afterReturning..");
}
@AfterThrowing("print()")
public void afterThrowing(){
System.out.println("afterThrowing..");
}
}
测试AOP
对Spring的Bean进行配置,这里用注解Java配置
@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.wlz")
public class AopConfig {
@Bean
public RoleAspect getRoleAspect(){
return new RoleAspect();
}
}
public static void main(String[] args) {
ApplicationContext applicationContext=new AnnotationConfigApplicationContext(AopConfig.class);
RoleService roleService=applicationContext.getBean(RoleService.class);
Role role1=new Role();
role1.setId("id");
role1.setName("name");
role1.setNote("note");
roleService.printRole(role1);
System.out.println("-----------------");
Role role2=null;
roleService.printRole(role2);
}
运行结果:
before..
id:id,name:name,node:note
after..
afterReturning..
-----------------
before..
after..
afterThrowing..
Exception in thread "main" java.lang.NullPointerException
at com.wlz.RoleServiceImpl.printRole(RoleServiceImpl.java:15)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:56)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:47)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:55)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:62)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy23.printRole(Unknown Source)
at com.wlz.TestAop.main(TestAop.java:26)
Spring可以把一些方法(通知)织入约定的流程当中,通过注解和XML配置的方式实现AOP的切点、切面、连接点、通知等功能。通过AOP可以把公用的代码抽取出来,进而减少开发者的工作量。