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

Spring学习笔记AOP(二)

2016-07-06 15:39 627 查看
鲁春利的工作笔记,好记性不如烂笔头

基于注解方式声明切面

要进行AOP编程,首先我们要在spring的配置文件中引入aop命名空间:




引入后AOP命名空间并启动对@AspectJ注解的支持(spring-context-aop-annotation.xml):
<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/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 启动对@AspectJ注解的支持 -->
<aop:aspectj-autoproxy />
</beans>


Spring默认不支持@AspectJ风格的切面声明,通过<aop:aspectj-autoproxy/>声明Spring就能够自动扫描被@Aspect标注的切面了。
Spring自带了名为AnnotationAwareAspectJAutoProxyCreator的自动代理类,可以在Spring的配置文件中把AnnotationAwareAspectJAutoProxyCreator注册为一个Bean,但是为了简化配置,Spring在aop命名空间提供了该元素配置,用来自动创建AnnotationAwareAspectJAutoProxyCreator类。



AnnotationAwareAspectJAutoProxyCreator会自动代理一些Bean,这些Bean的方法需要与使用@Aspect注解的Bean中所定义的切点想匹配,这些切点是通过@Pointcut注解定义的。

AOP相关的注解位于org.aspectj.lang.annotation包下。

声明切面
@AspectJ风格的声明切面非常简单,使用@Aspect注解进行声明:然后将该切面在配置文件中声明为Bean后,Spring就能自动识别并进行AOP方面的配置(就是一个普通的bean)。
@Aspect
public class LogAdapter {
// ......
}
声明切入点
@Pointcut修饰一个方法用来标识该方法为切入点方法,方法必须返回void类型。
@Pointcut(value="切入点表达式", argNames="参数名列表")
public void poincutMethod () {
// ......
}
说明:切入点@Pointcut的声明不是必须的,可以在通知声明时声明切入点表达式(但如果存在多种通知则每个通知上都需要声明切入点表达式),而声明切入点后可以在使用通知的地方引用切入点定义。

Spring支持9个@ApsectJ切点表达式函数:
1、方法切点函数
execution(方法匹配模式串)
方法匹配模式串:<注解?> <修饰符?> <返回类型> <方法名> (<参数列表>) <异常列表>
注解:可选,方法上持有的注解,如@Deprecated;
修饰符:可选,如public、protected;
返回值类型:必填,可以是任何类型模式;“*”表示所有类型;
方法名:必填,可以使用“*”进行模式匹配;
参数列表:“()”表示方法没有任何参数;“(..)”表示匹配接受任意个参数的方法,“(..,java.lang.String)”表示匹配接受java.lang.String类型的参数结束,且其前边可以接受有任意个参数的方法;“(java.lang.String,..)” 表示匹配接受java.lang.String类型的参数开始,且其后边可以接受任意个参数的方法;“(*,java.lang.String)” 表示匹配接受java.lang.String类型的参数结束,且其前边接受一个任意类型参数的方法;
异常列表:可选,以“throws 异常全限定名列表”声明,异常全限定名列表如有多个以“,”分割,如throws java.lang.IllegalArgumentException, java.lang.ArrayIndexOutOfBoundsException。

@annotation(方法注解类名)
2、方法入参切点函数
args(类名)
@args(类型注解类名)
3、目标类切点函数
within(类名匹配串)
@within(类名)
target(类型注解类名)
@target(类型注解类名)
4、代理类切点函数
this(类名)

AspectJ类型匹配的通配符:
*:匹配任何数量字符。
..:匹配任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数。

+:匹配指定类型的子类型;仅能作为后缀放在类型模式后边。
java.lang.String    匹配String类型;
java.*.String       匹配java包下的任何“一级子包”下的String类型;如匹配java.lang.String,但不匹配java.lang.ss.String
java..*             匹配java包及任何子包下的任何类型; 如匹配java.lang.String、java.lang.annotation.Annotation
java.lang.*ing      匹配任何java.lang包下的以ing结尾的类型;
java.lang.Number+   匹配java.lang包下的任何Number的自类型;如匹配java.lang.Integer,也匹配java.math.BigInteger


接口
package com.invicme.apps.aop;

/**
*
* @author lucl
*
* 数学计算接口类
*
*/
public interface ArithmeticCalculate {
public int add (int i, int j);
public int div (int i, int j);
public String validateNum (String level, int i);
}
实现类
package com.invicme.apps.aop.annotation;

import org.apache.log4j.Logger;

import com.invicme.apps.aop.ArithmeticCalculate;

/**
*
* @author lucl
*
* 数学计算实现类
*
*/
public class ArithmeticCalculateImpl implements ArithmeticCalculate {

private static final Logger logger = Logger.getLogger(ArithmeticCalculateImpl.class);

private int i = 0;
private int j = 0;

public ArithmeticCalculateImpl () {
this(0, 0);
}

public ArithmeticCalculateImpl (int i, int j) {
this.i = i;
this.j = j;
}

@Override
public int add(int i, int j) {
logger.info("The method add was invoke with args [" + i + ", " + j + "]");
int sum = i + j;
logger.info("The method add ends with result [" + sum + "]");
return sum;
}

@Override
public int div(int i, int j) {
logger.info("The method div was invoke with args [" + i + ", " + j + "]");
int result = i / j;
logger.info("The method div ends with result [" + result + "]");
return result;
}

@Override
public String validateNum(String level, int i) {
logger.info("The method validateNum was invoke with args [" + level + ", " + i + "]");
String result = this.getMsg(i);
logger.info("The method validateNum ends with result [" + result + "]");
return result;
}

private String getMsg (int i) {
if (i > 0) {
return "正数";
}
return "负数";
}
}
日志切面
package com.invicme.apps.aop.annotation;

