文章目录
1 AOP思想及实现原理
1.1 AOP思想
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过
预编译方式
和运行期动态代理
实现程序功能的统一维护
的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型
。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
1.2 实现原理
…
2 Spring中AOP的术语
- Joinpoint(连接点):
所谓连接点是指那些被拦截到的点。在spring中,指的是方法,因为spring只支持方法类型的连接点。 - Pointcut(切入点):
所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。 - Advice(通知/增强):
所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。通知的类型:前置通知
,后置通知
,异常通知
,最终通知
,环绕通知
。 - Introduction(引介):
引介是一种特殊的通知在不修改类代码的前提下, 可以在运行期为类动态地添加一些方法或Field。 - Target(目标对象):
代理的目标对象。 - Weaving(织入):
是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。 - Proxy(代理):
一个类被AOP织入增强后,就产生一个结果代理类。 - Aspect(切面):
是切入点和通知(引介)的结合。
3 AOP联盟(规范 org.aopalliance.aop.Advice)通知类型
AOP联盟为通知定义了几种类型,简单来说我们拦截的方法,我们有时需要再方法执行前做处理,也可能在执行后做一些处理,也可能在方法出现异常的时候做处理。
AOP联盟为通知Advice定义了org.aopalliance.aop.Advice
Spring按照通知Advice在目标类方法的连接点位置,可以分为5类:
-
前置通知 org.springframework.aop.MethodBeforeAdvice
- 在目标方法执行前实施增强
-
后置通知 org.springframework.aop.AfterReturningAdvice
- 在目标方法执行后实施增强
-
环绕通知 org.aopalliance.intercept.MethodInterceptor
- 在目标方法执行前后实施增强
-
异常抛出通知 org.springframework.aop.ThrowsAdvice
- 在方法抛出异常后实施增强
-
引介通知 org.springframework.aop.IntroductionInterceptor
- 在目标类中添加一些新的方法和属性
3 Spring注解驱动AOP开发入门
需求:实现在执行service方法时输出执行日志。(除了业务层外,表现层和持久层也可以实现)
3.1 java动态代理实现
- 日志切面类
package study.wyy.spring.anno.aop.java;
import lombok.extern.slf4j.Slf4j;
/**
* @author: wyaoyao
* @date: 2020-12-24 16:01
* @description:
*/
@Slf4j
public class LogAspect {
/***
* 前置通知: 在方法执行之前进行日志打印
*
* */
public void beforeLog() {
log.info("begin execute method");
}
/***
* 后置通知: 在目标方法执行后实施增强,比如在方法执行结束后,打印日志
* */
public void afterReturningLog() {
log.info("execute success ...");
}
/***
* 异常通知
* 发生异常的时候执行
* */
public void afterThrowingLog() {
log.error("execute method error");
}
}
- service 方法
package study.wyy.spring.anno.aop.service;
import study.wyy.spring.anno.aop.model.User;
public interface UserService {
public User save(User user);
public Boolean update(User user);
}
@Service
@Slf4j
public class UserServiceImpl implements UserService {
@Override
public User save(User user) {
String uuid = UUID.randomUUID().toString();
user.setId(uuid);
log.info("模拟保存用户。。。。。");
return user;
}
@Override
public Boolean update(User user) {
String id = user.getId();
if(null == id || id.length() ==0){
throw new RuntimeException("user.id.not.null");
}
log.info("模拟更新用户。。。。。");
return Boolean.TRUE;
}
}
- 生成代理类(使用jdk的动态代理)
public class MyProxyBeanFactory {
public static UserService createUserService() {
// 1 目标而对象
UserService userService = new UserServiceImpl();
// 2 切面类
LogAspect logAspect = new LogAspect();
// 3 创建代理对象
/*****
* 参数一:类加载器
* 参数二:目标对象和代理对象实现的接口的字节码对象的数组,接口的方法会被拦截,这里要拦截的就是UserService中的方法,
*
* 参数三:InvocationHandler h 处理器,在此内部实现方法的增强
*/
Object o = Proxy.newProxyInstance(MyProxyBeanFactory.class.getClassLoader(), new Class[]{UserService.class}, new InvocationHandler() {
/****
*
* @param proxy
* @param method: 要拦截的方法,要执行增强的方法
* @param args:拦截方法的参数
* @return 拦截方法的返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 执行方法之前进行日志打印
logAspect.beforeLog();
try {
// 执行方法
Object invoke = method.invoke(userService, args);
// 执行后再打印日志
logAspect.afterReturningLog();
return invoke;
} catch (Exception e) {
// 发生异常,打印异常日志
logAspect.afterThrowingLog();
// 把人家的异常接着在跑出去
throw e;
}
}
});
return (UserService) o;
}
}
- 测试
public class ClientTest {
public static void main(String[] args) {
UserService userService = MyProxyBeanFactory.createUserService();
User user = new User();
user.setMobile("13100001111");
user.setBirthday(new Date());
user.setNickname("kobe");
userService.save(user);
// 发生异常
user.setId(null);
userService.update(user);
}
}
20:47:45.795 [main] INFO study.wyy.spring.anno.aop.java.LogAspect - begin execute method
20:47:45.810 [main] INFO study.wyy.spring.anno.aop.service.impl.UserServiceImpl - 模拟保存用户。。。。。
20:47:45.811 [main] INFO study.wyy.spring.anno.aop.java.LogAspect - execute success ...
20:47:45.811 [main] INFO study.wyy.spring.anno.aop.java.LogAspect - begin execute method
20:47:45.811 [main] ERROR study.wyy.spring.anno.aop.java.LogAspect - execute method error
3.2 springAOP实现
spring 创建代理对象,从spring容器中手动的获取代理对象.
- 定义切面类
package study.wyy.spring.anno.aop.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* @author: wyaoyao
* @date: 2020-12-24 16:01
* @description:
*/
@Slf4j
@Aspect
@Component
public class LogSpringAspect {
/***
* 前置通知: 在方法执行之前进行日志打印
*
* */
@Before("execution(* study.wyy.spring.anno.aop.service.impl.*.*(..))")
public void beforeLog() {
log.info("execute method with params is {}");
}
/***
* 后置通知: 在目标方法执行后实施增强,比如在方法执行结束后,打印日志
* */
@AfterReturning("execution(* study.wyy.spring.anno.aop.service.impl.*.*(..))")
public void afterReturningLog() {
log.info("execute success ...");
}
/***
* 异常通知
* 发生异常的时候执行
* */
@AfterThrowing("execution(* study.wyy.spring.anno.aop.service.impl.*.*(..))")
public void afterThrowingLog() {
log.info("execute method error {} with params {}");
}
}
- 配置
package study.wyy.spring.anno.aop.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* @author wyaoyao
* @description
* @date 2021/2/1 16:25
*/
@Configuration
@ComponentScan("study.wyy.spring.anno.aop")
@EnableAspectJAutoProxy // 开启AspectJ
public class SpringConfiguration {
}
- 启动测试
package study.wyy.spring.anno.aop;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import study.wyy.spring.anno.aop.config.SpringConfiguration;
import study.wyy.spring.anno.aop.model.User;
import study.wyy.spring.anno.aop.service.UserService;
/**
* @author wyaoyao
* @description
* @date 2021/2/1 16:27
*/
public class SpringAOPTest {
public static void main(String[] args) {
// 1 获取容器
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
//2.获取bean对象
UserService userService = ac.getBean(UserService.class);
// 3.准备数据
User user = new User();
user.setId("1");
user.setUsername("test");
user.setNickname("泰斯特");
// 4.执行方法
userService.save(user);
// 5测试出现异常
user.setId(null);
userService.update(user);
}
}
20:57:01.457 [main] INFO study.wyy.spring.anno.aop.aop.LogSpringAspect - execute method with params is {}
20:57:01.469 [main] INFO study.wyy.spring.anno.aop.service.impl.UserServiceImpl - 模拟保存用户。。。。。
20:57:01.469 [main] INFO study.wyy.spring.anno.aop.aop.LogSpringAspect - execute success ...
20:57:01.469 [main] INFO study.wyy.spring.anno.aop.aop.LogSpringAspect - execute method with params is {}
20:57:01.470 [main] INFO study.wyy.spring.anno.aop.aop.LogSpringAspect - execute method error {} with params {}
Exception in thread "main" java.lang.RuntimeException: user.id.not.null