AOP切面编程:即对当前已添加入IOC容器Bean类进行相应的统一日志操作,一个切面即对Bean类的包装代理。
目录
一、注解方式进行AOP编程
1、导包:
* com.springsource.net.sf.cglib-2.2.0.jar
* com.springsource.org.aopalliance-1.0.0.jar
* com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
* spring-aspects-4.0.0.RELEASE.jar
2、在<beans>标签添加属性
xmlns:aop="http://www.springframework.org/schema/aop"
或者鼠标点击快速导入
3、 在XML文件里添加配置
使用注解方式自动扫描并自动装载入IOC容器的配置:<context:component-scan base-package="包名"></context:component-scan>
使 @AspjectJ 注解起作用的配置: <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
4、编写切面类,并在类前添加注解@Aspect,@Component。
@order(int):可不加,有多个切面类时可以指定切面类的优先级,索引从0开始
给切面类的方法前添加@注解名(切面表达式)
方法的注解:有5种,代表了当前是什么类型的包装;
切面表达式:对哪个类或者哪个方法的包装及动态代理。
@Before: 前置通知, 在方法执行之前执行
@After: 后置通知, 在方法执行之后执行,无论当前方法是否发送异常
@AfterReturning: 返回通知, 在方法返回结果之后(正常结束后)执行
@AfterThrowing: 异常通知, 在方法抛出异常之后
@Around: 环绕通知, 围绕着方法执行
切面表达式格式:
@注解类型("execution(public int 包名.类名.方法名(参数类型,..))")
注意: public int可用一个*表示通配符,int也可用一个*表示通配符,public不可以。
包名、类名、方法名都可以用*表示通配符。
参数类型可用..表示匹配数目不限的所有类型。
切面表达式例子:
public * 包名.类名.*(参数类型,..):该类所有匹配参数类型的所有公有方法
public * AOP.*.*(..)):该类所有方法
切面表达式可以提取为方法给多个注解使用:
@Component
@Aspect
class LogAspect{
@Pointcut("切面表达式")
public void 切面表达式名() {}
//前置通知
@Before("切面表达式名()")
public void beforeAdd(JoinPoint joinPoint) {
String methodName=joinPoint.getSignature().getName();
List<Object> list=Arrays.asList(joinPoint.getArgs());
System.out.println("方法开始了,方法名:"+methodName+",方法列表"+list);
}
}
示例:切面类的方法可以定义一个JoinPoint类型参数,getSignature().getName():获取被包装代理的方法名
getArgs():获取被包装代理方法的Object类型参数数组
package AOP;
import java.util.Arrays;
import java.util.List;
import org.aspectj.lang.JoinPoint;
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.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/*
* 日志切面类
*/
@Order(2)
@Component
@Aspect
public class LoggingAspect {
//切面表达式可以复用
@Pointcut("execution(public int AOP.CountService.add(..))")
public void addExecution() {}
//前置通知
@Before("addExecution()")
public void beforeAdd(JoinPoint joinPoint) {
String methodName=joinPoint.getSignature().getName();
List<Object> list=Arrays.asList(joinPoint.getArgs());
System.out.println("方法开始了,方法名:"+methodName+",方法列表"+list);
}
//后置通知
@After("addExecution()")
public void afterAdd() {
System.out.println("当前操作已执行完毕");
}
//返回通知
@AfterReturning(value="addExecution()"
,returning="result")
public void returnAdd(Object result) {
System.out.println("当前返回结果:"+result);
}
//异常通知
@AfterThrowing(value="execution(public * AOP.CountService.*(..))"
,throwing="e")
public void exceptionDiv(Exception e) {
System.out.println("异常信息:"+e.toString());
}
}
/*
* 验证切面类
*/
@Order(1)
@Component
@Aspect
class ValidateAspect{
//前置通知
@Before("execution(public int AOP.CountService.add(..))")
public void beforeAdd(JoinPoint joinPoint) {
System.out.println("我是验证,我先开始");
}
}
待包装代理类:需要装载入IOC容器,所以使用了@Service注解
package AOPAnnotation;
import org.springframework.stereotype.Service;
@Service
public class CountService {
public int add(int i,int j) {
System.out.println("now["+i+","+j+"]");
return i+j;
}
public int Div(int i,int j) throws Exception{
System.out.println("now["+i+","+j+"]");
return i/j;
}
}
5、调用方法测试,日志打印是否成功
//测试AOP切面编程
CountService countService=context.getBean(CountService.class);
countService.add(2, 3);
//关闭IOC容器:必须先转为子类ClassPathXmlApplicationContext对象后才能关闭
((AbstractApplicationContext) context).close();
注意:如果被包装代理的类是继承了接口的,则@Service,@Controller@Component@Respository是加在被包装代理类上,
而不是接口中。如果想让该接口的所有方法都被包装动态代理,则修改切面表达式如下。
格式:@注解类型("execution(public * 包名.接口名.*(参数类型,..))")
二、XML文件方式进行AOP编程
1、导包:
* com.springsource.net.sf.cglib-2.2.0.jar
* com.springsource.org.aopalliance-1.0.0.jar
* com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
* spring-aspects-4.0.0.RELEASE.jar
2、在<beans>标签添加属性
xmlns:aop="http://www.springframework.org/schema/aop"
或者鼠标点击快速导入
3、添加待包装类和切面类(此时不用添加注解,使用的是XML文件方式)
package AOPXML;
public class CountService {
public int add(int i,int j) {
System.out.println("now["+i+","+j+"]");
return i+j;
}
public int Div(int i,int j) throws Exception{
System.out.println("now["+i+","+j+"]");
return i/j;
}
}
package AOPXML;
import java.util.Arrays;
import java.util.List;
import org.aspectj.lang.JoinPoint;
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.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/*
* 日志切面类
*/
public class LoggingAspect {
//前置通知
public void beforeAdd(JoinPoint joinPoint) {
String methodName=joinPoint.getSignature().getName();
List<Object> list=Arrays.asList(joinPoint.getArgs());
System.out.println("方法开始了,方法名:"+methodName+",方法列表"+list);
}
//后置通知
public void afterAdd() {
System.out.println("当前操作已执行完毕");
}
//返回通知
public void returnAdd(Object result) {
System.out.println("当前返回结果:"+result);
}
//异常通知
public void exceptionDiv(Exception e) {
System.out.println("异常信息:"+e.toString());
}
}
/*
* 验证切面类
*/
class ValidateAspect{
//前置通知
public void beforeAdd(JoinPoint joinPoint) {
System.out.println("我是验证,我先开始");
}
}
4、在XML文件里添加配置
<!-- AOPXML文件方式切面编程 -->
<!-- 配置服务类(待包装类) -->
<bean id="countService"
class="AOPXML.CountService"></bean>
<!-- 配置切面类bean. -->
<bean id="loggingAspect"
class="AOPXML.LoggingAspect"></bean>
<bean id="vlidateAspect"
class="AOPXML.ValidateAspect"></bean>
<!-- 配置 AOP -->
<aop:config>
<!-- 配置切点表达式 -->
<aop:pointcut expression="execution(* AOPXML.*.*(int, int))"
id="expressionOne"/>
<!-- 配置切面及通知 -->
<aop:aspect ref="loggingAspect" order="2">
<aop:before method="beforeAdd" pointcut-ref="expressionOne"/>
<aop:after method="afterAdd" pointcut-ref="expressionOne"/>
<aop:after-throwing method="exceptionDiv" pointcut-ref="expressionOne" throwing="e"/>
<aop:after-returning method="returnAdd" pointcut-ref="expressionOne" returning="result"/>
</aop:aspect>
<aop:aspect ref="vlidateAspect" order="1">
<aop:before method="beforeAdd" pointcut-ref="expressionOne"/>
</aop:aspect>
</aop:config>
5、调用方法测试,日志打印是否成功
//测试AOP切面编程
CountService countService=context.getBean(CountService.class);
countService.add(2, 3);
//关闭IOC容器:必须先转为子类ClassPathXmlApplicationContext对象后才能关闭
((AbstractApplicationContext) context).close();