哈喽~大家好呀,上篇讲到 Spring 框架的两大核心思想 AOP 与 IOP的原理,现在来看看具体代码实现。
个人主页:个人主页
系列专栏:【云原生系列】
与这篇相关的文章:
【JAVAEE框架】Spring 项目构建流程
【JAVAEE框架】Spring 项目构建流程_程序猿追的博客-CSDN博客
【JAVAEE框架】Mybatis常用操作(CRUD)
【JAVAEE框架】Mybatis常用操作(CRUD)_程序猿追的博客-CSDN博客
Java测试、反射、注解
Java测试、反射、注解_程序猿追的博客-CSDN博客
目录
一、前言
1、实现需要的知识点
2、定义切入点
二、原生代码实现
三、注解实现
1、注解
2、Spring AOP配置元素
3、注解实现
上篇讲到 AOP 它是基于代理模式下进行的,这篇来讲讲代码是如何实现。
AspectJ 中常用的通知有四种类型:
(1)前置通知
(2)后置通知
(3)环绕通知
(4)最终通知
(5)定义切入点
切入点:简单的说,就是连接点的查询条件
示例
表达式匹配规则举例
public * addNewUser(entity.User): “*”表示匹配所有类型的返回值。
public void *(entity.User): “*”表示匹配所有方法名。
public void addNewUser(..): “..”表示匹配所有参数个数和类型。
* com.service.*.*(..):匹配com.service包下所有类的所有方法。
* com.service..*.*(..):匹配com.service包及其子包下所有类的所有方法
pom导入 aop 需要的依赖
aopalliance
aopalliance
1.0
org.aspectj
aspectjweaver
1.9.5
创建 userService.java
用来做方法测试类
public class UserService {
public Integer addUser(int n) {
Integer r = 0;
r = 100 / n;
System.out.println("addUser 运行了");
System.out.println(r);
return r;
}
public int deleteUser(String id) {
System.out.println("删除:" + id);
return 1;
}
}
编写 UserAspect.java
// 前置通知
// JoinPoint: 连接点,改对象包含目标方法的相关信息
public void before(JoinPoint jp){
System.out.println(jp.getTarget() + "的" + jp.getSignature().getName() + "方法执行了,参数: " + Arrays.toString(jp.getArgs()));
}
// 后置通知
public void after(JoinPoint jp){
System.out.println(jp.getTarget() + "的" + jp.getSignature().getName() + "之后方法执行了,参数: " + Arrays.toString(jp.getArgs()));
}
// 异常通知
public void errorNation(JoinPoint jp, Throwable e){
System.out.println(jp.getTarget() + "的" + jp.getSignature().getName() + "方法出错了,错误是: " + e.getMessage());
}
// 返回通知
public void ReturnNation(JoinPoint jp, Object ret){
System.out.println(jp.getTarget() + "的" + jp.getSignature().getName() + "方法的返回结果是: " + ret);
}
applicationContext.xml 文件
编写测试类
@Test
public void Test01(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("UserService");
userService.addUser(10);
System.out.println(userService);
}
效果
底层原理就是采用了代理模式
前置通知@Before:前置增强处理,在目标方法前织入增强处理
后置通知@AfterReturning:后置增强处理,在目标方法正常执行(不出现异常)后织入增强处理
环绕通知@Around:环绕增强处理,在目标方法的前后都可以织入增强处理
最终通知@After:最终增强处理,不论方法是否抛出异常,都会在目标方法最后织入增强处理
异常通知@AfterThrowing:异常增强处理,在目标方法抛出异常后织入增强处理
定义切入点@Pointcut
注解方式将Bean的定义信息和Bean实现类结合在一起,Spring提供的注解有
@Component:实现Bean组件的定义
@Repository :用于标注DAO类
@Service :用于标注业务类
@Controller :用于标注控制器类
@Repository("userDao")
public class UserDaoImpl implements UserDao {
…
}
与在XML配置文件中编写
class="dao.impl.UserDaoImpl" />
等效
使用@Autowired注解实现Bean的自动装配,默认按类型匹配,可以使用@Qualifier指定Bean的名称
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
@Qualifier("userDao")
private UserDao dao;
……
}可以对类的成员变量进行标注
@Service("userService")
public class UserServiceImpl implements UserService {
private UserDao dao;
@Autowired
public void setDao((@Qualifier("userDao") UserDao dao) {
this.dao = dao;
}
……
}也可以对方法的入参进行标注
使用注解信息启动Spring容器
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="......
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">
指定需要扫描的基类包,多个包可用逗号隔开
示例
UserAspect.java
@Component
@Aspect
public class UserAspect {
// 定义切入点
@Pointcut("execution(public * com.itxzw..*.*(..))")
public void pointcut(){}
// 前置通知
// JoinPoint: 连接点,改对象包含目标方法的相关信息
@Before("pointcut()")
public void before(JoinPoint jp){
System.out.println(jp.getTarget() + "的" + jp.getSignature().getName() + "方法执行了,参数: " + Arrays.toString(jp.getArgs()));
}
// 后置通知
@After("pointcut()")
public void after(JoinPoint jp){
System.out.println(jp.getTarget() + "的" + jp.getSignature().getName() + "之后方法执行了,参数: " + Arrays.toString(jp.getArgs()));
}
// 异常通知
@AfterThrowing(pointcut = "pointcut()", throwing = "e")
public void errorNation(JoinPoint jp, Throwable e){
System.out.println(jp.getTarget() + "的" + jp.getSignature().getName() + "方法出错了,错误是: " + e.getMessage());
}
// 返回通知
@AfterReturning(pointcut = "pointcut()", returning = "ret")
public void ReturnNation(JoinPoint jp, Object ret){
System.out.println(jp.getTarget() + "的" + jp.getSignature().getName() + "方法的返回结果是: " + ret);
}
// 环绕通知
@Around("pointcut()")
public void roundNation(ProceedingJoinPoint jp){
Object t = jp.getTarget();
String name = jp.getSignature().getName();
String args = Arrays.toString(jp.getArgs());
Object rel;
try {
System.out.println(t + "的" + name + "方法执行了,参数是:" + args);
rel = jp.proceed(jp.getArgs());
System.out.println(t + "的" + name + "方法结果是:" + rel);
}catch (Throwable e){
System.out.println(t + "的" + name + "方法出错了:" + e.getMessage());
}finally {
System.out.println(t + "的" + name + "方法结束了");
}
}
}
UserService.java
@Component(value = "userService")
public class UserService {
public Integer addUser(int n) {
Integer r = 0;
r = 100 / n;
System.out.println("addUser 运行了");
System.out.println(r);
return r;
}
public int deleteUser(String id) {
System.out.println("删除:" + id);
return 1;
}
}
applicationContext.xml
测试
@Test
public void test02(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");
userService.addUser(10);
}
效果
不积跬步无以至千里,趁年轻,使劲拼,给未来的自己一个交代!向着明天更好的自己前进吧!