テキストを読んだ後、この表現EZを理解しましょう
最初に次の記事を読んでから、この部分を見てください
まず、エントリポイントの式について説明します。次に、次の記事を読みます。
@Pointcut("execution(public * com.skxx.vrxmty.modules.*.*.controller.*.*(..))")
public void before(){}
他のメソッドを呼び出すcontrolelrのメソッドを除いて、モジュールの下のすべてのコントローラーの下のすべてのメソッドを一致させます
最初にここを見てください----------------------テキスト----------------------- ----------------------------
以下は、https://blog.csdn.net/lmb55/article/details/82470388から複製されています。
1.サンプルアプリケーションシナリオ:ログを記録するために、すべてのWebリクエストの断面を作成します。
1.SpringBootのWebモジュールとAOP関連の依存関係をpomに導入します。
<!--10.springboot aop-->
<!-- start of aop-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- end of aop-->
その中で、
cglibパッケージは動的プロキシ、クラスベースのプロキシに使用されます
。aspectjrtとaspectjweaverはaspectjに関連する
パッケージであり、アスペクトプログラミングをサポートするために使用されます 。aspectjrtパッケージはaspectjのランタイムパッケージです
。aspectjweaverはaspectjのウィービングパッケージです。
2.単純なWeb要求エントリを実装します(nameパラメータを渡して「helloxxx」を返す機能を実装します)。
注:AOP依存関係パッケージの導入が完了した後、一般的に言えば、他の構成は必要ありません。Springアノテーション設定メソッドを使用したことがある人は、それを有効にするためにプログラムのメインクラスに@EnableAspectJAutoProxyを追加する必要があるかどうかを尋ねますが、実際には必要ありません。
AOPのデフォルトの構成プロパティでは、spring.aop.autoプロパティがデフォルトで有効になっているため、AOP依存関係が導入されている限り、@ EnableAspectJAutoProxyがデフォルトで追加されています。
3.アスペクトクラスを定義して、Webレイヤーのログアスペクトを実現します。
クラスをアスペクトクラスに変換するには、2つの手順が必要です
。①クラスの@Componentアノテーションを使用して、アスペクトクラスをIOCコンテナに追加し
ます。②クラスの@Aspectアノテーションを使用して、アスペクトクラスにします。
package com.example.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
/**
* Created by lmb on 2018/9/5.
*/
@Aspect
@Component
public class WebLogAcpect {
private Logger logger = LoggerFactory.getLogger(WebLogAcpect.class);
/**
* 定义切入点,切入点为com.example.aop下的所有函数
*/
@Pointcut("execution(public * com.example.aop..*.*(..))")
public void webLog(){}
/**
* 前置通知:在连接点之前执行的通知
* @param joinPoint
* @throws Throwable
*/
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 记录下请求内容
logger.info("URL : " + request.getRequestURL().toString());
logger.info("HTTP_METHOD : " + request.getMethod());
logger.info("IP : " + request.getRemoteAddr());
logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));
}
@AfterReturning(returning = "ret",pointcut = "webLog()")
public void doAfterReturning(Object ret) throws Throwable {
// 处理完请求,返回内容
logger.info("RESPONSE : " + ret);
}
}
上記のアスペクトクラスは、@ Pointcutで定義されたエントリポイントを使用して、com.example.aopパッケージの下のすべての関数の人をカットし、@ Beforeを介してエントリポイントの事前通知を実現し、@ AfterReturningを介してリクエストによって返されたオブジェクトを記録します。
訪問のhttp:// localhostを:8004 /ハロー名= LMBをするには?次のようにコンソール出力が得られます。
詳細なコードについては、私のGithubを参照してください:SpringBootはAOPを統合します
2.AOPでサポートされる通知1.事前通知@Before:接続ポイントの前に実行される通知。例外がスローされない限り、この通知は接続ポイントの前の実行プロセスを防ぐことはできません。
/**
* 前置通知,方法调用前被调用
* @param joinPoint/null
*/
@Before(value = POINT_CUT)
public void before(JoinPoint joinPoint){
logger.info("前置通知");
//获取目标方法的参数信息
Object[] obj = joinPoint.getArgs();
//AOP代理类的信息
joinPoint.getThis();
//代理的目标对象
joinPoint.getTarget();
//用的最多 通知的签名
Signature signature = joinPoint.getSignature();
//代理的是哪一个方法
logger.info("代理的是哪一个方法"+signature.getName());
//AOP代理类的名字
logger.info("AOP代理类的名字"+signature.getDeclaringTypeName());
//AOP代理类的类(class)信息
signature.getDeclaringType();
//获取RequestAttributes
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
//从获取RequestAttributes中获取HttpServletRequest的信息
HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
//如果要获取Session信息的话,可以这样写:
//HttpSession session = (HttpSession) requestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION);
//获取请求参数
Enumeration<String> enumeration = request.getParameterNames();
Map<String,String> parameterMap = Maps.newHashMap();
while (enumeration.hasMoreElements()){
String parameter = enumeration.nextElement();
parameterMap.put(parameter,request.getParameter(parameter));
}
String str = JSON.toJSONString(parameterMap);
if(obj.length > 0) {
logger.info("请求的参数信息为:"+str);
}
}
注:ここでは、JoinPointとRequestContextHolderが使用されます。
1)通知の署名情報(ターゲットメソッド名、ターゲットメソッドパラメータ情報など
)はJoinPointを介して取得できます 。2)リクエスト情報とセッション情報はRequestContextHolderを介して取得できます。
2.事後通知@AfterReturning:接続ポイントの後に実行される通知は、通常、一致するメソッドが戻ったときに実行されます(戻り値は事後通知にバインドできます)。
/**
* 后置返回通知
* 这里需要注意的是:
* 如果参数中的第一个参数为JoinPoint,则第二个参数为返回值的信息
* 如果参数中的第一个参数不为JoinPoint,则第一个参数为returning中对应的参数
* returning:限定了只有目标方法返回值与通知方法相应参数类型时才能执行后置返回通知,否则不执行,
* 对于returning对应的通知方法参数为Object类型将匹配任何目标返回值
* @param joinPoint
* @param keys
*/
@AfterReturning(value = POINT_CUT,returning = "keys")
public void doAfterReturningAdvice1(JoinPoint joinPoint,Object keys){
logger.info("第一个后置返回通知的返回值:"+keys);
}
@AfterReturning(value = POINT_CUT,returning = "keys",argNames = "keys")
public void doAfterReturningAdvice2(String keys){
logger.info("第二个后置返回通知的返回值:"+keys);
}
3.例外通知の投稿@AfterThrowing:例外がスローされてメソッドが終了したときに実行される通知。
/**
* 后置异常通知
* 定义一个名字,该名字用于匹配通知实现方法的一个参数名,当目标方法抛出异常返回后,将把目标方法抛出的异常传给通知方法;
* throwing:限定了只有目标方法抛出的异常与通知方法相应参数异常类型时才能执行后置异常通知,否则不执行,
* 对于throwing对应的通知方法参数为Throwable类型将匹配任何异常。
* @param joinPoint
* @param exception
*/
@AfterThrowing(value = POINT_CUT,throwing = "exception")
public void doAfterThrowingAdvice(JoinPoint joinPoint,Throwable exception){
//目标方法名:
logger.info(joinPoint.getSignature().getName());
if(exception instanceof NullPointerException){
logger.info("发生了空指针异常!!!!!");
}
}
4.最終通知@After:接続ポイントが終了したときに実行される通知(通常の復帰または異常な終了に関係なく)。
/**
* 后置最终通知(目标方法只要执行完了就会执行后置通知方法)
* @param joinPoint
*/
@After(value = POINT_CUT)
public void doAfterAdvice(JoinPoint joinPoint){
logger.info("后置最终通知执行了!!!!");
}
5.サラウンド通知@Around:メソッド呼び出しなど、接続ポイントを囲む通知。これは最も強力なタイプの通知です。周囲の通知は、メソッド呼び出しの前後にカスタム動作を完了することができます。また、接続ポイントの実行を続行するか、独自の戻り値を直接返すか、例外をスローして実行を終了するかを選択します。
サラウンド通知は最も強力で最も面倒です。これはラッピングの方法です。特定のメソッドはエージェントを介してアスペクトに渡されます。アスペクトでは、メソッドを実行するかどうか、および実行するメソッドの数を選択できます。サラウンド通知は、タイプProceedingJoinPointのオブジェクトを使用してターゲットオブジェクトを管理するため、この通知の最初のパラメーターはタイプProceedingJoinPointである必要があります。通知本文でProceedingJoinPointのproceed()メソッドを呼び出すと、バックグラウンド接続ポイントメソッドが実行されます。proceed()メソッドを呼び出して、Object []オブジェクトを渡すこともできます。配列内の値は、メソッドの実行時に入力パラメーターとして使用されます。
/**
* 环绕通知:
* 环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。
* 环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型
*/
@Around(value = POINT_CUT)
public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
logger.info("环绕通知的目标方法名:"+proceedingJoinPoint.getSignature().getName());
try {
Object obj = proceedingJoinPoint.proceed();
return obj;
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return null;
}
6.アスペクトを定義するときに、ターゲットオブジェクトの特定のパラメータをアスペクトで使用する必要がある場合があります。アスペクトはどのようにしてターゲットオブジェクトのパラメータを取得できますか?argsを使用してバインドできます。args式でタイプ名を使用する必要がある場所でパラメーター名を使用すると、通知の実行時にオブジェクトのパラメーター値が渡されます。
@Before("execution(* findById*(..)) &&" + "args(id,..)")
public void twiceAsOld1(Long id){
System.err.println ("切面before执行了。。。。id==" + id);
}
注:どの通知方法でも、最初のパラメーターをorg.aspectj.lang.JoinPointタイプとして定義できます(サラウンド通知では、最初のパラメーターを、JoinPointのサブクラスであるProceedingJoinPointタイプとして定義する必要があります)。JoinPointインターフェイスは、getArgs()(メソッドパラメータを返す)、getThis()(プロキシオブジェクトを返す)、getTarget()(ターゲットを返す)、getSignature()(通知されるメソッドに関する情報を返す)などの一連の便利なメソッドを提供します。そしてtoString()(通知されているメソッドに関する有用な情報を出力します)。
第3に、ポイントカット式
は、署名名とパラメータを含める必要がある場合のエントリポイントと、Execution(public * com.example.aop..。(..))などのエントリポイント式を定義しました。
エントリポイント式の形式:実行([可視性]リターンタイプ[宣言タイプ]。メソッド名(パラメータ)[例外])
ここで、[]はオプションであり、その他もワイルドカードの使用をサポートします:
1)* :すべての文字に一致
2)..:通常、複数のパッケージ、複数のパラメーターに一致するために使用されます
3)+:クラスとそのサブクラスを
表します4)演算子は次のとおりです:&&、||、!
ポイントカット式のキーワードの使用例:
1)実行:部分式を照合するために使用されます。
// com.cjm.modelマッチングパケットとそのサブパッケージすべてのクラスのすべてのメソッド、リターンタイプ、任意、任意のメソッドパラメータ
@Pointcut( "Execution(com.cjm.model .. * 。(..))")
public void before(){}
2)内:接続ポイントが配置されているJavaクラスまたはパッケージを照合するために使用されます。
// Personクラスのすべてのメソッドを一致させます
@Pointcut(“ within(com.cjm.model.Person)”)
public void before(){}
//com.cjmパッケージとそのサブパッケージのすべてのクラスのすべてのメソッドを一致させます
@Pointcut(“ within(com.cjm .. *)”)
public void before(){}
3)これ:通知メソッドへのプロキシオブジェクト参照を渡すために使用されます。
@Before( "before()&& this(proxy)")
public void beforeAdvide(JoinPoint point、Object proxy){
//処理ロジック
}
4)ターゲット:通知メソッドへのターゲットオブジェクト参照を渡すために使用されます。
@Before( "before()&& target(target)
public void beforeAdvide(JoinPoint point、Object proxy){
//処理ロジック
}
5)args:通知メソッドにパラメーターを渡すために使用されます。
@Before( "before()&& args(age、username)")
public void beforeAdvide(JoinPoint point、int age、String username){
//処理ロジック
}
6)@within:クラスレベルでパラメータによって決定された注釈を使用するクラスを照合するために使用され、そのすべてのメソッドが照合されます。
@Pointcut(“ @ within(com.cjm.annotation.AdviceAnnotation)”)-@ AdviceAnnotation
によって注釈が付けられたすべてのクラスは、
public void before(){}と一致し ます。
7)@target:関数は@withinに似ていますが、注釈インターフェイスの保持ポリシーをRUNTIMEとして指定する必要があります。
@Pointcut(“ @ target(com.cjm.annotation.AdviceAnnotation)”)
public void before(){}
8)@args:接続ポイントで渡されるオブジェクトに対応するJavaクラスは、@ argsで指定された注釈でマークする必要があります。
@Before(“ @ args(com.cjm.annotation.AdviceAnnotation)”)
public void beforeAdvide(JoinPoint point){
//処理ロジック
}
9)@annotation:パラメーターで指定されたAnnotationアノテーションを照合する方法。つまり、指定された注釈でマークされたすべてのメソッドが一致します。
@Pointcut(“ @ annotation(com.cjm.annotation.AdviceAnnotation)”)
public void before(){}
10)Bean:接続ポイントが配置されているBeanを管理対象Beanの名前で修飾します。このキーワードはSpring2.5で新しく追加されました。
@Pointcut(“ bean(person)”)
public void before(){}