春の研究ノート-04AOP

1.エージェンシーモデル

プロキシモードの適用可能なシナリオは、一部のエンティティクラスのメソッドを拡張したい場合がありますが、エンティティクラスの対応するメソッド定義を変更するのは不便です。現時点では、拡張された部分をプロキシに書き込むことができます。モードを介して。

1)静的プロキシ

携帯電話を販売したいとします。そのため、エンティティクラスを次のように記述できます。

Person.java

public class Person {
    public void sellPhone(){
        System.out.println("卖手机");
    }
}

テスト:

public class TestStaticProxy {
    public static void main(String[] args) {
        Person person = new Person();
        person.sellPhone();
    }
}

携帯電話を販売する前に次の家を探す必要があることがわかりましたが、携帯電話の販売方法を定義しました。現時点では、静的プロキシを使用して拡張できます。

1)インターフェース定義仕様を使用する

public interface ISellPhone {
    public void sellPhone();
}

2)定義方法

public class Person implements ISellPhone {
    public void sellPhone(){
        System.out.println("卖手机");
    }
}

3)エージェントを介してメソッドを強化する

public class SellPhoneProxy implements ISellPhone {
    private Person person = new Person();
    public void sellPhone() {
        System.out.println("寻找买家");
        person.sellPhone();
    }
}

4)プロキシを直接使用するだけです

public class TestStaticProxy {
    public static void main(String[] args) {
        ISellPhone iSellPhone = new SellPhoneProxy();
        iSellPhone.sellPhone();
    }
}

2)JDK動的プロキシ

静的エージェントの欠点は、ほとんどすべてのビジネスで、拡張メソッドがほぼ同じであっても、エージェントを追加して実行する必要があることです。動的エージェントを使用して、多くのエージェントクラスを記述せずにメソッドを直接拡張することもできます。 。

携帯電話のアフターサービスを追加します。

public interface ISellPhone {
    public void sellPhone();
    public void serviceAfterSelling();
}

Person.java

public class Person implements ISellPhone {
    public void sellPhone(){
        System.out.println("卖手机");
    }

    public void serviceAfterSelling() {
        System.out.println("售后服务");
    }
}

JDKの動的プロキシインターフェースを使用します。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class SellPhoneProxy implements InvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("调用前");
        Object result = method.invoke(new Person(),args);
        System.out.println("调用后");
        return result;
    }
}

パッケージ名を見ると、動的プロキシがリフレクションメカニズムによって実装されていることもわかります。Objectプロパティは、プロキシされるオブジェクトを格納するために使用されます。

テスト:

import java.lang.reflect.Proxy;

public class TestDynamicProxy {
    public static void main(String[] args) {
        Person person = new Person();
        ISellPhone iSellPhone = (ISellPhone) Proxy.newProxyInstance(ISellPhone.class.getClassLoader(),new Class[]{ISellPhone.class},new SellPhoneProxy());
        iSellPhone.serviceAfterSelling();
    }
}

[注]:静的プロキシであろうと動的プロキシであろうと、プロキシクラスとプロキシクラスは共通のインターフェースを持っている必要があります。

3)CGLib動的エージェント

もちろん、継承メソッドを使用して、プロキシクラスにプロキシクラスを継承させ、メソッドを上書きすることで、対応するプロキシメソッド(cglib)を拡張することもできます。

A.依存関係をインポートする

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2.2</version>
        </dependency>

B.cglib動的プロキシを使用する

import java.lang.reflect.Method;

public class Client {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Person.class);
        enhancer.setCallback(new InvocationHandler() {
            public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                System.out.println("执行前");
                Object invoke = method.invoke(new Person(),objects);
                System.out.println("执行后");
                return invoke;
            }
        });
        Person personAgent = (Person) enhancer.create();
        personAgent.sellPhone();
        System.out.println("============");
        personAgent.serviceAfterSelling();
    }
}

3.実行結果

2.AOPアスペクト指向プログラミング

AOP(アスペクト指向プログラミング)アスペクト指向プログラミングは、主に、ログ、トランザクション、アクセス許可など、プログラム開発のプロセスにおけるいくつかのシステムレベルの問題を解決するために使用されます。

SpringのAOPは、実際にはメソッドのプロキシを拡張したものです。上記の分析の結果、インターフェイスベースのプロキシがJDKを使用していることがわかりました。インターフェイスがない場合、プロキシはcglibによって使用されます。

最初にOOP(オブジェクト指向プログラミング)オブジェクト指向プログラミングを見てみましょう。すべてのものはオブジェクトと見なされます。JavaはOOP言語です。クラスの拡張は継承によって実現されます。Javaで構築されたシステムは垂直関係を示します。

ただし、継承には欠点もあります。継承によって親クラスメソッドの拡張を完了することはできますが、クラスを拡張するたびに継承が必要になるため、システム全体でクラスが多すぎて、特にすべてのユーザーにとって保守が容易ではありません。ログのようなタイプ。メソッドと拡張メソッドがほぼ同じである場合、継承によって実装すると、ワークロードが大きすぎます。

Sringでは、継承せずにメソッドに断面を挿入することで強化される「横断的」技術を定義し、システム全体が水平関係を示し、最下層はクロスを持つエージェントによって実現されます。 -セクション。効果は次のとおりです。

A. AOPに関連する用語:

1.通知、拡張処理(アドバイス)

機能拡張を実装するために記述されたコードまたはロジックは、上記のセキュリティ、トランザクション、ログなどです。

2.接続ポイント(JoinPoint)

Springでは、各メソッドの前面、背面、サラウンド(前後の両方)を含む通知を挿入できます。例外がスローされると、Springはメソッドの接続ポイントのみをサポートします。

3.ポイントカット

つまり、実際に使用する接続ポイント、つまり実際に通知を切断する接続ポイントを定義します。

4.アスペクト(アスペクト)

アスペクトは、通知とエントリポイントの組み合わせです。通知は、何をいつ実行するかを説明する責任があり、エントリポイントは、それを実行する場所を示します。これらは一緒になってアスペクトを構成します。

5.はじめに

導入により、ターゲットクラスにアスペクトを導入できます。

6.ターゲット

冒頭で述べた対象クラス、つまり通知対象は、実際のビジネスロジックを持っており、切り込みに注意を払う必要はなく、独自のビジネスロジックに注意を払うだけでよい。

7.織り

アスペクトをターゲットオブジェクトに適用し(通知を連絡先にカットし)、それに応じて新しいプロキシオブジェクトを構築するプロセス。

B. AOP(注釈)の使用を感じてください:

1.ヘッダーファイルを追加します

<?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/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd">
    
</beans>

2.織り込まれた依存関係をインポートします

        <!--引入织入相关的依赖-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>

3.拡張する2つのメソッドを追加します

AdminService.java

package com.zt.Service;

import org.springframework.stereotype.Service;

@Service
public class AdminService {

    public Integer getAdmin(){
        System.out.println("-----getAdmin-----");
        return 100;
    }

}

UserService.java

package com.zt.Service;

import org.springframework.stereotype.Service;

@Service
public class UserService {

    public void getUser(){
        System.out.println("-----getUser-----");
    }

}

4.アスペクトクラスを作成します

package com.zt.Aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class LogAdvice {

    @Before("execution(* com.zt.Service.*.*(..))")
    public void before(){
        System.out.println("-----方法执行前 before -----");
    }

    @After("execution(* com.zt.Service.*.*(..))")
    public void after(){
        System.out.println("-----方法执行后 after -----");
    }

    @AfterReturning("execution(* com.zt.Service.*.*(..))")
    public void afterReturning(){
        System.out.println("-----方法执行返回后 afterReturning -----");
    }

    @Around("execution(* com.zt.Service.*.*(..))")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("-----环绕前-----");
        System.out.println("方法名:" + joinPoint.getSignature());
        Object proceed = joinPoint.proceed();
        System.out.println("-----环绕后-----");
        System.out.println(proceed);
    }

    @AfterThrowing("execution(* com.zt.Service.*.*(..))")
    public void afterThrow(){
        System.out.println("-----有异常-----");
    }

}

[注]:実行式は、拡張するメソッドを指定するために使用されます。

もちろん、拡張するメソッドを指定することもできます:execution(void com.zt.Service.UserService.getUser(..))

 

5.アスペクトクラスメソッドを使用するため、パッケージスキャンをオンにして、構成に使用されるBeanをSpringコンテナに管理させる必要があります。

<context:component-scan base-package="com.zt"/>

6.AOP自動プロキシの注釈サポートを有効にします

<aop:aspectj-autoproxy/>

7.構成クラスを使用してテストする

package com.zt.Config;

import com.zt.Service.AdminService;
import com.zt.Service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application.xml")
public class JavaConfig {

    @Autowired
    private UserService userService;

    @Autowired
    private AdminService adminService;

    @Test
    public void TestAop(){
        userService.getUser();
        System.out.println("-------------------------------------------");
        adminService.getAdmin();
    }

}

[注]:Springはコンテナー内のBeanメソッドのみを拡張でき、コンテナーの外部でインスタンス化されたオブジェクトは拡張されません。

C. AOPの使用(構成ファイル方式)

1.アスペクトクラスを定義します

package com.zt.Aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;

@Component
public class XMLAdvice {

    public void before(){
        System.out.println("-----方法执行前 before -----");
    }

    public void after(){
        System.out.println("-----方法执行后 after -----");
    }

    public void afterReturning(){
        System.out.println("-----方法执行返回后 afterReturning -----");
    }

    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("-----环绕前-----");
        System.out.println("方法名:" + joinPoint.getSignature());
        Object proceed = joinPoint.proceed();
        System.out.println("-----环绕后-----");
        System.out.println("方法执行结果的返回值:" + proceed);
    }

    public void afterThrow(){
        System.out.println("-----有异常-----");
    }
    
}

2.構成ファイルで、ポイントカットを使用し、織り込む側面を参照します

    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* com.zt.Service.*.*(..))"/>
        <aop:aspect ref="XMLAdvice">
            <aop:before pointcut-ref="pointcut" method="before"/>
            <aop:after pointcut-ref="pointcut" method="after"/>
        </aop:aspect>
    </aop:config>

<aop:pointcut>:ポイントカットを定義するために使用されます。

<aop:aspect>:アスペクトクラスを導入するために使用されます。

<aop:before>:実行前と同じように実行モードを定義します。ポイントカットを導入するには、refを使用して既存のポイントカットを参照するか(pointcut-ref)、実行式を直接使用してポイントカットを定義します。methodは、通知が送信されるアスペクトのどのメソッドを指定するために使用され、他の実行メソッドも同様です。


        <aop:aspect ref="XMLAdvice">
            <aop:before pointcut="execution(* com.zt.Service.*.*(..))" method="before"/>
            <aop:after pointcut="execution(* com.zt.Service.*.*(..))" method="after"/>
        </aop:aspect>

D. AOPの使用(特定のインターフェースを実装することにより、通知を直接使用する)

LogBefore.java

package com.zt.Aop;

import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component
public class LogBefore implements MethodBeforeAdvice {
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("-----before------" + method.getName() + "-----");
    }
}

LogAfter.java

package com.zt.Aop;

import org.springframework.aop.AfterReturningAdvice;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component
public class LogAfter implements AfterReturningAdvice {
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("-----after-----" + method.getName() + "------");
    }
}

構成ファイル内の参照:

    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* com.zt.Service.*.*(..))"/>
        <aop:advisor advice-ref="logBefore" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="logAfter" pointcut-ref="pointcut"/>
    </aop:config>

[注]:@ Componentアノテーションを使用して、SpringがこのBeanを自動的にインスタンス化および管理するようにします。デフォルト名は小文字で、その他は変更されません(キャメルケースの命名)。もちろん、読みやすくするために、注入されたBeanIDを指定することもできます。

LogBefore.java

package com.zt.Aop;

import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component("Before")
public class LogBefore implements MethodBeforeAdvice {
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("-----before------" + method.getName() + "-----");
    }
}

LogAfter.java

package com.zt.Aop;

import org.springframework.aop.AfterReturningAdvice;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component("After")
public class LogAfter implements AfterReturningAdvice {
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("-----after-----" + method.getName() + "------");
    }
}

構成ファイル内の参照:

    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* com.zt.Service.*.*(..))"/>
        <aop:advisor advice-ref="Before" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="After" pointcut-ref="pointcut"/>
    </aop:config>

 

 

おすすめ

転載: blog.csdn.net/qq_39304630/article/details/112392223