代理模式及Spring AOP

代理模式及Spring AOP

代理模式:爲其他對象提供一種代理以控制對這個對象的訪問。

在這裏插入圖片描述

Subject:定義RealSubject和Proxy 的共用接口

RealSubject:定義Proxy所代理的實體

Proxy:保存一個指向真實實體的引用,並提供和真實實體相同的接口

Java實現:

    package com.lbl.pojo;
    
    public interface Subject {
        void talk();
        void run();
        void jump();
        void fly();
    }
    package com.lbl.pojo;
    
    public class RealSubject implements Subject {
        public void talk() {
            System.out.println("talk");
        }
    
        public void run() {
            System.out.println("run");
        }
    
        public void jump() {
            System.out.println("jump");
        }
    
        public void fly() {
            System.out.println("fly");
        }
    }
    package com.lbl.pojo;
    
    public class Proxy implements Subject{
        private Subject subject;
    
        public Proxy() {
            this.subject = new RealSubject();
        }
    
        public void talk() {
            subject.talk();
        }
    
        public void run() {
            subject.run();
        }
    
        public void jump() {
            subject.jump();
        }
    
        public void fly() {
            subject.fly();
        }
    }
    package com.lbl.pojo;
    
    public class Client {
        public static void main(String[] args) {
            Proxy proxy = new Proxy();
            proxy.run();
            proxy.talk();
            proxy.fly();
            proxy.jump();
        }
    }

代理模式的應用

  • 遠程代理。爲一個對象在不同的地址空間提供局部代表。這樣可以隱藏一個對象存在於不同地址空間的事實
  • 虛擬代理。根據需要創建開銷很大的對象。通過它來存放實例化需要很長時間的真實對象。如網頁中一張張加載的圖片
  • 安全代理。用來控制真實對象的訪問權限
  • 智能指引。當調用真實的對象時,代理處理另外的一些事。

代理模式的分類

  • 靜態代理
  • 動態代理

靜態代理的缺點是每一個真實實體都會產生一個代理角色,開發效率變低。而動態代理可以解決這個問題。

動態代理

  • 動態代理的代理類是動態生成的,不是事先寫好的
  • 動態代理分爲兩類:基於接口的動態代理,基於類的動態代理
    • 基於接口:JDK動態代理
    • 基於類:cglib
    • Java字節碼:javasist

基於接口要了解的兩個類:Proxy,InvocationHandler

Proxy:提供了創建動態代理類和實例的靜態方法

InvocationHandler:由代理實例調用處理程序實現的接口。

每個代理實例都有一個關聯的調用處理程序,當在代理實例上調用方法時,方法調用將被編碼並分派到其調用處理程序的invoke方法。

實例

我們有一個“戰士(warrior)”接口,獸人戰士(OrkWarrior)實現了“戰士接口”,ProxyInvokeHandler類提供對獸人戰士的代理訪問,最後玩家(Player)通過代理操縱獸人戰士。

    package com.lbl.demo;
    
    public interface Warrior {
        void attack();
        void defend();
        void skill();
    }
    package com.lbl.demo;
    
    public class OrkWarrior implements Warrior {
        public void attack() {
            System.out.println("爲了部落!!");
        }
    
        public void defend() {
            System.out.println("獸人永不爲奴");
        }
    
        public void skill() {
            System.out.println("大風車!");
        }
    }
    package com.lbl.demo;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class ProxyInvokeHandler implements InvocationHandler {
        private Warrior warrior;
    
        //設置需要代理的接口
        public void setWarrior(Warrior warrior) {
            this.warrior = warrior;
        }
        //獲取代理
        public Object getProxy() {
            return Proxy.newProxyInstance(this.getClass().getClassLoader(),warrior.getClass().getInterfaces(),this);
        }
        //執行接口中的方法
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return method.invoke(warrior,args);
        }
    
    }
    package com.lbl.demo;
    
    public class Player {
        public static void main(String[] args) {
            Warrior warrior = new OrkWarrior();
            ProxyInvokeHandler pih = new ProxyInvokeHandler();
            pih.setWarrior(warrior);
            Warrior warrior1 = (Warrior) pih.getProxy();
            warrior1.attack();
            warrior1.defend();
            warrior1.skill();
        }
    }

Spring中AOP的實現

Spring API實現

導入maven依賴:

    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.4</version>
    </dependency>

在Spring配置文件中加入AOP相關約束

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

實例

