您的位置:首页 > 编程语言 > Java开发

Spring学习(六)-面向切面编程(AOP)

2017-08-24 20:35 721 查看
在软件系统中,存在一些功能需要用到应用程序的多个地方,但是我们又不想在每个点都去调用它,比如日志,事务管理,安全等。如果我们让应用对象只关注于自己所针对的业务领域问题,而上述这些功能由其他应用对象自行处理。为解决上述问题,Spring提出了面向切面的编程思想。在给出AOP的具体实现之前,先给出AOP相关的一些具体概念。

1、基本概念

(1)横切关注点

在软件开发过程中,散布于应用中多处的功能。面向切面编程的目的就是解决把横切关注点和业务逻辑相分离。

切面:把横切关注点模块化成为一些特殊的类。切面告诉我们是什么,在何时何处完成 其功能。

(2)通知

在AOP中,切面的工作称之为通知。通知主要定义了切面是什么,以及何时使用。

Spring切面可以应用5种类型的通知:

前置通知(Before):在目标方法被调用之前调用通知功能;

后置通知(After):在目标方法完成之后调用,此时不会关心方法的输出是什么;

返回通知(After-returning):在目标方法执行成功后调用通知;

异常通知(After-throwing):在目标方法抛出异常后调用通知;

环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为

(3)切点

切点定义了通知在何处执行。切点和通知结合构成了一个完整的切面。

2、AOP具体实现

除了Spring的基础jar外,还需要额外导入aspectjweaver.jar,aopalliance.jar,aspectjrt.jar

(1)在XML中声明切面

定义一个接口IHelloWorldService :

package com.wygu.service;

public interface IHelloWorldService {

public void sayBefore();

public void sayAfter();

public boolean sayAfterReturning();

public void sayAfterThrow();

public void sayAround();

}


编写接口的实现HelloWorldService :

package com.wygu.impl;

import com.wygu.service.IHelloWorldService;

public class HelloWorldService implements IHelloWorldService{

@Override
public void sayBefore() {
System.out.println("Before Hello World!");

}

@Override
public void sayAfter() {
System.out.println("After Hello World!");

}

@Override
public boolean sayAfterReturning() {
System.out.println("After Returning Hello World!");
return true;
}

@Override
public void sayAfterThrow() {
System.out.println("After Throw Hello World!");
throw new RuntimeException("后置抛出异常");
}

@Override
public void sayAround() {
System.out.println("Around Hello World!");

}
}


定义切面HelloWorldAspect:

packa
1160d
ge com.wygu.aop;

import org.aspectj.lang.ProceedingJoinPoint;

public class HelloWorldAspect {

public void beforeAdvice(){
System.out.println("*******************前置通知");
}

public void afterAdvice(){
System.out.println("*******************后置通知");
}

public void afterReturnAdvice(Object retVal){
System.out.println("*******************返回通知"+":"+retVal);
}

public void afterThrowingAdvice(Exception exception){
System.out.println("*******************异常通知");
exception.printStackTrace();
}

public void aroundAdvice(ProceedingJoinPoint pjp){
try {
System.out.println("******************环绕之前通知");
//执行环绕方法
pjp.proceed();
System.out.println("******************环绕之后通知");
} catch (Throwable e) {
e.printStackTrace();
System.out.println("******************执行环绕方法失败");
}

}

}


定义配置文件spring-aop.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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

<!-- 定义Bean实例 -->
<bean id="helloWorldService"  class="com.wygu.impl.HelloWorldService"/>
<!-- -定义切面Bean实例-->
<bean id="helloAspect"  class="com.wygu.aop.HelloWorldAspect"/>

<aop:config>

<!-- -声明切面 -->
<aop:aspect id="ascept" ref="helloAspect">

<!-- -声明切点 -->
<aop:pointcut id="pointcut1" expression="execution(* com.wygu.impl.*.sayBefore(..))" />
<aop:pointcut id="pointcut2" expression="execution(* com.wygu.impl.*.sayAfter(..))" />
<aop:pointcut id="pointcut3" expression="execution(* com.wygu.impl.*.sayAfterReturning(..))" />
<aop:pointcut id="pointcut4" expression="execution(* com.wygu.impl.*.sayAfterThrow(..))" />
<aop:pointcut id="pointcut5" expression="execution(* com.wygu.impl.*.sayAround(..))" />

