一、基于注解管理的AOP
1、Spring配置文件
<!-- 配置自动扫描包,自动扫描Bean组件,切面类 -->
<context:component-scan base-package="com.zhoujian.spring.anno,com.zhoujian.spring.test">
<!-- <context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/> -->
</context:component-scan>
<!-- 启动@Aspectj支持 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
2、切面类
package com.zhoujian.spring.aspect;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
/**
* 如何声明一个切面类: 首先是放入IOC容器进行管理, 其次是需要Aspect进行修饰
*
*/
public class AuthAspectClass {
@Before(value = "execution(* com.zhoujian.spring.anno.*.*(..))")
// @Before("execution(* com.zhoujian.spring.test.*.*(..))")
public void before() {
System.out.println("模拟进行权限检查...");
}
@After("execution(* com.zhoujian.spring.anno.*.*(..))")
public void after() {
/**
* after增强处理,不管目标方法是如何结束的,该方法会在目标方法结束之后被织入
* 如果对同一目标方法进行了after和afterReturning增强处理且目标方法正常执行结束,
* 此时会先织入after增强处理,之后才是afterReturning增强处理,
* 如果目标方法非正常结束, 那么只会在目标方法结束之后,织入after增强处理,
* 如果目标方法的非正常结束抛出了异常,伴儿会在目标方法结束之后,先织入after增强处理,
* 之后才会织入afterThrowing增强处理
*/
System.out.println("关闭当前事务...");
}
@AfterReturning(returning = "returnVal", value = "execution(* com.zhoujian.spring.anno.*.*(..))")
public void afterReturning(Object returnVal) {
System.out.println("目标方法的返回值:" + returnVal);
System.out.println("只有目标方法正常执行完成后,才会执行AfterReturning...");
}
@AfterThrowing(throwing="exception",value="execution(void com.zhoujian.spring.anno.HelloImpl.getSub())")
public void afterThrowing(Exception exception){
System.out.println("只有执行com.zhoujian.spring.anno.HelloImpl.getSub() 该方法 ,才会进行增强");
System.out.println("目标方法抛出的异常为: " + exception.getMessage());
System.out.println("模拟Advice进行异常处理...");
}
@Around("execution(public int com.zhoujian.spring.anno.HelloImpl.total(..))")
public Object around(ProceedingJoinPoint joinPoint){
/**
* Around增强处理是一个很强大,但是通常需要在一个线程安全的环境使用(比如在proceed方法调用前后需要共享数据)
*/
Object result = null;
try {
Object[] args = {6,3,"4"};
/**
* 在这里传入一个Object数组,可以替换目标方法中的实参,偷天换日
* 但是,传入参数的个数和类型应该与目标方法保持一致
* 如果个数少于目标方法参数个数或者类型不匹配都会抛出异常
* 如果是 个数多余目标方法参数个数,那么会从数组下标0开始往后取出相同个数作为实参,此时如果取出的数组元素
* 类型与目标方法类型不匹配,那么也会抛出异常
*/
System.out.println(joinPoint.getThis());
System.out.println(joinPoint.getTarget());
result = joinPoint.proceed(args);
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(result);
/**
* return 返回值时 也可以进行目标方法返回值的修改,偷天换日
*/
return result;
}
}
3、业务类
package com.zhoujian.spring.anno;
import org.springframework.stereotype.Component;
@Component
public class HelloImpl implements Hello {
@Override
public void foo() {
System.out.println("执行Hello组件的 foo()方法...");
}
@Override
public int addUser(String name, String pass) {
System.out.println("执行Hello组件的addUser()方法添加用户: " + name);
return 10;
}
public void getSub(){
System.out.println("执行Hello组件的getSub()方法...");
int num = 12;
if(num % 2 == 0){
throw new NullPointerException("假装我是一个空指针异常...");
}
}
}
4、测试方法
@Test
public void beanTest(){
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
HelloImpl he = ac.getBean(HelloImpl.class);
// System.out.println(he.getClass());
he.foo();
he.addUser("张三", "123456");
he.getSub();
}
5、定义切入点
package com.zhoujian.spring.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAspectClass {
/**
* 使用 @Pointcut 定义一个切入点,方法返回类型必须是void
* 因为这仅仅只是一个签名形式的方法,为了声明切入点的名称,不需要方法体,所以方法体也为空
* 如果需要在其他的切面类中进行引用时,必须遵循Java的权限访问控制标准,
* 采用类名为前缀: value="LogAspectClass.myPointcut()"
* 或者 pointcut="LogAspectClass.myPointcut()"
*/
@Pointcut("execution(* com.zhoujian.spring.anno.*.*(..))")
public void myPointcut(){}
@Before(value="myPointcut()")
public void beforeLog(){
System.out.println("使用定义好的切入点");
}
}
二、基于XML管理的AOP
1、Spring配置文件
<!-- 切面类 -->
<bean id="logAspectClassWithXML" class="com.zhoujian.spring.aspect.LogAspectClassWithXML"></bean>
<!-- helloImpl 实现了 Hello 接口 -->
<bean id="helloImpl" class="com.zhoujian.spring.anno.HelloImpl"></bean>
<!-- helloImpl2 是一个单实体类,没有继承任何类和 实现任何接口 -->
<bean id="helloImpl2" class="com.zhoujian.spring.anno.HelloImpl2"></bean>
<!-- fordCarChild 继承了FordCar类-->
<bean id="fordCarChild" class="com.zhoujian.spring.anno.FordCarChild"></bean>
<!-- AOP 配置 -->
<aop:config>
<aop:aspect id="logAspect" ref="logAspectClassWithXML" order="1">
<aop:before method="beforeLog" pointcut="execution(* com.zhoujian.spring.anno.*.*(..))"/>
</aop:aspect>
</aop:config>
2、切面类和相关实体类
Hello接口
package com.zhoujian.spring.anno;
public interface Hello {
public void foo();
public int addUser(String name, String pass);
}
Hello实现类 HelloImpl
package com.zhoujian.spring.anno;
public class HelloImpl implements Hello {
@Override
public void foo() {
System.out.println("执行Hello组件的 foo()方法...");
}
@Override
public int addUser(String name, String pass) {
System.out.println("执行Hello组件的addUser()方法添加用户: " + name);
return 10;
}
public void getSub(){
System.out.println("执行Hello组件的getSub()方法...");
int num = 12;
if(num % 2 == 0){
throw new NullPointerException("假装我是一个空指针异常...");
}
}
public int total(int a, int b){
int total = a + b;
return total;
}
}
单实体类 HelloImpl2
package com.zhoujian.spring.anno;
public class HelloImpl2{
public void foo() {
System.out.println("执行Hello组件的 foo()方法...");
}
public int addUser(String name, String pass) {
System.out.println("执行Hello组件的addUser()方法添加用户: " + name);
return 10;
}
public void getSub(){
System.out.println("执行Hello组件的getSub()方法...");
int num = 12;
if(num % 2 == 0){
throw new NullPointerException("假装我是一个空指针异常...");
}
}
public int total(int a, int b){
int total = a + b;
return total;
}
}
切面类 LogAspectClassWithXML
package com.zhoujian.spring.aspect;
import org.apache.catalina.tribes.util.Arrays;
import org.aspectj.lang.JoinPoint;
public class LogAspectClassWithXML {
public void beforeLog(JoinPoint point){
System.out.println("Before:增强的目标方法名为:" + point.getSignature().getName());
System.out.println("Before:增强的目标方法的参数为:" + Arrays.toString(point.getArgs()));
System.out.println("Before:被织入增强的目标对象为:" + point.getTarget());
}
public void after(JoinPoint point) {
/**
* after增强处理,不管目标方法是如何结束的,该方法会在目标方法结束之后被织入
* 如果对同一目标方法进行了after和afterReturning增强处理且目标方法正常执行结束,
* 此时会先织入after增强处理,之后才是afterReturning增强处理,
* 如果目标方法非正常结束, 那么只会在目标方法结束之后,织入after增强处理,
* 如果目标方法的非正常结束抛出了异常,那么会在目标方法结束之后,先织入after增强处理,
* 之后才会织入afterThrowing增强处理
*/
System.out.println("After: 目标方法结束后释放资源...");
System.out.println("After:增强的目标方法名为:" + point.getSignature().getName());
System.out.println("After:增强的目标方法的参数为:" + Arrays.toString(point.getArgs()));
System.out.println("After:被织入增强的目标对象为:" + point.getTarget());
}
public void afterReturning(JoinPoint point, Object returnVal) {
System.out.println("AfterReturning: 获取目标方法的返回值" + returnVal);
System.out.println("AfterReturning:增强的目标方法名为:" + point.getSignature().getName());
System.out.println("AfterReturning:增强的目标方法的参数为:" + Arrays.toString(point.getArgs()));
System.out.println("AfterReturning:被织入增强的目标对象为:" + point.getTarget());
}
public void afterThrowing(JoinPoint point, Exception exception, String name, String age){
System.out.println("AfterThrowing: 获取到目标方法抛出的异常" + exception.getMessage());
System.out.println("AfterThrowing:增强的目标方法名为:" + point.getSignature().getName());
System.out.println("AfterThrowing:增强的目标方法的参数为:" + Arrays.toString(point.getArgs()));
System.out.println("AfterThrowing:被织入增强的目标对象为:" + point.getTarget());
}
}
3、测试类 AspectWithXMLTest
package com.zhoujian.spring.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.zhoujian.spring.anno.HelloImpl2;
public class AspectWithXMLTest {
@Test
public void test() {
ApplicationContext ac = new ClassPathXmlApplicationContext("aspectWithXML.xml");
System.out.println(ac.getBean("helloImpl").getClass().getName());
/**
* helloImpl 实体类实现了一个接口,所以其代理类的获取必须使用其实现的接口进行获取,不然会发生类型转换异常
*/
com.zhoujian.spring.anno.Hello hel = (com.zhoujian.spring.anno.Hello) ac.getBean("helloImpl");
hel.foo();
}
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("aspectWithXML.xml");
/**
* 单实体类:即没有继承和实现任何类和接口的实体类, 在获取其代理对象的时候,直接获取就行了
* 即使目标类继承了一个或者多个类,获取时正常获取即可,用其父类进行获取也行
*/
System.out.println(ac.getBean("helloImpl").getClass().getName());
HelloImpl2 hel = (HelloImpl2) ac.getBean("helloImpl2");
hel.foo();
}
}
注:Spring AOP 由 IOC 容器进行生成和管理,当进行IOC容器创建的时候,IOC容器会扫描切入点表达式所包含的所有包中所有类,并为所有类创建代理对象
如果目标类没有实现任何的接口,Spring将会是cglib方式创建代理对象;
如果目标类实现了一个或多个接口或者,那么Spring将会采用JDK动态代理创建代理类(此时代理类的获取必须使用接口类型接收)
使用Spring版本:spring-framework-4.3.10.RELEASE-dist