完整:
- "1.0" encoding="UTF-8"?>
- <project xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0modelVersion>
-
- <groupId>com.zhoulzgroupId>
- <artifactId>spring_aop_studyartifactId>
- <version>1.0-SNAPSHOTversion>
-
- <dependencies>
-
-
-
-
- <dependency>
- <groupId>org.springframeworkgroupId>
- <artifactId>spring-contextartifactId>
- <version>5.2.3.RELEASEversion>
- dependency>
-
-
-
- <dependency>
- <groupId>junitgroupId>
- <artifactId>junitartifactId>
- <version>4.12version>
- <scope>testscope>
- dependency>
-
-
-
- <dependency>
- <groupId>org.springframeworkgroupId>
- <artifactId>spring-aopartifactId>
- <version>5.2.3.RELEASEversion>
- dependency>
-
-
-
- <dependency>
- <groupId>cglibgroupId>
- <artifactId>cglibartifactId>
- <version>3.3.0version>
- dependency>
-
-
-
- <dependency>
- <groupId>org.aspectjgroupId>
- <artifactId>aspectjweaverartifactId>
- <version>1.9.5version>
-
- dependency>
-
-
- <dependency>
- <groupId>aopalliancegroupId>
- <artifactId>aopallianceartifactId>
- <version>1.0version>
- dependency>
-
-
- <dependency>
- <groupId>org.springframeworkgroupId>
- <artifactId>spring-aspectsartifactId>
- <version>5.2.3.RELEASEversion>
- dependency>
-
- dependencies>
-
-
- project>
首先进行pom依赖的添加,然后是xml配置文件的配置:applicatonContext.xml
- "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:component-scan base-package="com.zhoulz">context:component-scan>
-
-
- <aop:aspectj-autoproxy>aop:aspectj-autoproxy>
-
- beans>
添加注解,见 LogUtil:
- package com.zhoulz.util;
-
- import org.aspectj.lang.annotation.*;
- import org.springframework.stereotype.Component;
-
- import java.lang.reflect.Method;
-
- import java.util.Arrays;
-
- @Aspect
- @Component
- public class LogUtil {
- /**
- * 通知注解有以下几种类型:
- * @Before:前置通知,在方法执行之前完成
- * @After:后置通知,在方法执行之后执行
- * @AfterReturning:返回通知,在返回结果之后运行
- * @AfterThrowing:异常通知,出现异常的时候使用
- * @Around:环绕通知
- *
- * 注意:在方法 的参数列表中不要随便添加参数值,会有异常信息
- * */
-
- @Before("execution(public Integer com.zhoulz.service.MyCalculator.add(Integer,Integer))")
- public static void start(){ //方法里面暂时不用传入参数
- System.out.println("方法开始执行,参数是:" );
- }
- @AfterReturning("execution(public Integer com.zhoulz.service.MyCalculator.add(Integer,Integer))")
- public static void stop(){
- System.out.println("方法执行结束,结果是:" );
- }
- @AfterThrowing("execution(public Integer com.zhoulz.service.MyCalculator.add(Integer,Integer))")
- public static void logException(){
- System.out.println("方法抛出异常:");
- }
- @After("execution(public Integer com.zhoulz.service.MyCalculator.add(Integer,Integer))")
- public static void logFinally(){
- System.out.println("方法执行结束。。。。over");
- }
- }
/** * 通知注解有以下几种类型: * @Before:前置通知,在方法执行之前完成 * @After:后置通知,在方法执行之后执行 * @AfterReturning:返回通知,在返回结果之后运行 * @AfterThrowing:异常通知,出现异常的时候使用 * @Around:环绕通知 * * 注意:在方法 的参数列表中不要随便添加参数值,会有异常信息 * * 切入点表达式: * 最精确的匹配方式: —— 但是太死板了(别的方法就用不了了) * execution(public Integer com.zhoulz.service.MyCalculator.add(Integer,Integer)) * 在实际的生产环境中,更多的时候是使用通配符的方式: “*”、“..” * * : * 1、可以用来匹配一个或多个字符: * execution(public Integer com.zhoulz.service.MyCalculator.*(Integer,Integer)) * execution(public Integer com.zhoulz.service.M*Calculator.*(Integer,Integer)) * 2、匹配任意类型的参数: * execution(public Integer com.zhoulz.service.M*Calculator.*(Integer,*)) * 3、* 在进行匹配的时候,只能匹配一层路径,不能匹配多层 * execution(public Integer com.zhoulz.service.*.*.M*Calculator*.*(Integer,*)) * 4、* 不能够匹配访问修饰符,如果不确定访问修饰符是什么,可以直接省略不写 * execution(Integer com.zhoulz.service.M*Calculator.*(Integer,*)) —— 省略了public * 5、返回值可以使用*来代替,但是不能空着不写 * execution(* com.zhoulz.service.M*Calculator.*(Integer,*)) * .. : * 1、可以匹配多个参数,任意类型 * execution(public Integer com.zhoulz..M*Calculator*.*(..)) * 2、可以匹配多层路径, * execution(* com.zhoulz..M*Calculator.*(..)) * * 最偷懒的方式: * execution(* *(..)) * * 如果表达式是以*开头,那么可以代替所有: * execution(* com.*(..)) —— 不可以 * execution(* com..*(..)) —— 可以 * execution(* com.*.*(..)) —— 可以 * * 使用通配符的时候不是越简洁越好,更多的是要选择符合要求或者符合规则的匹配方式, * 此时就要求在定义标识符的时候必须要遵守项目规范 * * 在使用 表达式的时候还支持逻辑运算符: * && : 多个条件必须同时满足; * || : 多个条件只要满足其中一个即可; * ! : 取反,处理这种情况的其他都满足; * 如:execution(public Integer com.zhoulz.service.MyCalculator.*(..)) && execution(* *(..)) * !execution(public Integer com.zhoulz.service.MyCalculator.add(Integer,Integer)) * * 通知的正常执行顺序: * 如果正常执行:@Before —> @After —> @AfterReturning * 如果异常结束:@Before —> @After —> @AfterThrowing * * 如果想要在方法中获取对应的参数或者方法参数等信息的时候,必须要使用JoinPoint对象,并且此参数必须是第一个 * getSignature()、getArgs()、signature.getName() * 如果方法中有返回值,那么必须要在注解中添加returning = "result",这个result必须要和参数列表中的参数名称保持一致 * 如果需要添加异常信息,那么在注解中要添加throwing = "e",这个e必须要和参数列表中的参数名称保持一致 * 如果想要添加其他参数,必须要添加args(参数列表),ArgName(参数列表) * * 通知方法(即下面的start、stop等方法)在定义的时候有没有什么特殊的要求? * 通知方法在定义的时候对访问修饰符、返回值类型都没有明确的要求, * 但是要注意,参数不能随便添加 * * 环绕通知: * 环绕通知在执行的时候是优于普通通知的 * 如果是正常结束,那么执行的顺序是: * 环绕前置通知— @Before—环绕后置通知—环绕返回通知—@After—@AfterReturning * 如果是异常结束,那么执行的顺序是: * 环绕前置通知— @Before—环绕异常通知—环绕返回通知—@After—@AfterReturning * (注意:异常时,外部(即环绕通知外的通知)没有异常通知@AfterThrowing了,即,没有异常捕获了) * 即;如果出现异常的时候,在环绕通知中解决了,那么普通通知是接受不到异常的, * 如果想让普通通知接收到,需要进行抛出:在环绕通知中加上throw throwable * 然后执行顺序是: * 环绕前置通知— @Before—环绕异常通知—环绕返回通知—@After—@AfterThrowing * * 当应用程序中包含多个切面的时候,具体的执行顺序是怎样的? * :按照切面类的首字母进行排序操作,按照字典序A->Z * 如果需要人为的规定顺序,可以在切面上添加@Order注解,同时可以添加具体的值 * 值越小越优先 1 —> 2147483647 * */
代码示例: 代码说明见上面
LogUti:
- 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.lang.reflect.Method;
-
- import java.util.Arrays;
-
- @Aspect
- @Component
- @Order(100)
- 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("环绕通知start:"+signature.getName()+"方法开始执行,参数为:"+ Arrays.asList(args));
- //通过反射的方式调用目标的方法,相当于执行method.invoke(),可以自己修改结果值
- result = pjp.proceed(args);
- result = 100;
- System.out.println("环绕通知stop:"+signature.getName()+"方法执行结束了");
- } catch (Throwable throwable) {
- //throwable.printStackTrace();
- System.out.println("环绕异常通知:"+signature.getName()+"出现异常了");
- throw throwable;
- }finally {
- System.out.println("环绕返回通知:"+signature.getName()+"方法返回结果是:"+result);
- }
- return result;
- }*/
- }
再创建一个切面类:SecurityUtil
- 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("环绕通知start:"+signature.getName()+"方法开始执行,参数为:"+ Arrays.asList(args));
- //通过反射的方式调用目标的方法,相当于执行method.invoke(),可以自己修改结果值
- result = pjp.proceed(args);
- result = 100;
- System.out.println("环绕通知stop:"+signature.getName()+"方法执行结束了");
- } catch (Throwable throwable) {
- //throwable.printStackTrace();
- System.out.println("环绕异常通知:"+signature.getName()+"出现异常了");
- throw throwable;
- }finally {
- System.out.println("环绕返回通知:"+signature.getName()+"方法返回结果是:"+result);
- }
- return result;
- }*/
- }
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 —— 注意,这里取消了对Calculator 接口 的继承(所以不是其实现类了)
- 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;
- }
-
- }
测试类:
- 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");
-
- @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,0);
- }
- }
结果:
Security----div方法开始执行,参数是:[10, 2]
Log----div方法开始执行,参数是:[10, 2]
Log----div方法执行结束。。。。over
Log----div方法执行结束,结果是:5
Security----div方法执行结束。。。。over
Security----div方法执行结束,结果是:5