SpringAOP:面向切面编程
首先要懂几个专业术语
- Aspect:切面,其实也就是一个普通的类,不依赖任何的类
- Join point:连接点,某个程序执行到某个方法的时候,可能在这个方法前要执行或者在这个方法后要执行或者异常要执行等这个要执行的特定方法叫做连接点
- Advice:增强(建议),增强有几种前置、后置、异常、环绕等,就是在程序某个方法上要执行的特定的操作叫做增强
- Pointcut:切入点,其实也就是一个表达式,用来判断在什么方法上要切入
- Target object:目标类对象,就是需要增强的方法的类的对象
- AOP proxy:AOP代理,由AOP框架创建的对象,就是目标类方法增强的过程。在Spring
Framework中,AOP代理是JDK动态代理或CGLIB代理。
得知道springAOP是由spring Framework自动帮我们完成的,所以在执行AOP之前IOC一定得已经完成了,也就是说springAOP之前一定得有springIOC,但是springIOC不一定得有springAOP
下面来使用springAOP
//创建一个接口
public interface Studentdao {
public int add(String name);
public void update(String name);
public void find(String name);
}
//创建几个类方便我们用来测试
@Repository
public class Studentdaojpa implements Studentdao {
public int add(String name) {
System.out.println("jpa添加成功"+name);
Random r=new Random();
return r.nextInt();
}
public void update(String name) {
System.out.println("jpa修改成功"+name);
}
public void find(String name) {
System.out.println("jpa查找成功"+name);
}
}
@Repository
public class Studentdaomy implements Studentdao {
public int add(String name) {
System.out.println("my添加成功"+name);
Random r=new Random();
return r.nextInt();
}
public void update(String name) {
System.out.println("my修改成功"+name);
}
public void find(String name) {
System.out.println("my查找成功"+name);
}
}
public interface Student {
int add(String name);
void update(String name);
void find(String name);
}
创建目标类
@Service
public class Studentbiz implements Student{
@Autowired
@Qualifier("studentdaojpa")
private Studentdao studentdao;
public Studentbiz(Studentdao studentdao) {
this.studentdao = studentdao;
}
public Studentbiz() {
}
public int add(String name){
System.out.println("=================业务层==================");
System.out.println("用户是否重名");
int resulte=studentdao.add(name);
System.out.println("添加成功");
return resulte;
}
public void update(String name){
System.out.println("=================业务层==================");
System.out.println("用户是否重名");
studentdao.update(name);
System.out.println("修改成功");
}
public void find(String name){
System.out.println("=================业务层==================");
System.out.println("查找:"+name);
studentdao.find(name);
System.out.println("查找成功成功");
}
}
下面就是来创建一个切面类
@Aspect//切面类: 你要增强的功能写到这里
@Component//IOC注解已实现spring托管的功能
public class LogAspect {
//切入点的声明 *表示修饰符 中间表示方法路径 (..)表示参数
@Pointcut("execution(* com.yc.biz.Studentbiz.add(..))") // the pointcut expression 切入点表达式:那些方法上增加增强
private void add() {} // the pointcut signature
@Pointcut("execution(* com.yc.biz.Studentbiz.update(..))") // the pointcut expression 切入点表达式:那些方法上增加增强
private void update() {} // the pointcut signature
@Pointcut("add()||update())") // the pointcut expression 切入点表达式:那些方法上增加增强
private void addAndupate() {} // the pointcut signature
//切入点表达式的语法: ?代表出现0次或一次
//modifiers-pattern:修饰符
//ret-type-pattern:返回类型
//declaring-type-pattern:
//name-pattern:方法名
// execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
// throws-pattern?)
//增强的声明
@Before("com.yc.aspect.LogAspect.addAndupate()")//要么写切入点表达式要么写切入点方法名
public void log() {
System.out.println("==========前置增强的日志=================");
Date d=new Date();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(d).toString());
System.out.println("==========前置增强结束=================");
}
}
}
可以看到这里我们是对StudentBiz里面的add()和update方法增强
//容器配置类
@Configuration
@ComponentScan(basePackages = {"com.yc"})
@EnableAspectJAutoProxy//启用AspectJ的注解支持
public class MyAppConfig {
}
做一个测试类:
//这里我是用的spring来整合了junit框架
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = MyAppConfig.class)
public class StudentbizTest {
//@Resource(name ="studentbiz")//先按名字如果没有同名的则按类型查找bean然后注入
@Autowired//只按类型注入
private Student st;
@Test
public void testAdd() {
st.add("张三");//如果Studentbiz没有现实一个接口则是用CGLIB代理 反之则使用JDK代理
}
@Test
public void testUpdate() {
st.update("张三");
}
@Test
public void testFind() {
st.find("张三");
}
}
运行得到结果可以看到输入的结果和我们设置的一样
但是他使用的是什么代理呢? 我们可以看看这里在st.add()上打一个断点然后debug来看看
可以看到是JDK动态代理
然后我们把StudentBiz类的代码改改让他不实现Student接口
然后把测试类里面的也Student也改成Studentbiz
继续debug运行测试
由此可以得出AOP的代理可以分为一下两类
- JDK动态代理:目标类必须实现接口
- CGLIB代理:目标类必须继承某个类
如果同时两个增强作用在同一个方法上面谁先运行?
查询官方文档得知
有一个@Order的注解用来解决这类问题
表示增强调用的顺序谁的value小谁先运行 如果是环绕增强则小的先运行然后再运行大的再运行目标方法然后大的先结束小的后结束
这里给大家测试一下
@Aspect//切面类: 你要增强的功能写到这里
@Component//IOC注解已实现spring托管的功能
@Order(value = 1)//表示增强调用的顺序谁的value小谁先运行 如果是环绕增强则小的先运行然后再运行大的再运行目标方法然后大的先结束小的后结束
public class LogAspect {
//这里和上面一样
...
//加入一个环绕增强ProceedingJoinPoint是Around规定的第一个参数必须为它
@Around("execution(* com.yc.biz.Studentbiz.find(..))")
public Object compute1(ProceedingJoinPoint pjp) throws Throwable {//这是DI操作
System.out.println("compute1");
long start=System.currentTimeMillis();
Object retVal=pjp.proceed();//目标类的目标方法
long end=System.currentTimeMillis();
System.out.println("compute1用时:"+(end-start));
return retVal;
}
}
//再做一个切面同样的功能
@Aspect//切面类: 你要增强的功能写到这里
@Component//IOC注解已实现spring托管的功能
@Order(value = 122)
public class Log3Aspect {
@Pointcut("execution(* com.yc.biz.Studentbiz.add(..))") // the pointcut expression 切入点表达式:那些方法上增加增强
private void add() {} // the pointcut signature
@Pointcut("execution(* com.yc.biz.Studentbiz.update(..))") // the pointcut expression 切入点表达式:那些方法上增加增强
private void update() {} // the pointcut signature
@Pointcut("add()||update())") // the pointcut expression 切入点表达式:那些方法上增加增强
private void addAndupate() {} // the pointcut signature
//@Around("execution(* com.yc.biz.Studentbiz.find(..))")
public Object compute2(ProceedingJoinPoint pjp) throws Throwable {//这是DI操作
System.out.println("compute2");
long start=System.currentTimeMillis();
Object retVal=pjp.proceed();//目标类的目标方法
long end=System.currentTimeMillis();
System.out.println("compute2用时:"+(end-start));
return retVal;
}
}
然后我们运行测试类里的find()方法
可以看到他先是运行了compute1()方法然后再运行了compute2()方法然后执行目标方法然后再继续执行compute2()方法最后再执行完compute1()方法
如果我们把两个切面的values对换一下再来执行看结果
这样就会发现执行的顺序就反过来了
以上就是我个人对AOP的理解了