AOP的作用
在前几天学习到的spring中的java基础(AOP)通过java程序例子已经讲述了什么是AOP。
AOP的好处就是解除非核心功能且又在核心代码类中与核心类强耦合和非核心功能的代码冗余。
AOP中的主要概念
- 横切关注点
AOP把一个业务流程分成几部分,例如日志记录,每个部分单独处理,然后把它们组装成完整的业务流,每部分被称为切面或关注点 - 切面
类是对物体特征的抽象,切面就是对横切关注点的抽风象。可以将每部分抽象成一叠纸一样,一层一层,那么每张纸都是一个切面。 - 连接点
因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器。其实,Spring只支持方法类型的连接点包含字段和构造器。因为字段通过get、set方法得到,构造器其实也是方法。 - 切入点
对连接点进行拦截的定义。 - 通知
通知指的就是指拦截到连接点之后要执行的代码。 - 目标对象
代理的目标对象。 - 织入
切面是独立的,目标对象也是独立的,它们是不耦合的,通过织入操作能将目标对象和切面联系在一起。在Spring中,ProxyFactory作为织入器。 - 引入
不改变代码的同时,为类动态地添加方法或字段。
AOP实现
基于代理的AOP
基于代理的AOP是实现了三个接口MethodBeforeAdvice
、AfterReturningAdvice
、ThrowsAdvice
。
- MethodBeforeAdvice
方法前拦截器在执行指定方法前调用。只有一个方法
public void before(Method method, Object[] objects, Object o)
参数为:
(即将执行的方法,执行时传入的参数,被拦截的Bean)
- AfterReturningAdvice
方法后拦截器在执行指定方法后调用。只有一个方法
public void afterReturning(Object o, Method method, Object[] objects, Object o1)
参数为:
(方法返回值,被调用的方法,执行时被传入的参数,被拦截的Bean)
- ThrowsAdvice
异场拦截器,在该接口中没有定义任何方法,但在Spring的反射中他只要实现了下四接口的其中一个,就会在异场时进行拦截并执行方法。
public void afterThrowing(Exception ex)
public void afterThrowing(RomoteException ex)
public void afterThrowing(Method method,Object[] args,Object targer,Exception ex)
public void afterThrowing(Method method,Object[] args,Object targer,ServletException ex)
拦截器示范
IAOPServices
创建一个接口类IAOPServices,其中拦截器作用到withAopMethod方法。
public interface IAOPServices {
public String withAopMethod() throws Exception;
public String withNoAopMethod() throws Exception;
}
AOPServicesImpl
创建一个实现接口类AOPServicesImpl
。
public class AOPServicesImpl implements IAOPServices {
private String description;
public String getDescription(){
return description;
}
public void setDescription(String description){
this.description = description;
}
@Override
public String withAopMethod() throws Exception {
System.out.println("AOP函数运行方法:withAopMethod");
if(description.trim().length()==0){
throw new Exception("description属性不能为空");
}
return description;
}
@Override
public String withNoAopMethod() throws Exception {
System.out.println("无AOP函数运行方法:withNoAopMethod");
return description;
}
}
AOPInterceptor
创建一个拦截器类AOPInterceptor
实现了上述提到的三个接口。
public class AOPInterceptor implements AfterReturningAdvice,
MethodBeforeAdvice,ThrowsAdvice {
@Override
public void afterReturning(Object o, Method method,
Object[] objects, Object o1) throws Throwable {
System.out.println("方法" + method.getName()
+ "运行结束,返回值为:"+ o);
}
@Override
public void before(Method method, Object[] objects,
Object o) throws Throwable {
System.out.println("执行MethodBeforeAdvice,即将执行的方法"
+ method.getName());
if(o instanceof AOPServicesImpl){
String description = ((AOPServicesImpl)o).getDescription();
if(description==null){
throw new NullPointerException("description属性不能为Null");
}
}
}
//通过反射进行方法匹配ThrowsAdvice中并无抽象方法
public void afterThrowing(Exception ex){
System.out.println("抛出了异常:" + ex.getMessage());
}
public void afterThrowing(Method method ,Object[] args,
Object target,Exception ex) {
System.out.println("方法" + method.getName()
+ "抛出了异常:" + ex.getMessage());
}
}
pom.xml引入依赖
在pom中添加依赖spring-aop,pom.xml
中增加以下代码。
org.springframework
spring-aop
4.2.6.RELEASE
xml注入配置
实际上无法直接完成实现类与拦截器的直接组装,因为在拦截器中并没有对应的setter、getter方法,只能先将自定义拦截器注入NmaeMatchMethodPointcutAdvisor
类的advice属性中,再将NameMatchMethodPointcutAdvisor
对象注入到ProxyFactoryBean
中。
这里将AOPInterceptor
拦截器注入到NameMathcMethodPointcutAdvisor
中。再将NameMathcMethodPointcutAdvisor
对象注入到ProxyFactoryBean
中。最后使用ProxyFactoryBean
。
aopInterceptor
main
public class AopTest {
public static void main(String args[]) throws Exception {
ApplicationContext context = new ClassPathXmlApplicationContext("basicAOP/app.xml");
IAOPServices services = (IAOPServices)context.getBean("aopService");
services.withAopMethod();
services.withNoAopMethod();
}
}
执行结果
执行MethodBeforeAdvice,即将执行的方法withAopMethod
AOP函数运行方法:withAopMethod
方法withAopMethod运行结束,返回值为:basicAOP
无AOP函数运行方法:withNoAopMethod
AspectJ基于XML
AspectJ是一个面向切面的框架,下表给出AspectJ主要的配置元素。
AOP配置元素 | 描述 |
---|---|
顶层的AOP配置元素,大多数的 |
|
定义切面 | |
启动@AspectJ注解驱动的切面 | |
定义切点 | |
定义AOP通知器 | |
定义AOP前置通知 | |
定义AOP后置通知 | |
定义成功返回后的通知 | |
定义抛出异常后的通知 | |
定义AOP环绕通知 | |
为被通知的对象引入额外的接口,并透明地实现 |
|
这里定义了XMLAdvice拦截器方法。用于演示前置、后置、成功返回、异常返回,环绕通知。
package test.Services;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class XMLAdvice {
public void beforeAdvice(){
System.out.println("前置通知执行了");
}
public void afterAdvice(){
System.out.println("后置通知执行了");
}
public void afterReturnAdvice(String result){
System.out.println("返回通知执行了" + "运行业务方法返回的结果为" + result);
}
public String aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
String result = "";
try{
System.out.println("环绕通知开始执行了");
long start = System.currentTimeMillis();
result = (String)proceedingJoinPoint.proceed();
long end = System.currentTimeMillis();
System.out.println("环绕执行结束了");
System.out.println("执行业务方法共计:" + (end - start) + "毫秒");
}catch (Exception e){
e.printStackTrace();
}
return result;
}
public void throwingAdvice(JoinPoint joinPoint,Exception e){
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("异常通知执行了");
stringBuffer.append("方法").append(joinPoint.getSignature().getName()).append("出现了异常");
stringBuffer.append("异常信息为:").append(e.getMessage());
System.out.println(stringBuffer.toString());
}
}
AspectJ的xml配置如下
注意这里的Aspectj切入点语法定义
execution(* test.basicAOP.*.withAop*(..))
分五个部分。
- execution(): 表达式主体。
- 第一个*号:表示返回类型, *号表示所有的类型。
- 包名:表示需要拦截的包名
- 第二个号:表示类名,号表示所有的类。
- (..):最后这个星号表示方法名,号表示所有的方法,即withAop后加任意字符串的所有方法,后面括弧里面表示方法的参数,两个句点表示任何参数。
运行结果
前置通知执行了
环绕通知开始执行了
AOP函数运行方法:withAopMethod
环绕执行结束了
执行业务方法共计:0毫秒
返回通知执行了运行业务方法返回的结果为basicAop
后置通知执行了
无AOP函数运行方法:withNoAopMethod
AspectJ基于注解
@Component
@Aspect
public class AnnontationAdvice {
@Before("execution(* test.basicAOP.*.withAop*(..))")
public void beforeAdvice(){
System.out.println("前置通知执行了");
}
@After("execution(* test.basicAOP.*.withAop*(..))")
public void afterAdvice(){
System.out.println("后置通知执行了");
}
@AfterReturning(value = "execution(* test.basicAOP.*.withAop*(..))",returning = "result")
public void afterReturnAdvice(String result){
System.out.println("返回通知执行了" + "运行业务方法返回的结果为" + result);
}
@Around("execution(* test.basicAOP.*.withAop*(..))")
public String aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
String result = "";
try{
System.out.println("环绕通知开始执行了");
long start = System.currentTimeMillis();
result = (String)proceedingJoinPoint.proceed();
long end = System.currentTimeMillis();
System.out.println("环绕执行结束了");
System.out.println("执行业务方法共计:" + (end - start) + "毫秒");
}catch (Exception e){
e.printStackTrace();
}
return result;
}
@AfterThrowing(value = "execution(* test.basicAOP.*.withAop*(..))",throwing = "e")
public void throwingAdvice(JoinPoint joinPoint, Exception e){
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("异常通知执行了");
stringBuffer.append("方法").append(joinPoint.getSignature().getName()).append("出现了异常");
stringBuffer.append("异常信息为:").append(e.getMessage());
System.out.println(stringBuffer.toString());
}
}
环绕通知开始执行了
前置通知执行了
AOP函数运行方法:withAopMethod
环绕执行结束了
执行业务方法共计:0毫秒
后置通知执行了
返回通知执行了运行业务方法返回的结果为basicAop
无AOP函数运行方法:withNoAopMethod