之前,aop是通过注解的方式,在resources文件夹下定义了applicationContext.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/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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
<!--上面添加了context命名空间-->
<!--然后又添加了aop命名空间-->
<!--开启包的扫描-->
<context:component-scan base-package="com.zhoulz"></context:component-scan>
<!--开启aop的注解功能。见上面:先像context命名空间那样加后,再开启:-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!--注意:如果这里没有开启注解功能,那么LogUtil中的@Before、@After都是无效的-->
</beans>
下面介绍aop的xml配置文件的方式:
同样在resources文件夹下定义了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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
<!--aop命名空间的添加,见上-->
<!--
之前,aop是通过注解的方式,下面介绍aop的xml配置文件的方式
-->
<!--1、首先保证切面被扫描到-->
<bean id="logUtil" class="com.zhoulz.util.LogUtil" ></bean>
<bean id="securityUtil" class="com.zhoulz.util.SecurityUtil"></bean>
<!--2、然后指定好 往哪个地方去设置这些日志/安全-->
<!--之前是往service下的MyCalculator中设置的,所以:(注意:先把之前aop的注解给注释掉,方便对比)-->
<bean id="myCalculator" class="com.zhoulz.service.MyCalculator"></bean>
<!--接着,对应之前aop注解的方式,在配置文件中应该对应进行如下配置:-->
<!--3、配置aop-->
<aop:config>
<!--保险起见,或者说最好将通用表达式的抽象提取到两个切面的外部,作为公共的来用-->
<aop:pointcut id="globalPoint" expression="execution(public Integer com.zhoulz.service.MyCalculator.*(..))"/>
<!--3.1、声明切面-->
<aop:aspect ref="logUtil">
<!--发现,两个切面有通用的表达式,现在给抽取出来:(同LogUtil中表达式的抽象-创建空方法的方式)-->
<!--同时注意:这里把抽象定义在 aop:aspect ref="logUtil" 这个切面里,则表示该抽象在当前切面内可以使用。但是在 aop:aspect ref="securityUtil" 这个切面里也能用??(可以用)-->
<aop:pointcut id="myPoint" expression="execution(public Integer com.zhoulz.service.MyCalculator.*(..))"/>
<!--3.2、通知定义在哪些方法上使用-->
<!--<aop:before method="start" pointcut="execution(public Integer com.zhoulz.service.MyCalculator.*(..))"></aop:before>-->
<!--上面进行抽象后,然后,后面的表达式就都可以简写了:记住把 pointcut 改成pointcut-ref-->
<aop:before method="start" pointcut-ref="myPoint"></aop:before>
<aop:after method="logFinally" pointcut-ref="myPoint"></aop:after>
<aop:after-returning method="stop" pointcut-ref="myPoint" returning="result"></aop:after-returning>
<aop:after-throwing method="logException" pointcut-ref="myPoint" throwing="e"></aop:after-throwing>
<aop:around method="around" pointcut-ref="myPoint"></aop:around>
</aop:aspect>
<!--然后,同理,声明另一个切面:SecurityUtil-->
<aop:aspect ref="securityUtil">
<aop:before method="start" pointcut-ref="myPoint"></aop:before>
<aop:after method="logFinally" pointcut-ref="myPoint"></aop:after>
<aop:after-returning method="stop" pointcut-ref="myPoint" returning="result"></aop:after-returning>
<aop:after-throwing method="logException" pointcut-ref="globalPoint" throwing="e"></aop:after-throwing>
<aop:around method="around" pointcut-ref="globalPoint"></aop:around>
</aop:aspect>
</aop:config>
</beans>
代码示例: —— 注意,相关注解已取消
参考:11 - Spring AOP介绍与使用2 - 简单配置_哆啦A梦的_梦的博客-CSDN博客
基本相同。
Calculator接口:
package com.zhoulz.service;
import org.springframework.stereotype.Service;
//@Service
public interface Calculator {
public Integer add(Integer i,Integer j) throws NoSuchMethodException;
public Integer sub(Integer i,Integer j) throws NoSuchMethodException;
public Integer mul(Integer i,Integer j) throws NoSuchMethodException;
public Integer div(Integer i,Integer j) throws NoSuchMethodException;
}
MyCalculator 类:
package com.zhoulz.service;
import com.zhoulz.util.LogUtil;
import org.springframework.stereotype.Service;
import java.lang.reflect.Method;
//@Service
public class MyCalculator /*implements Calculator */{
public Integer add(Integer i, Integer j) throws NoSuchMethodException {
//现在,想添加日志的输出功能,怎么做?
//最简单的做法:在每行代码里面都做一个最基本的输出 —— 但是这样做效率太低了
//System.out.prIntegerln("add方法开始执行,参数是:" + i + "----" + j );
//改进: —— 定义一个LogUtil类(包含start()方法和stop()方法)
//通过反射
/*Method add = MyCalculator.class.getMethod("add", Integer.class, Integer.class);
//然后add传入到下面的方法中 —— 下面的都同理
LogUtil.start(add,i,j);*/
Integer result = i + j;
//System.out.prIntegerln("add方法执行结束,结果是:" + result);
//LogUtil.stop(add,result);
return result;
}
public Integer sub(Integer i, Integer j) throws NoSuchMethodException {
//System.out.prIntegerln("sub方法开始执行,参数是:" + i + "----" + j );
/*Method sub = MyCalculator.class.getMethod("sub", Integer.class, Integer.class);
LogUtil.start(sub,i,j);*/
Integer result = i - j;
//System.out.prIntegerln("sub方法执行结束,结果是:" + result);
//LogUtil.stop(sub,result);
return result;
}
public Integer mul(Integer i, Integer j) throws NoSuchMethodException {
//System.out.prIntegerln("mul方法开始执行,参数是:" + i + "----" + j );
//Method mul = MyCalculator.class.getMethod("mul", Integer.class, Integer.class);
//LogUtil.start(mul,i,j);
Integer result = i * j;
//System.out.prIntegerln("mul方法执行结束,结果是:" + result);
//LogUtil.stop(mul,result);
return result;
}
public Integer div(Integer i, Integer j) throws NoSuchMethodException {
//System.out.prIntegerln("div方法开始执行,参数是:" + i + "----" + j );
//Method div = MyCalculator.class.getMethod("div", Integer.class, Integer.class);
//LogUtil.start(div,i,j);
Integer result = i / j;
//System.out.prIntegerln("div方法执行结束,结果是:" + result);
//LogUtil.stop(div,result);
return result;
}
//再加一个方法
public Integer show(Integer i, Double j){
System.out.println("show ........");
return i;
}
}
util文件夹下,两个切面类:LogUtil、SecurityUtil
package com.zhoulz.util;
import com.zhoulz.service.Calculator;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Arrays;
//@Aspect
//@Component
//@Order(200)
public class LogUtil {
//如果有多个匹配的表达式相同,能否做抽象?可以
// : 定义一个没有返回值的空方法,给该方法添加@Pointcut注解,后续在使用(匹配表达式)的时候直接调用该方法名称
// 此处的方法只是起一个声明的作用,能够供内部的其他通知方法进行调用
@Pointcut("execution(public Integer com.zhoulz.service.MyCalculator.*(Integer,*))")
public void myPointCut(){} // —— 然后后面用的时候直接写:myPointCut() 即可
//还可以定义多个
@Pointcut("execution(* *(..))")
public void myPointCut2(){}
@Before("execution(public Integer com.zhoulz.service.M*Calculator.*(Integer,*))")
//public static void start(Method method,Object ... args){ //原来的写法 //方法里面暂时不用传入参数
public static void start(JoinPoint joinPoint){
//获取方法签名
Signature signature = joinPoint.getSignature();
//获取方法参数信息
Object[] args = joinPoint.getArgs();
//获取方法名称
//System.out.println(signature.getName());
System.out.println("Log----"+signature.getName()+"方法开始执行,参数是:" +Arrays.asList(args));
}
//@AfterReturning(value = "execution(public Integer com.zhoulz.service.MyCalculator.*(Integer,*))",returning = "result")
//匹配表达式进行了抽象,见上面的 myPointCut()方法
@AfterReturning(value = "myPointCut()",returning = "result")
public static void stop(JoinPoint joinPoint,Object result){ // —— 怎么加结果result?:上面的通知注解要改动
Signature signature = joinPoint.getSignature();
System.out.println("Log----"+signature.getName()+"方法执行结束,结果是:" +result);
}
@AfterThrowing(value = "myPointCut()",throwing = "e")
public static void logException(JoinPoint joinPoint,Exception e){
Signature signature = joinPoint.getSignature();
System.out.println("Log----"+signature.getName()+"方法抛出异常:" + e.getMessage());
}
@After("myPointCut2()")
public static void logFinally(JoinPoint joinPoint){
Signature signature = joinPoint.getSignature();
System.out.println("Log----"+signature.getName()+"方法执行结束。。。。over");
}
//环绕通知 —— 把上面4个通知任意拿过来执行
@Around("myPointCut2()")
public Object around(ProceedingJoinPoint pjp) throws Throwable { //需要传参
Signature signature = pjp.getSignature();
Object[] args = pjp.getArgs();
Object result = null;
try {
System.out.println("Log----"+"环绕通知start:"+signature.getName()+"方法开始执行,参数为:"+ Arrays.asList(args));
//通过反射的方式调用目标的方法,相当于执行method.invoke(),可以自己修改结果值
result = pjp.proceed(args);
//result = 100;
System.out.println("Log----"+"环绕通知stop:"+signature.getName()+"方法执行结束了");
} catch (Throwable throwable) {
//throwable.printStackTrace();
System.out.println("Log----"+"环绕异常通知:"+signature.getName()+"出现异常了");
throw throwable;
}finally {
System.out.println("Log----"+"环绕返回通知:"+signature.getName()+"方法返回结果是:"+result);
}
return result;
}
}
package com.zhoulz.util;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/*@Aspect
@Component
@Order(100)*/
public class SecurityUtil {
//如果有多个匹配的表达式相同,能否做抽象?可以
// : 定义一个没有返回值的空方法,给该方法添加@Pointcut注解,后续在使用(匹配表达式)的时候直接调用该方法名称
// 此处的方法只是起一个声明的作用,能够供内部的其他通知方法进行调用
@Pointcut("execution(public Integer com.zhoulz.service.MyCalculator.*(Integer,*))")
public void myPointCut(){} // —— 然后后面用的时候直接写:myPointCut() 即可
//还可以定义多个
@Pointcut("execution(* *(..))")
public void myPointCut2(){}
@Before("execution(public Integer com.zhoulz.service.M*Calculator.*(Integer,*))")
//public static void start(Method method,Object ... args){ //原来的写法 //方法里面暂时不用传入参数
public static void start(JoinPoint joinPoint){
//获取方法签名
Signature signature = joinPoint.getSignature();
//获取方法参数信息
Object[] args = joinPoint.getArgs();
//获取方法名称
//System.out.println(signature.getName());
System.out.println("Security----"+signature.getName()+"方法开始执行,参数是:" +Arrays.asList(args));
}
//@AfterReturning(value = "execution(public Integer com.zhoulz.service.MyCalculator.*(Integer,*))",returning = "result")
//匹配表达式进行了抽象,见上面的 myPointCut()方法
@AfterReturning(value = "myPointCut()",returning = "result")
public static void stop(JoinPoint joinPoint, Object result){ // —— 怎么加结果result?:上面的通知注解要改动
Signature signature = joinPoint.getSignature();
System.out.println("Security----"+signature.getName()+"方法执行结束,结果是:" +result);
}
@AfterThrowing(value = "myPointCut()",throwing = "e")
public static void logException(JoinPoint joinPoint,Exception e){
Signature signature = joinPoint.getSignature();
System.out.println("Security----"+signature.getName()+"方法抛出异常:" + e.getMessage());
}
@After("myPointCut2()")
public static void logFinally(JoinPoint joinPoint){
Signature signature = joinPoint.getSignature();
System.out.println("Security----"+signature.getName()+"方法执行结束。。。。over");
}
//环绕通知 —— 把上面4个通知任意拿过来执行
@Around("myPointCut2()")
public Object around(ProceedingJoinPoint pjp) throws Throwable { //需要传参
Signature signature = pjp.getSignature();
Object[] args = pjp.getArgs();
Object result = null;
try {
System.out.println("Security----"+"环绕通知start:"+signature.getName()+"方法开始执行,参数为:"+ Arrays.asList(args));
//通过反射的方式调用目标的方法,相当于执行method.invoke(),可以自己修改结果值
result = pjp.proceed(args);
//result = 100;
System.out.println("Security----"+"环绕通知stop:"+signature.getName()+"方法执行结束了");
} catch (Throwable throwable) {
//throwable.printStackTrace();
System.out.println("Security----"+"环绕异常通知:"+signature.getName()+"出现异常了");
throw throwable;
}finally {
System.out.println("Security----"+"环绕返回通知:"+signature.getName()+"方法返回结果是:"+result);
}
return result;
}
}
测试类:—— 注意:切换到 aop.xml (原来是 applicationContext.xml )
import com.zhoulz.service.Calculator;
import com.zhoulz.service.MyCalculator;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
//ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//采用xml配置文件的方式,一定要注意切换到aop.xml
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
@Test
public void test01() throws NoSuchMethodException {
//ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//Calculator calculator = context.getBean("myCalculator", Calculator.class);
//或者直接写
//Calculator calculator = context.getBean(Calculator.class);
//取消MyCalculator对Calculator的继承,再测试 —— 结果正常(即没有接口实现的话,则用的是cglib进行动态代理)
MyCalculator calculator = context.getBean(MyCalculator.class);
calculator.add(10,2);
// calculator.sub(10,2);
calculator.div(10,0);
calculator.show(10,2.5);
//System.out.println(calculator.getClass()); //class com.sun.proxy.$Proxy24
//取消继承后,结果为:class com.zhoulz.service.MyCalculator$$EnhancerBySpringCGLIB$$77df863d
}
@Test
public void test02() throws NoSuchMethodException {
MyCalculator calculator = context.getBean(MyCalculator.class);
//calculator.add(10,2);
calculator.div(10,2);
}
}
结果:
Log----div方法开始执行,参数是:[10, 2] Log----环绕通知start:div方法开始执行,参数为:[10, 2] Security----div方法开始执行,参数是:[10, 2] Security----环绕通知start:div方法开始执行,参数为:[10, 2] Security----环绕通知stop:div方法执行结束了 Security----环绕返回通知:div方法返回结果是:5 Security----div方法执行结束,结果是:5 Security----div方法执行结束。。。。over Log----环绕通知stop:div方法执行结束了 Log----环绕返回通知:div方法返回结果是:5 Log----div方法执行结束,结果是:5 Log----div方法执行结束。。。。over
|