有一個UserService接口,一個UserServiceImp的實現類;有兩個打印日誌的事務:BeforeLog(方法執行前打印),AfterLog(方法返回前打印)。現要將BeforeLog切入到UserService的add方法前,將AfterLog切入到UserService的delete方法後。

    package com.lbl.service;
    
    public interface UserService {
        void add();
        void delete();
        void update();
        void select();
    }
    package com.lbl.service;
    
    public class UserServiceImp implements UserService {
        public void add() {
            System.out.println("add an user");
        }
    
        public void delete() {
            System.out.println("delete an user");
        }
    
        public void update() {
            System.out.println("update an user");
        }
    
        public void select() {
            System.out.println("select an user");
        }
    }
    package com.lbl.log;
    
    
    import org.springframework.aop.MethodBeforeAdvice;
    
    import java.lang.reflect.Method;
    
    public class BeforeLog implements MethodBeforeAdvice {
        //MethodBeforeAdvice接口用於切入方法執行前
        public void before(Method method, Object[] objects, Object o) throws Throwable {
            System.out.println(o.getClass().getName() + "類的" + method.getName() + "方法被執行了");
        }
    }
    package com.lbl.log;
    
    import org.springframework.aop.AfterReturningAdvice;
    
    import java.lang.reflect.Method;
    
    public class AfterLog implements AfterReturningAdvice {
        //AfterReturningAdvice用於切入方法返回前
        public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
            System.out.println(method.getName() + "被執行了,返回了" + o);
        }
    }

Spring配置文件:

    <?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
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop
            https://www.springframework.org/schema/aop/spring-aop.xsd">
    
    
        <bean id="userService" class="com.lbl.service.UserServiceImp"/>
        <bean id="beforeLog" class="com.lbl.log.BeforeLog"/>
        <bean id="afterLog" class="com.lbl.log.AfterLog"/>
        <bean id="myaop" class="com.lbl.myaop.DiyAop"/>
    
        
        <aop:config>
    <!--        配置切入點-->
            <aop:pointcut id="addpointcut" expression="execution(* com.lbl.service.UserServiceImp.add())"/>
            <aop:pointcut id="deletepointcut" expression="execution(* com.lbl.service.UserServiceImp.delete())"/>
    <!--        配置執行環繞-->
            <aop:advisor advice-ref="beforeLog" pointcut-ref="addpointcut"/>
            <aop:advisor advice-ref="afterLog" pointcut-ref="deletepointcut"/>
        </aop:config>
    
    
    </beans>

Spring AOP 中pointcut expression表達式解析及配置_Java_敲碼農的小代碼   ;-CSDN博客

關於pointcut中的表達式(expression)詳解

自定義切入

這種方法不使用Spring提供的接口,而在Spring配置文件裏進行設置,不如前一種方法強大,因爲前一種方法可以利用反射獲取大量信息。

UserService接口和UserServiceImp實現類不變。新增一個自定義切面:

    package com.lbl.myaop;
    
    public class DiyAop {
        public void beforeMethod() {
            System.out.println("在方法執行之前");
        }
    
        public void afterMethod() {
            System.out.println("在方法執行之後");
        }
    }

修改配置文件:

    <?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
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop
            https://www.springframework.org/schema/aop/spring-aop.xsd">
    
    
        <bean id="userService" class="com.lbl.service.UserServiceImp"/>
        <bean id="beforeLog" class="com.lbl.log.BeforeLog"/>
        <bean id="afterLog" class="com.lbl.log.AfterLog"/>
        <bean id="myaop" class="com.lbl.myaop.DiyAop"/>
        <aop:config>
    <!--        註冊一個切面-->
            <aop:aspect ref="myaop">
    <!--            註冊一個切入點-->
                <aop:pointcut id="diypc" expression="execution(* com.lbl.service.UserServiceImp.*(..))"/>
    <!--            選擇切入方法及位置和切入點-->
                <aop:before method="beforeMethod" pointcut-ref="diypc"/>
                <aop:after method="afterMethod" pointcut-ref="diypc"/>
            </aop:aspect>
        </aop:config>
    </beans>

使用註解實現AOP

    package com.lbl.myaop;
    
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    
    @Aspect //聲明這是一個切面
    public class Annopc {
        //聲明切入位置和切入點
        @Before("execution(* com.lbl.service.UserServiceImp.add())")
        public void beforeMethod() {
            System.out.println("註解方式,方法執行前");
        }
    }

在配置文件中註冊bean並引入對AOP註解的支持:

    <bean id="annopc" class="com.lbl.myaop.Annopc"/>
    <!--    proxy-target-class爲false是基於JDK,爲true是基於cglib,但兩者執行的結果相同-->
    <aop:aspectj-autoproxy proxy-target-class="false"/>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章