<!-- -前置通知 -->
<aop:before method="beforeAdvice" pointcut-ref="pointcut1"/>
<!-- -后置通知 -->
<aop:after method="afterAdvice" pointcut-ref="pointcut2" />
<!-- -后置返回通知 -->
<aop:after-returning method="afterReturnAdvice" pointcut-ref="pointcut3"
arg-names="retVal" returning="retVal" />
<!-- -后置异常通知 -->
<aop:after-throwing method="afterThrowingAdvice" pointcut-ref="pointcut4"
arg-names="exception"  throwing="exception" />
<!-- -环绕通知 -->
<aop:around method="aroundAdvice" pointcut-ref="pointcut5"/>

</aop:aspect>
</aop:config>

</beans>


程序测试:

package com.wygu.main;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.wygu.service.IHelloWorldService;

public class TestAOP {

public static void main(String[] args) throws InterruptedException {
@SuppressWarnings("resource")
ApplicationContext context = new ClassPathXmlApplicationContext("spring-aop.xml");
IHelloWorldService helloWorldService = (IHelloWorldService) context.getBean("helloWorldService");
helloWorldService.sayBefore();
helloWorldService.sayAfter();
helloWorldService.sayAfterReturning();
try {
helloWorldService.sayAfterThrow();
} catch (Exception e) {
Thread.sleep(1000);
}

helloWorldService.sayAround();

}

}


最终执行结果为:

八月 28, 2017 8:23:51 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@55801443: startup date [Mon Aug 28 20:23:51 GMT+08:00 2017]; root of context hierarchy
八月 28, 2017 8:23:51 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring-aop.xml]
*******************前置通知
Before Hello World!
After Hello World!
*******************后置通知
After Returning Hello World!
*******************返回通知:true
After Throw Hello World!
*******************异常通知
java.lang.RuntimeException: 后置抛出异常
at com.wygu.impl.HelloWorldService.sayAfterThrow(HelloWorldService.java:28)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:62)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy2.sayAfterThrow(Unknown Source)
at com.wygu.main.TestAOP.main(TestAOP.java:18)
******************环绕之前通知
Around Hello World!
******************环绕之后通知


(2)使用注解创建切面

我们利用XML方式实现了AOP的五种通知类型,然而在实际开发中,使用比较多的还是利用注解的方式实现注入,下面利用注解实现切面的五种通知类型。

接口IHelloWorldService和接口的实现类HelloWorldService同上一个实例。

修改切面HelloWorldAspect

package com.wygu.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class HelloWorldAspect {

@Before("execution(* com.wygu.impl.*.sayBefore(..))")
public void beforeAdvice(){
System.out.println("*******************前置通知");
}

@After("execution(* com.wygu.impl.*.sayAfter(..))")
public void afterAdvice(){
System.out.println("*******************后置通知");
}

@AfterReturning(value="execution(* com.wygu.impl.*.sayAfterReturning(..))", returning="retVal")
public void afterReturnAdvice(Object retVal){
System.out.println("*******************返回通知"+":"+retVal);
}

@AfterThrowing(value="execution(* com.wygu.impl.*.sayAfterThrow(..))",throwing="exception" )
public void afterThrowingAdvice(Exception exception){
System.out.println("*******************异常通知");
exception.printStackTrace();
}

@Around("execution(* com.wygu.impl.*.sayAround(..))")
public void aroundAdvice(ProceedingJoinPoint pjp){
try {
System.out.println("******************环绕之前通知");
//执行环绕方法
pjp.proceed();
System.out.println("******************环绕之后通知");
} catch (Throwable e) {
e.printStackTrace();
System.out.println("******************执行环绕方法失败");
}

}
}


定义spring-aop.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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

<!-- 启用AspectJ自动代理 -->
<aop:aspectj-autoproxy />

<!-- 定义Bean实例 -->
<bean id="helloWorldService"  class="com.wygu.impl.HelloWorldService"/>
<!-- -定义切面Bean实例-->
<bean id="helloAspect"  class="com.wygu.aop.HelloWorldAspect"/>

</beans>


在xml中声明aspect的代理对象,使得在初始化spring 容器的时候,spring会自动对切点生成代理对象。如下:

<!-- 启用AspectJ自动代理 -->
<aop:aspectj-autoproxy />


运行结果为:

*******************前置通知
Before Hello World!
After Hello World!
*******************后置通知
After Returning Hello World!
*******************返回通知:true
After Throw Hello World!
*******************异常通知
java.lang.RuntimeException: 后置抛出异常
at com.wygu.impl.HelloWorldService.sayAfterThrow(HelloWorldService.java:28)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:62)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy10.sayAfterThrow(Unknown Source)
at com.wygu.main.TestAOP.main(TestAOP.java:19)
******************环绕之前通知
Around Hello World!
******************环绕之后通知
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: