V哥官网:http://www.vgxit.com
本博客对应视频教程:http://www.vgxit.com/course/23
Spring Aop基于AspectJ开发
1,选择切点
Spring是一个方法级别的AOP框架。也就是我们可以再调用某个方法之前,之后,正常返回之后,异常返回之后来切入我们的代码。我们需要被拦截的方法就叫做一个切点。那么我们应该创建对应的切点。这里我们就直接把上节课讲的添加用户复制过来作为我们的切点。
1,创建对应的PO:
package com.vgxit.learn.spring.ktdm.springaop.po;
import lombok.Data;
@Data
public class User {
private Integer id;
private String name;
private Short gender;
}
2,创建对应的切点:
package com.vgxit.learn.spring.ktdm.springaop.service;
import com.vgxit.learn.spring.ktdm.springaop.po.User;
public interface IUserService {
void add(User user);
}
package com.vgxit.learn.spring.ktdm.springaop.service.impl;
import com.vgxit.learn.spring.ktdm.springaop.po.User;
import com.vgxit.learn.spring.ktdm.springaop.service.IUserService;
public class UserService implements IUserService {
@Override
public void add(User user) {
System.out.println("添加用户[" + user + "]到数据库");
throw new RuntimeException("添加错误");
}
}
2,创建切面
选择好了切点之后,我们就可以创建切面了。切面就如同我们上节课写的拦截器。在spring中,我们只需要提供一个AspectJ注解,就可以让一个类成一个切面。但是如果我们想要使用这个注解,必须引入对应的依赖包:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
接下来我们创建对应的切面:
package com.vgxit.learn.spring.ktdm.springaop.aspect;
import org.aspectj.lang.annotation.*;
@Aspect//使用了这个注解就表明该类为一个切面类
public class TranAspect {
@Before("execution(* com.vgxit.learn.spring.ktdm.springaop.service.impl.UserService.add(..))")
public void before() {
System.out.println("获取数据库连接");
System.out.println("开启事务");
}
@After("execution(* com.vgxit.learn.spring.ktdm.springaop.service.impl.UserService.add(..))")
public void after() {
System.out.println("对应逻辑执行完成");
}
@AfterReturning("execution(* com.vgxit.learn.spring.ktdm.springaop.service.impl.UserService.add(..))")
public void afterReturning() {
System.out.println("提交事物");
}
@AfterThrowing("execution(* com.vgxit.learn.spring.ktdm.springaop.service.impl.UserService.add(..))")
public void afterThrowing() {
System.out.println("事物回滚");
}
}
上面我们使用了Aspect来定义对应的类是一个切面。然后我们使用了4个注解来定义对应的方法。这四个注解的含义如下:
- @Before:在切点调用之前前置通知执行的方法。
- @After:在切点调用之后后置通知执行的方法。
- @AfterReturning:在被代理对象的方法正常返回之后调用
- @AfterThrowing:在被代理对象的方法抛出异常之后调用
3,连接点
连接点在AOP中的作用其实就是用来判断是否要拦截对应的方法。比如我们上面的4个注解我们就可以理解成为连接点。
比如我们的@After("execution(* com.vgxit.learn.spring.ktdm.springaop.service.impl.UserService.add(..))"):
- execution:代表执行方法的时候触发
- *:代表方法可以是任意返回类型
- com.vgxit.learn.spring.ktdm.springaop.service.impl.UserService表示对应的方法提供者的全限定名
- add:表示被拦截的方法的名称
- (..):表示是任意类型的参数
4,测试运行
1,首先应该创建一个配置类
package com.vgxit.learn.spring.ktdm.springaop.config;
import com.vgxit.learn.spring.ktdm.springaop.aspect.TranAspect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@ComponentScan("com.vgxit.learn.spring.ktdm.springaop")
@EnableAspectJAutoProxy//表示spring启动AspectJ框架自动代理
@Configuration
public class SpringAopConfig {
//注入切面
@Bean
public TranAspect tranAspect() {
return new TranAspect();
}
}
2,测试:
package com.vgxit.learn.spring.ktdm.springaop.test;
import com.vgxit.learn.spring.ktdm.springaop.config.SpringAopConfig;
import com.vgxit.learn.spring.ktdm.springaop.po.User;
import com.vgxit.learn.spring.ktdm.springaop.service.IUserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class SpringaopTest001 {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringAopConfig.class);
IUserService userService = ctx.getBean(IUserService.class);
User user = User.builder().id(1).name("李一桐").gender((short) 2).build();
userService.add(user);
}
}
5,简化连接点操作
我们上面写的这个切面是比较复杂的,每一个连接点我们都把自己的配置重复写了一遍,其实这个完全没有,我们可以简化。
package com.vgxit.learn.spring.ktdm.springaop.aspect;
import org.aspectj.lang.annotation.*;
@Aspect//使用了这个注解就表明该类为一个切面类
public class TranAspect {
@Pointcut("execution(* com.vgxit.learn.spring.ktdm.springaop.service.impl.UserService.add(..))")
public void tranPointer(){}
@Before("tranPointer()")
public void before() {
System.out.println("获取数据库连接");
System.out.println("开启事务");
}
@After("tranPointer()")
public void after() {
System.out.println("对应逻辑执行完成");
}
@AfterReturning("tranPointer()")
public void afterReturning() {
System.out.println("提交事物");
}
@AfterThrowing("tranPointer()")
public void afterThrowing() {
System.out.println("事物回滚");
}
}