import java.util.Arrays;
import java.util.List;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
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;
import org.aspectj.lang.annotation.Pointcut;

/**
*
* @author lucl
*
*/
@Aspect
public class LogAdapter {
private static final Logger logger = Logger.getLogger(LogAdapter.class);

/**
* Pointcut
* 定义Pointcut,Pointcut的名称为poincutMethod(),此方法没有返回值和参数
* 该方法就是一个标识,不进行调用
*/
@Pointcut(value="execution(public * com.invicme.apps.aop.annotation.ArithmeticCalculateImpl.*(..))")
public void poincutMethod () {
// ......
}

/**
* 前置通知,在目标方法执行之前被调用(JoinPoint参数不是必须的,传入是为了获取目标对象的相关属性)
*/
@Before(value="poincutMethod ()")
public void beforeAdvice (JoinPoint joinPoint) {
// 目标对象
Object target = joinPoint.getTarget();
// 目标方法
String methodName = joinPoint.getSignature().getName();
// 方法参数
List<Object> asList = Arrays.asList(joinPoint.getArgs());
/**
* target.getClass().getName() : 获取的是全路径名(包名+类名)
* target.getClass().getSimpleName() : 获取类名
*/
logger.info("[@Before]" + target.getClass().getSimpleName() + "@" + methodName + " was invoke with args " + asList + ".");
}

/**
* 后置通知(在目标方法执行之后被执行,无论该方法是否抛出异常)
*/
@After(value="poincutMethod ()")
public void afterAdvice (JoinPoint joinPoint) {
// 目标对象
Object target = joinPoint.getTarget();
// 目标方法
String methodName = joinPoint.getSignature().getName();
logger.info("[@After]" + target.getClass().getSimpleName() + "@" + methodName + " ends.");
}

/**
* 返回通知(在方法正常执行后执行,若出现异常不会被执行)
* 返回通知可以获取到目标方法的返回值
*/
@AfterReturning (value="poincutMethod ()", returning="result")
public void afterReturningAdvice (JoinPoint joinPoint, Object result) {
// 目标对象
Object target = joinPoint.getTarget();
// 目标方法
String methodName = joinPoint.getSignature().getName();
logger.info("[@AfterReturning]" + target.getClass().getSimpleName() + "@" + methodName + " ends with result " + result + ".");
}

/**
* 异常通知(当目标方法出现异常时会被执行,可以访问到异常,也可以通过指定异常类型,
* 如Exception ex,也可以为NullPointerException ex则只有空指针异常才会被执行)
*/
@AfterThrowing (value="poincutMethod ()", throwing="ex")
public void afterThrowingAdvice (JoinPoint joinPoint, Exception ex) {
// 目标对象
Object target = joinPoint.getTarget();
// 目标方法
String methodName = joinPoint.getSignature().getName();
logger.info("[@AfterThrowing]" + target.getClass().getSimpleName() + "@" + methodName + " occurs exception : " + ex + ".");
}

/**
* 环绕通知
* 说明:
*         环绕通知需要携带ProceedingJoinPoint类型的参数
*         环绕通知类似于动态代理的全过程:ProceedingJoinPoint类型参数可以决定是否执行目标方法
*         环绕通知必须有返回值,且返回值为目标方法的返回值
*/
@Around (value="poincutMethod ()")
public Object aroundAdvice (ProceedingJoinPoint joinPoint) {
// 目标对象
Object target = joinPoint.getTarget();
// 目标方法
String methodName = joinPoint.getSignature().getName();
// 参数
List<Object> asList = Arrays.asList(joinPoint.getArgs());
//
Object result = null;
// 执行目标方法
try {
// 前置通知
result = joinPoint.proceed();
// 返回通知
} catch (Throwable e) {
throw new RuntimeException(e);
// 异常通知
// 或者
// 直接throw出去(否则程序会执行到最后的return result,而result为null,目标方法可能需要类型转换,当试图将null转化为特定类型时,出错)
}
// 后置通知
logger.info("[@Around]" + target.getClass().getSimpleName() + "@" + methodName + " was invoke with args " + asList + ", ends with " + result + " .");
// 注意这里的result实际上是目标方法的返回值,如果出现问题返回值不匹配会出现错误
return result;
}
}
配置文件spring-context-aop-annotation.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/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 启动对@AspectJ注解的支持 -->
<aop:aspectj-autoproxy />

<!-- 业务类 -->
<bean id="calculate" class="com.invicme.apps.aop.annotation.ArithmeticCalculateImpl" />

<!-- 切入的日志类 -->
<bean id="logAdapter" class="com.invicme.apps.aop.annotation.LogAdapter" />
</beans>
测试类
package com.test.apps.spring.aop;

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

import com.invicme.apps.aop.ArithmeticCalculate;

/**
*
* @author lucl
*
*/
public class TestSpringAopByAnnotation {
@Test
public void testProxyFactoryBean() {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/spring-context-aop-annotation.xml");
ArithmeticCalculate calculate = context.getBean("calculate", ArithmeticCalculate.class);
calculate.add(1, 2);
System.out.println("----------------------------------------------------------------");
try {
Thread.sleep(1 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
calculate.div(1, 0);
}
}
运行结果




本文出自 “闷葫芦的世界” 博客,请务必保留此出处http://luchunli.blog.51cto.com/2368057/1810564
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: