番禺响应式网站建设电子商务网站建设与管理课程的感想
news/
2025/9/23 5:43:13/
文章来源:
番禺响应式网站建设,电子商务网站建设与管理课程的感想,沈阳企业网站优化排名方案,免费网站流量统计工具Spring结合自定义注解实现 AOP 切面功能 Spring AOP 注解概述Aspect 快速入门execution 切点表达式 拦截指定类的方法Pointcut(annotation(xx)) 拦截拥有指定注解的方法环绕通知 实现开关目标方法案例1#xff1a;自定义注解切面实现统一日志处理1.自定义日志注解… Spring结合自定义注解实现 AOP 切面功能 Spring AOP 注解概述Aspect 快速入门execution 切点表达式 拦截指定类的方法Pointcut(annotation(xx)) 拦截拥有指定注解的方法环绕通知 实现开关目标方法案例1自定义注解切面实现统一日志处理1.自定义日志注解2声明日志切面组件3.控制层运行结果4.运行结果 案例2自定义注解与切面类1、创建自定义注解2、创建一个类定义方法后使用自定义注解3、定义切面类进行扫描自定义注解并对切入点进行处理 Spring AOP 注解概述
1、Spring 的 AOP 功能除了在配置文件中配置一大堆的配置比如切入点、表达式、通知等等以外使用注解的方式更为方便快捷特别是 Spring boot 出现以后基本不再使用原先的 beans.xml 等配置文件了而都推荐注解编程。
注解功能Aspect切面声明标注在类、接口包括注解类型或枚举上。Pointcut切入点声明即切入到哪些目标类的目标方法。既可以用 execution 切点表达式, 也可以是 annotation 指定拦截拥有指定注解的方法.value 属性指定切入点表达式默认为 “”用于被通知注解引用这样通知注解只需要关联此切入点声明即可无需再重复写切入点表达式Before前置通知, 在目标方法(切入点)执行之前执行。value 属性绑定通知的切入点表达式可以关联切入点声明也可以直接设置切入点表达式注意如果在此回调方法中抛出异常则目标方法不会再执行会继续执行后置通知 - 异常通知。After后置通知, 在目标方法(切入点)执行之后执行AfterReturning返回通知, 在目标方法(切入点)返回结果之后执行.pointcut 属性绑定通知的切入点表达式优先级高于 value默认为 “”AfterThrowing异常通知, 在方法抛出异常之后执行, 意味着跳过返回通知pointcut 属性绑定通知的切入点表达式优先级高于 value默认为 注意如果目标方法自己 try-catch 了异常而没有继续往外抛则不会进入此回调函数Around环绕通知目标方法执行前后分别执行一些代码类似拦截器可以控制目标方法是否继续执行。通常用于统计方法耗时参数校验等等操作。 正常流程【环绕通知-前】- 【前置通知】- 【返回通知】- 【后置通知】-【环绕通知-后】。 2、上面这些 AOP 注解都是位于如下所示的 aspectjweaver 依赖中 3、对于习惯了 Spring 全家桶编程的人来说并不是需要直接引入 aspectjweaver 依赖因为 spring-boot-starter-aop 组件默认已经引用了 aspectjweaver 来实现 AOP 功能。换句话说 Spring 的 AOP 功能就是依赖的 aspectjweaver
!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop --
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-aop/artifactIdversion2.1.4.RELEASE/version
/dependency4、AOP 底层是通过 Spring 提供的的动态代理技术实现的在运行期间动态生成代理对象代理对象方法执行时进行增强功能的介入再去调用目标对象的方法从而完成功能的增强。主要使用 JDK 动态代理与 Cglib 动态代理。
5、所以如果目标类不是 Spring 组件则无法拦截如果是 类名.方法名 方式调用也无法拦截。
Aspect 快速入门
1、Aspect 常见用于记录日志、异常集中处理、权限验证、Web 参数校验、事务处理等等 2、要想把一个类变成切面类只需3步
在类上使用 Aspect 注解使之成为切面类切面类需要交由 Sprign 容器管理所以类上还需要有 Service、Repository、Controller、Component 等注解在切面类中自定义方法接收通知
3、AOP 的含义就不再累述了下面直接上示例
/*** 切面注解 Aspect 使用入门* 1、Aspect声明本类为切面类* 2、Component将本类交由 Spring 容器管理* 3、Order指定切入执行顺序数值越小切面执行顺序越靠前默认为 Integer.MAX_VALUE** author wangMaoXiong* version 1.0* date 2020/8/20 19:22*/
Aspect
Order(value 999)
Component
public class AspectHelloWorld {private static final Logger LOG LoggerFactory.getLogger(AspectHelloWorld.class);/*** Pointcut 切入点声明即切入到哪些目标方法。value 属性指定切入点表达式默认为 。* 用于被下面的通知注解引用这样通知注解只需要关联此切入点声明即可无需再重复写切入点表达式* p* 切入点表达式常用格式举例如下* - * com.wmx.aspect.EmpService.*(..))表示 com.wmx.aspect.EmpService 类中的任意方法* - * com.wmx.aspect.*.*(..))表示 com.wmx.aspect 包(不含子包)下任意类中的任意方法* - * com.wmx.aspect..*.*(..))表示 com.wmx.aspect 包及其子包下任意类中的任意方法* /p* value 的 execution 可以有多个使用 || 隔开.*/Pointcut(value execution(* com.wmx.hb.controller.DeptController.*(..)) || execution(* com.wmx.hb.controller.EmpController.*(..)))private void aspectPointcut() {}/*** 前置通知目标方法执行之前执行以下方法体的内容。* value绑定通知的切入点表达式。可以关联切入点声明也可以直接设置切入点表达式* br/* * param joinPoint提供对连接点处可用状态和有关它的静态信息的反射访问br/ p* * * Object[] getArgs()返回此连接点处目标方法的参数目标方法无参数时返回空数组* * * Signature getSignature()返回连接点处的签名。* * * Object getTarget()返回目标对象* * * Object getThis()返回当前正在执行的对象* * * StaticPart getStaticPart()返回一个封装此连接点的静态部分的对象。* * * SourceLocation getSourceLocation()返回与连接点对应的源位置* * * String toLongString()返回连接点的扩展字符串表示形式。* * * String toShortString()返回连接点的缩写字符串表示形式。* * * String getKind()返回表示连接点类型的字符串* * * /p*/Before(value aspectPointcut())public void aspectBefore(JoinPoint joinPoint) {Object[] args joinPoint.getArgs();Signature signature joinPoint.getSignature();Object target joinPoint.getTarget();Object aThis joinPoint.getThis();JoinPoint.StaticPart staticPart joinPoint.getStaticPart();SourceLocation sourceLocation joinPoint.getSourceLocation();String longString joinPoint.toLongString();String shortString joinPoint.toShortString();LOG.debug(【前置通知】 args{},signature{},target{},aThis{},staticPart{}, sourceLocation{},longString{},shortString{}, Arrays.asList(args), signature, target, aThis, staticPart, sourceLocation, longString, shortString);}/*** 后置通知目标方法执行之后执行以下方法体的内容不管目标方法是否发生异常。* value绑定通知的切入点表达式。可以关联切入点声明也可以直接设置切入点表达式*/After(value aspectPointcut())public void aspectAfter(JoinPoint joinPoint) {LOG.debug(【后置通知】kind{}, joinPoint.getKind());}/*** 返回通知目标方法返回后执行以下代码* value 属性绑定通知的切入点表达式。可以关联切入点声明也可以直接设置切入点表达式* pointcut 属性绑定通知的切入点表达式优先级高于 value默认为 * returning 属性通知签名中要将返回值绑定到的参数的名称默认为 ** param joinPoint 提供对连接点处可用状态和有关它的静态信息的反射访问* param result 目标方法返回的值参数名称与 returning 属性值一致。无返回值时这里 result 会为 null.*/AfterReturning(pointcut aspectPointcut(), returning result)public void aspectAfterReturning(JoinPoint joinPoint, Object result) {LOG.debug(【返回通知】,shortString{},result, joinPoint.toShortString(), result);}/*** 异常通知目标方法发生异常的时候执行以下代码此时返回通知不会再触发* value 属性绑定通知的切入点表达式。可以关联切入点声明也可以直接设置切入点表达式* pointcut 属性绑定通知的切入点表达式优先级高于 value默认为 * throwing 属性与方法中的异常参数名称一致** param ex捕获的异常对象名称与 throwing 属性值一致*/AfterThrowing(pointcut aspectPointcut(), throwing ex)public void aspectAfterThrowing(JoinPoint jp, Exception ex) {String methodName jp.getSignature().getName();if (ex instanceof ArithmeticException) {LOG.error(【异常通知】 methodName 方法算术异常ArithmeticException ex.getMessage());} else {LOG.error(【异常通知】 methodName 方法异常 ex.getMessage());}}/*** 环绕通知* 1、Around 的 value 属性绑定通知的切入点表达式。可以关联切入点声明也可以直接设置切入点表达式* 2、Object ProceedingJoinPoint.proceed(Object[] args) 方法继续下一个通知或目标方法调用返回处理结果如果目标方法发生异常则 proceed 会抛异常.* 3、假如目标方法是控制层接口则本方法的异常捕获与否都不会影响目标方法的事务回滚* 4、假如目标方法是控制层接口本方法 try-catch 了异常后没有继续往外抛则全局异常处理 RestControllerAdvice 中不会再触发** param joinPoint* return* throws Throwable*/Around(value aspectPointcut())public Object handleControllerMethod(ProceedingJoinPoint joinPoint) throws Throwable {this.checkRequestParam(joinPoint);StopWatch stopWatch StopWatch.createStarted();LOG.debug(【环绕通知】执行接口开始方法{}参数{} , joinPoint.getSignature(), Arrays.asList(joinPoint.getArgs()).toString());//继续下一个通知或目标方法调用返回处理结果如果目标方法发生异常则 proceed 会抛异常.//如果在调用目标方法或者下一个切面通知前抛出异常则不会再继续往后走.Object proceed joinPoint.proceed(joinPoint.getArgs());stopWatch.stop();long watchTime stopWatch.getTime();LOG.debug(【环绕通知】执行接口结束方法{}, 返回值{},耗时{} (毫秒), joinPoint.getSignature(), proceed, watchTime);return proceed;}/*** 参数校验防止 SQL 注入** param joinPoint*/private void checkRequestParam(ProceedingJoinPoint joinPoint) {Object[] args joinPoint.getArgs();if (args null || args.length 0) {return;}String params Arrays.toString(joinPoint.getArgs()).toUpperCase();String[] keywords {DELETE , UPDATE , SELECT , INSERT , SET , SUBSTR(, COUNT(, DROP ,TRUNCATE , INTO , DECLARE , EXEC , EXECUTE , AND , OR , --};for (String keyword : keywords) {if (params.contains(keyword)) {LOG.warn(参数存在SQL注入风险其中包含非法字符 {}., keyword);throw new RuntimeException(参数存在SQL注入风险params params);}}}
}如上所示在不修改原来业务层代码的基础上就可以使用 AOP 功能在目标方法执行前后或者异常时都能捕获然后执行。
execution 切点表达式 拦截指定类的方法
1、Pointcut 切入点声明注解以及所有的通知注解都可以通过 value 属性或者 pointcut 属性指定切入点表达式。 2、切入点表达式通过 execution 函数匹配连接点语法execution([方法修饰符] 返回类型 包名.类名.方法名(参数类型) [异常类型])
访问修饰符可以省略返回值类型、包名、类名、方法名可以使用星号*代表任意包名与类名之间一个点.代表当前包下的类两个点…表示当前包及其子包下的类;参数列表可以使用两个点…表示任意个数任意类型的参数列表; 3、切入点表达式的写法比较灵活比如* 号表示任意一个… 表示任意多个还可以使用 、||、! 进行逻辑运算不过实际开发中通常用不到那么多花里胡哨的掌握以下几种就基本够用了。 4、特别注意当明确指定了切入的类时类必须存在否则启动报错此时可以在类名前后加上*号表示模糊包含。 切入点表达式常用举例
标题内容execution(* com.wmx.aspect.EmpServiceImpl.findEmpById(Integer))匹配 com.wmx.aspect.EmpService 类中的 findEmpById 方法且带有一个 Integer 类型参数。execution(* com.wmx.aspect.EmpServiceImpl.findEmpById(*))匹配 com.wmx.aspect.EmpService 类中的 findEmpById 方法且带有一个任意类型参数。execution(* com.wmx.aspect.EmpServiceImpl.findEmpById(…))匹配 com.wmx.aspect.EmpService 类中的 findEmpById 方法参数不限。execution(* grp.basic3.se.service.SEBasAgencyService3.editAgencyInfo(…)) || execution(*grp.basic3.se.service.SEBasAgencyService3.adjustAgencyInfo(…))匹配 editAgencyInfo 方法或者 adjustAgencyInfo 方法Pointcut(“(execution(* grp.basic3…Controller.(…)) !execution( grp.basic3.BaseExceptionController*.*(…)))”)匹配 grp.basic3包及其子包下面名称包含 ‘Controller’ 类中的全部方法但是排除掉其中的以 BaseExceptionController 开头的类。execution(* com.wmx.aspect.EmpService.*(…))匹配 com.wmx.aspect.EmpService 类中的任意方法execution(* com.wmx.aspect..(…))匹配 com.wmx.aspect 包(不含子包)下任意类中的任意方法execution(* com.wmx.aspect….(…))匹配 com.wmx.aspect 包及其子包下任意类中的任意方法execution(* grp.pm…Controller.(…))匹配 grp.pm 包下任意子孙包中以 “Controller” 结尾的类中的所有方法* com.wmx…Controller.*(…))com.wmx 包及其子包下面类名包含’Controller’的任意类中的任意方法* com.wmx..controller..*(…))第一二层包名为 com.wmx 第三层包名任意第4层包名为 controller 下面的任意类中的任意方法
Pointcut(“annotation(xx)”) 拦截拥有指定注解的方法 /*** Pointcut 切入点声明即切入到哪些目标方法。* execution可以用于指定具体类中的具体方法* annotation匹配拥有指定注解的方法; 只匹配实现类中有注解的方法不会匹配接口中的注解方法; 如果注解是在类上,而不是方法上,并不会匹配类中的全部方法.* 用于被下面的通知注解引用这样通知注解只需要关联此切入点声明即可无需再重复写切入点表达式* annotation 中的路径表示拦截特定注解*/Pointcut(annotation(com.wmx.annotation.RedisLock))public void redisLockPC() {}环绕通知 实现开关目标方法
1、比如某个方法只有管理员才有权限执行而普通用户是没有权限 2、比如不符合条件的时候需要终止(跳过)目标方法的执行 3、比如一个组件(Component)专门用于做校验里面的方法是否校验可以配置在数据库中当配置为启用时则继续校验否则不校验。 /*** 环绕通知* 1、Around 的 value 属性绑定通知的切入点表达式。可以关联切入点声明也可以直接设置切入点表达式* 2、Object ProceedingJoinPoint.proceed(Object[] args) 方法继续下一个通知或目标方法调用返回处理结果如果目标方法发生异常则 proceed() 会抛异常.* 3、假如目标方法是控制层接口则本方法的异常捕获与否都不会影响业务层方法的事务回滚* 4、假如目标方法是控制层接口本方法 try-catch 了异常后没有继续往外抛则全局异常处理 RestControllerAdvice 中不会再触发*/Around(value aspectPointcut())public Object around(ProceedingJoinPoint joinPoint) throws Throwable {Signature signature joinPoint.getSignature();Object target joinPoint.getTarget();System.out.println(环绕通知 signature);System.out.println(环绕通知 target);// 是否继续校验boolean validation true;if (validation) {// 校验通过后执行目标方法// 继续下一个切面通知或目标方法调用返回处理结果如果目标方法发生异常则 proceed 会抛异常.// 如果在调用目标方法或者下一个切面通知前抛出异常则不会再继续往后走return joinPoint.proceed(joinPoint.getArgs());} else {// 校验未通过时不继续往后走直接返回。// 可以返回提示信息但是必须保证返回的参数类型与目标方法的返回值类型一致否则类型转换异常。// 也可以直接抛异常。return null;}}案例1自定义注解切面实现统一日志处理
1.自定义日志注解
/*** 自定义操作日志注解*/
Target(ElementType.METHOD)
Retention(RetentionPolicy.RUNTIME)
Documented
public interface OptLog {/*** 业务* return*/String business();/*** 操作类型增删改查* return*/OptType optType();
}2声明日志切面组件
import com.alibaba.fastjson.JSONObject;
import com.example.demo.annotation.OptLog;
import com.example.demo.annotation.OptType;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;Aspect
Component
public class OptLogAspect {private static final Logger LOG LoggerFactory.getLogger(OptLogAspect.class);/*** 声明切入点凡是使用该注解都经过拦截*/Pointcut(annotation(com.example.demo.annotation.OptLog))public void OptLog() {}Before(OptLog())public void doOptLogBefore(JoinPoint proceedingJoinPoint) {LOG.info(前置通知, 在方法执行之前执行...);}After(OptLog())public void doOptLogAfter(JoinPoint proceedingJoinPoint) {LOG.info(后置通知, 在方法执行之后执行...);}AfterReturning(OptLog())public void doOptLogAfterReturning(JoinPoint proceedingJoinPoint) {LOG.info(返回通知, 在方法返回结果之后执行...);}AfterThrowing(OptLog())public void doOptLogAfterThrowing(JoinPoint proceedingJoinPoint) {LOG.info(异常通知, 在方法抛出异常之后执行...);}/*** 设置环绕通知,围绕着方法执行** param proceedingJoinPoint* return*/Around(OptLog())public Object optLogAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {Method method ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();if (method null) {return null;}// 获取方法名称String methodName proceedingJoinPoint.getSignature().getName();LocalVariableTableParameterNameDiscoverer discoverer new LocalVariableTableParameterNameDiscoverer();// 请求参数名称String[] parameterNames discoverer.getParameterNames(method);// 请求参数值Object[] paramValues proceedingJoinPoint.getArgs();OptLog optLog method.getAnnotation(OptLog.class);this.handle(optLog.optType(), optLog.business(), methodName, parameterNames, paramValues);return proceedingJoinPoint.proceed();}/*** 日志处理** param optType* param business* param methodName* param parameterNames* param paramValues*/public void handle(OptType optType, String business, String methodName, String[] parameterNames, Object[] paramValues) {JSONObject jsonObject new JSONObject();if (parameterNames ! null parameterNames.length 0) {for (int i 0; i parameterNames.length; i) {jsonObject.put(parameterNames[i], paramValues[i]);}}LOG.info(optType: optType ,business: business , methodName: methodName , params: jsonObject);}}3.控制层运行结果
RestController
RequestMapping(/user/)
public class UserController {OptLog(optType OptType.CREATE,business 用户信息)RequestMapping(create)public String createUser(String userName,int age,String address) {System.out.println(方法执行中...);return success;}
}4.运行结果
15:32:49.494 [http-nio-8080-exec-2] INFO c.e.d.a.OptLogAspect - [handle,91] - optType:CREATE,business:用户信息, methodName:createUser, params:{address:广州市,userName:阿杰,age:18}
15:32:49.494 [http-nio-8080-exec-2] INFO c.e.d.a.OptLogAspect - [doOptLogBefore,32] - 前置通知, 在方法执行之前执行...
方法执行中...
15:32:49.495 [http-nio-8080-exec-2] INFO c.e.d.a.OptLogAspect - [doOptLogAfterReturning,42] - 返回通知, 在方法返回结果之后执行...
15:32:49.495 [http-nio-8080-exec-2] INFO c.e.d.a.OptLogAspect - [doOptLogAfter,37] - 后置通知, 在方法执行之后执行...案例2自定义注解与切面类
1、创建自定义注解
import java.lang.annotation.*;Target({ ElementType.METHOD, ElementType.TYPE })
Retention(RetentionPolicy.RUNTIME)
Documented
public interface TestAnnotation {String name() default 默认值; // 允许注解有参数String age() default 15; // 允许多个参数
}2、创建一个类定义方法后使用自定义注解
import com.yh.annotation.OperateLogAnnotation;
import com.yh.annotation.TestAnnotation;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
RestController
RequestMapping(/test)
public class TestAOPController {RequestMapping(/show3)ResponseBodyTestAnnotation(name 我把值传进去, age 24) // 加上自定义注解public String getById() { return hello;}}3、定义切面类进行扫描自定义注解并对切入点进行处理
import com.yh.annotation.TestAnnotation;
import com.yh.annotation.TestAnnotation;
//import javassist.bytecode.SignatureAttribute;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;Aspect // FOR AOP
Order(-99) // 控制多个Aspect的执行顺序越小越先执行, 当然也可以不写这注解, 对于写和不写order的两个切面, 有order的优先于无order的执行; 都有order时, 越小越执先执行
Component
public class TestAspect {// 可以参考若依的自定义注解。自定义注解一般使用annotation// Before可以有两种写法, annotation(形参test)Before(annotation(test))// 拦截被TestAnnotation注解的方法如果你需要拦截指定package指定规则名称的方法可以使用表达式execution(...)public void beforeTest(JoinPoint point, TestAnnotation test) throws Throwable {System.out.println(beforeTest: test.name()); // 直接获取注解参数//test.name()和test.age()}After(annotation(test))public void afterTest(JoinPoint point, TestAnnotation test) {System.out.println(afterTest: test.name()); // 直接获取注解参数}// 可以控制方法运行, 同时修改入参和返回值Around(annotation(test)) // test表示aroundTest方法中的test入参public Object aroundTest(ProceedingJoinPoint pjp, TestAnnotation test) throws Throwable {System.out.println(aroundTest: test.value());// 获取入参并修改Object[] args pjp.getArgs();args[0] ;// 传入修改后的参数, 并继续执行Object res pjp.proceed(args);// 修改返回值return res.toString() res.toString();}/* // 指定切面Pointcut(annotation(com.yh.annotation.TestAnnotation))public void annotationPointCut() {}// Before可以有两者写法, annotation(函数名annotationPointCut)Before(annotationPointCut())public void before(JoinPoint joinPoint) {MethodSignature sign (MethodSignature) joinPoint.getSignature();Method method sign.getMethod();TestAnnotation annotation method.getAnnotation(TestAnnotation.class); // 获取指定注解实例System.out.println(打印 annotation.name() 前置日志1); // 获取注解实例的参数}After(annotationPointCut())public void afterTTT(JoinPoint point) {MethodSignature sign (MethodSignature) point.getSignature();Method method sign.getMethod();TestAnnotation annotation method.getAnnotation(TestAnnotation.class); // 获取指定注解实例System.out.println(打印自带参数 annotation.age() 后置日志1); // 获取注解实例的参数}
*/}
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/911502.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!