AOP中的术语
连接点(Joinpoint) :即可以被拦截的点,也就是某个可以进行增强的方法,比如数据库持久层的 增删改查方法就可以称为连接点
切入点(Pointcut): 即被增强的点,比如之后的例子中的delect()方法就可以称为切入点
通知(增强)(Advice):即增强的方法,也就是需要在原有方法(连接点)上额外加入的方法
织入(weaving):把将通知加到切入点上的过程就叫做织入
切面(aspect):多个通知和切入点组成的类称为切面
Spring中的通知
Spring中提供了几种通知方式,分别是前置通知,后置通知,环绕通知,异常通知,最终通知
前置通知(before): 在方法执行前调用通知
后置通知(after-returing): 在方法执行后调用通知
环绕通知(before): 在方法执行前和执行后调用通知
异常通知(before): 在方法执行时出现异常时调用通知
最终通知(before): 在方法执行后无论是否产生异常都调用通知
实例
所谓AOP,也就是面向切面编程,很多人刚开始无法理解什么叫做切面,到后面也无法理解AOP的意义在哪里,其实原因还是没有遇到那种比较复杂的开发环境,在这里举个例子:
-
大家都明白,Dao 层、service层、action层之间是一个调用的关系,假如我的dao层中有很多dao,类似Studentdao,Subjectdao…,每个dao都有增删改查四个方法,相应的service再去调用相应dao层的某个方法,得到它的返回值,检测合理性,action层再决定前端的操作,这是一般的工作模式对吧,那么突然有一天客户提出需求,需要在数据库的delate操作之前加一个权限判断,确保只有最高权限才能删除,按照我们一般的做法就是,在每个dao中的delate()中,最前面添加一个判断权限的方法的调用,假如有很多的dao,那么我们就要改很多的dao,如果下次又取消了这个需求,又得改回去,在dao很多的情况下,非常的麻烦。
我们用图解的方式说:
大家还得明白,Spring的AOP底层实现其实还是采用的代理机制,那么假如你实现了一个接口,Spring就给你采用jdkproxy,若没有实现就采用cglibproxy,下面我们用代码举个例子
首先导入所需jar包
首先写一个接口,即采用jdkproxy:
public interface IUserDao {
public String save();
public void find();
public void update();
public void delate();
}
然后写出它的实现类
public class UserDao implements IUserDao {
@Override
public String save() {
System.out.println("执行save()方法");
return "返回";
}
@Override
public void find() {
System.out.println("执行find()方法");
}
@Override
public void update() {
System.out.println("执行update()方法");
}
@Override
public void delate() {
System.out.println("执行delate()方法");
}
}
再产生它的代理类:
public class Proxy {
public void aspectbefore() {
System.out.println("前置拦截");
}
public void aspectafter(Object result) {
System.out.println("后置拦截"+":"+result);
}
//若采用环绕通知,ProceedingJoinPoint,执行proceed()方法就相当于执行实现类中的方法
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("前置通知");
joinPoint.proceed();
System.out.println("后置通知");
}
}
下面是XML的配置
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<!-- 将类交给Spring管理-->
<bean id ="userDao" class = "com.mec.spring.core6.UserDao"/>
<!--切面类 -->
<bean id ="proxy" class = "com.mec.spring.core6.Proxy"/>
<!-- 通过AOP的配置完成目标类的代理-->
<aop:config>
<!-- 写明配置类的那些方法需要增强 即配置切点 -->
<aop:pointcut expression = "execution(* com.mec.spring.core6.UserDao.save(..))" id = "pointcut1"/>
<aop:pointcut expression = "execution(* com.mec.spring.core6.UserDao.find(..))" id = "pointcut2"/>
<!-- 配置切面 -->
<aop:aspect ref = "proxy">
<!-- 前置通知 -->
<aop:before method = "aspectbefore" pointcut-ref = "pointcut1"/>
<aop:before method = "aspectbefore" pointcut-ref = "pointcut2"/>
<!-- 后置通知(after-returning) -->
<aop:after-returning method = "aspectafter" pointcut-ref = "pointcut1" returning = "result"/>
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pointcut2"/>
<!-- 异常通知(after-throwing 方法参数:Thowable getmessage()) -->
<!-- 最终通知(after)-->
</aop:aspect>
</aop:config>
</beans>
结果:
注:上面采用XML配置的方法实现AOP,也可以采用注解的方式,两者各有利弊,注解开发的话便于开发,开发速度快,但是若后期需求变更需修改源代码;XML开发的话bean之间关系很清楚,而且便于维护,只需修改配置文件而不需要修改源代码,至于采取哪种方式开发还是看个人喜好或者说公司要求。