天天看點

Java 實作代理模式以及通過Spring AOP 實作代理模式什麼是代理代理模式

文章目錄

  • 什麼是代理
  • 代理模式
    • 靜态代理
        • 實作步驟
    • 動态代理
      • 基本JDK動态代理
        • 實作步驟:
      • Spring 手動代理---JDK動态代理
        • 實作步驟
      • cglib動态代理
        • 實作步驟
      • Spring 手動代理 cglib位元組碼增強
        • 實作步驟
      • Spring 工廠Bean代理 ----半自動化
        • 實作步驟
      • Spring aop 實作代理
        • 實作步驟

什麼是代理

我們大家都知道微商代理,簡單地說就是代替廠家賣商品,廠家“委托”代理為其銷售商品。關于微商代理,首先我們從他們那裡買東西時通常不知道背後的廠家究竟是誰,也就是說,“委托者”對我們來說是不可見的;其次,微商代理主要以朋友圈的人為目标客戶,這就相當于為廠家做了一次對客戶群體的“過濾”。我們把微商代理和廠家進一步抽象,前者可抽象為代理類,後者可抽象為目标類(被代理類)。通過使用代理,通常有兩個優點,并且能夠分别與我們提到的微商代理的兩個特點對應起來:

優點一:可以隐藏目标類的實作;

優點二:可以實作客戶與委托類間的解耦,在不修改委托類代碼的情況下能夠做一些額外的處理

參考連結:https://juejin.im/post/5ad3e6b36fb9a028ba1fee6a

代理模式

靜态代理

一般将代理類在程式運作之前就存在的代理模式稱為靜态代理。通常情況下,代理類與目标類都實作了同一接口。

實作步驟

1.目标類:

public class StuServiceImpl implements StuService{
    @Override
    public void addStu() {
        System.out.println("add Stu!!!");

    }
    @Override
    public void deleteStu() {
        System.out.println("delete Stu!!!");
    }
    @Override
    public void modifyStu() {
        System.out.println("modify Stu!!!");
    }
}
           

2.代理類:

public class AgencyStu implements StuService{
    private  StuService stuService;
    //接收目标類執行個體
    public AgencyStu(StuService stuService) {
        this.stuService = stuService;
    }
    //實作代理
    @Override
    public void addStu() {
        System.out.println("before add");
        stuService.addStu();
        System.out.println("end add");
    }
    @Override
    public void deleteStu() {
        System.out.println("before delete");
        stuService.deleteStu();
        System.out.println("end delete");
    }
    @Override
    public void modifyStu() {
        System.out.println("before modify");
        stuService.modifyStu();
        System.out.println("end modify");
    }
}
           

可以看到上面代理類實作了對目标類的代理,但是由于代理類是在程式運作開始前就存在了,是以當存在不同的代理需求時,還需要再次建立一個新的代理類去實作代理需求,這就造成了代碼的備援,是以就出現了動态代理。

動态代理

基本JDK動态代理

代理類在運作時被建立的代理方式交動态代理。也就是說代理類不是在java代碼中定義的,而是在運作過程中在我們java代碼的訓示下動态生成的。相比于靜态代理,它可以實作對代理方法的統一處理,而不用像靜态代理一樣,挨個去編寫重複代碼。

實作步驟:

需要建立一個類似于"中介"的類,中介類需要實作InvocationHandler接口,并重寫method.invoke方法,然後調用Proxy.newProxyInstance建立代理類,實作動态代理。

例如:

目标類:

public class ShopServiceImpl implements  ShopService{
    @Override
    public void addShop() {
        System.out.println("add Shop!!!");
    }
    @Override
    public void deleteShop() {
        System.out.println("delete Shop!!!");
    }
    @Override
    public void queryShop() {
        System.out.println("query Shop!!!");
    }
}
           

中介類:

public class Dynamicagency implements InvocationHandler {
    private Object object;
    //調用目标類執行個體
    public Dynamicagency(Object object) {
        this.object = object;
    }
    @Override
    //代理類每執行方法時,都會調用此類的invoke方法
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before");
        Object result = method.invoke(object,args);
        System.out.println("after");
        return result;
    }
}
           

動态建立代理類

//生成中介類執行個體
Dynamicagency dynamicagency = new Dynamicagency(new ShopServiceImpl());
//生成代理類
ShopService shopService = (ShopService) Proxy.newProxyInstance(ShopService.class.getClassLoader(),
        new Class[]{ShopService.class},dynamicagency);
shopService.addShop();
           

在這裡解釋一下Proxy.newProxyInstance的參數:

參數1 : loader,類加載器,動态代理類,運作時建立,任何類都需要加載器将其加載到記憶體。

參數2: Class[] interfaces 代理類需要實作的所有接口

1. 目标類執行個體.getClass().getInterfaces(); 注意隻能獲得自己接口,不能獲得父元素接口
2. new Class[]{interface.class}

參數3:InvocationHandler 處理類,接口,必須進行實作類,一般采取匿名内部類

​	   提供invoke方法,代理類的每一個方法執行時,都将調用一次invoke。

​		參數31:Object proxy : 代理對象

​		參數32:Method method: 代理對象目前執行的方法的描述對象(反射)

​		參數33:Object[] args:方法實際參數		
           

是以,代理類執行代理方法,其實就是對實作InvocationHandler 中介類的調用,然後中介類通過反射原理實作對原目标類方法的調用。

Spring 手動代理—JDK動态代理

在這裡說一下aop術語:

AOP 術語:

  1. target : 目标類,需要被代理的類,例如: UserService
  2. Joinpoint: 連接配接點,所謂連接配接點是指那些可能被攔截到的方法。例如所有方法
  3. PonitCut :切入點,指已經被增強的連接配接點。例如addUser()
  4. advice: 通知/增強,增強代碼。例如after,before
  5. Weaving : 織入,是指把增強advice應用到目标對象target來建立的代理對象proxy的過程。
  6. proxy 代理類
  7. Aspect 切面 是把pointcut和通知advice的結合。

    ​ 一個是一個特殊的面

    ​ 一個切入點和一個通知,組合成一個特殊的面

    圖示:

    Java 實作代理模式以及通過Spring AOP 實作代理模式什麼是代理代理模式

實作步驟

  1. 目标(委托)類:接口+實作類
public class UserServiceImpl  implements Userservice {
    @Override
    public void addUser() {
        System.out.println("add User!!!");
    }
    @Override
    public void deleteUser() {
        System.out.println("delete User!!!");
    }
    @Override
    public void updateUser() {
        System.out.println("update User!!!");
    }
}
           
  1. 切面類:用于通知
public class MyAspect {
    public void before()
    {
        System.out.println("running before");
    }

    public void after()
    {
        System.out.println("running after");
    }
}
           
  1. 工廠類: 編寫工廠生産代理
public class MyBeanfactory {
    public static  Userservice  createUserSerive()
    {
        final Userservice userservice = new UserServiceImpl();

        final MyAspect myAspect = new MyAspect();

        Userservice result = (Userservice) Proxy.newProxyInstance(Userservice.class.getClassLoader()
        ,userservice.getClass().getInterfaces(), new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        myAspect.before();
                        Object object = method.invoke(userservice,args);
                        myAspect.after();
                        return object;

                    }
                });
        return result;
    }
}
           

cglib動态代理

Cglib是一個強大的、高性能的代碼生成包,它廣泛被許多AOP架構使用,為它們提供方法的攔截。

cglib動态代理的特點是目标類可以不用實作特定的方法,cglib在運作時建立目标類的增強類。

實作步驟

  1. 建立目标類(不用實作接口)
public class ShopServiceImpl {
    public void addShop() {
        System.out.println("add Shop!!!");
    }

    public void deleteShop() {
        System.out.println("delete Shop!!!");
    }

    public void queryShop() {
        System.out.println("query Shop!!!");
    }
}
           
  1. 編寫攔截器(實作MethodInterceptor)
public class MyMethodIntecetor implements MethodInterceptor {
    private ShopServiceImpl shopService;

    public MyMethodIntecetor(ShopServiceImpl shopService) {
        this.shopService = shopService;
    }

    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("before");
        //兩種方法都可以
        //Object result = methodProxy.invokeSuper(proxy,args);
        Object result = method.invoke(shopService,args);
        System.out.println("after");
        return result;
    }
}
           

其中MethodInterceptor中intercept方法的參數如下:

proxy 表示的是代理類

method:代理對象目前執行的方法的描述對象(反射)

args:方法實際參數

methodProxy:方法的代理

3. 建立代理類

Enhancer enhancer = new Enhancer();
        //添加父類
        enhancer.setSuperclass(ShopServiceImpl.class);
        //調用回調函數
        enhancer.setCallback(new MyMethodIntecetor(new ShopServiceImpl()));
        //建立代理對象
        ShopServiceImpl shopService = (ShopServiceImpl) enhancer.create();
        shopService.addShop();
           

Spring 手動代理 cglib位元組碼增強

這種方式主要是通過BeanFactory生産代理對象,與上面不同的就是多個工廠還有含有通知的類。

實作步驟

  1. 目标類
public class ShopServiceImpl  {

    public void addShop() {
        System.out.println("add Shop!!!");
    }

    public void deleteShop() {
        System.out.println("delete Shop!!!");
    }

    public void queryShop() {
        System.out.println("query Shop!!!");
    }
}
           
  1. 含有通知的類
public class AdviceClass {
    public void before()
    {
        System.out.println("before");
    }
    public void after()
    {
        System.out.println("after");
    }
}
           
  1. 建立工廠
public class MyBeanFactory {
    public static ShopServiceImpl createShopServiceImpl()
    {
        ShopServiceImpl shopService = new ShopServiceImpl();

        AdviceClass adviceClass = new AdviceClass();

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(ShopServiceImpl.class);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            //建立匿名内部類
            public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                adviceClass.before();
                // 兩種寫法都可以
                //Object result = methodProxy.invokeSuper(object,args);
                Object result = method.invoke(shopService,args);
                adviceClass.after();
                return result;
            }
        });
        ShopServiceImpl proxyShop = (ShopServiceImpl) enhancer.create();
        return proxyShop;
    }
}
           

Spring 工廠Bean代理 ----半自動化

上面的工廠是我們手動建立的,其實我們可以通過使用Spring中的ProxyFactoryBean代替手動建立工廠。下面是步驟。

實作步驟

  1. 目标類
public class UserServiceImpl implements  UserService{
    @Override
    public void addUser() {
        System.out.println("addUser!!!");
    }

    @Override
    public void deleteUser() {
        System.out.println("deleteUser!!!");
    }

    @Override
    public void updateUser() {
        System.out.println("updateUser!!!");
    }
}
           
  1. 切面類
public class MyAspect implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("before");
        //實作目标類的相應方法
        Object object =methodInvocation.proceed();
        System.out.println("after");
        return object;
    }
}
           
  1. 接下來是配置xml檔案
<!--建立目标類-->
    <bean id="userServiceImplId" class="com.zamao.FactoryAgencyBeanXml.UserServiceImpl"></bean>
    <!--建立切面類-->
    <bean id="myAspect" class="com.zamao.FactoryAgencyBeanXml.MyAspect"></bean>
    <!--建立代理類-->
    <!--
       使用工廠bean FactoryBean,底層調用getObject() 傳回特殊bean
       ProxyFactoryBean 用于建立代理工廠bean,生成特殊代理對象
       interfaces : 确定接口們  通過<array> 可以設定多個值 隻有一個值時,value=""
        target: 目标類
        interceptorNames : 通知切面類的名稱,類型String[],如果設定一個值value=""
       -->
    <bean id="proxyUserService" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="interfaces" value="com.zamao.FactoryAgencyBeanXml.UserService">			   	</property>
        <property name="target" ref="userServiceImplId"></property>
        <property name="interceptorNames" value="myAspect"></property>
    </bean>
           

Spring aop 實作代理

與上面不同的是将配置工廠替換成了aop配置,相比于工廠配置來說更加簡便,也可以對攔截條件進行設定。

實作步驟

1.導入aspectjweaver-1.9.4.jar。

2. 引入aop命名空間

在xml中添加如下代碼:

xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
           

3.目标類:

public class UserServiceImpl implements UserService {
    @Override
    public void addUser() {
        System.out.println("addUser!!! springAopProgramm");
    }

    @Override
    public void deleteUser() {
        System.out.println("deleteUser!!! springAopProgramm");
    }

    @Override
    public void updateUser() {
        System.out.println("updateUser!!! springAopProgramm");
    }
}
           

4.切面類:

public class MyAspect implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("before");
        Object object =methodInvocation.proceed();
        System.out.println("after");
        return object;
    }
}
           

5.配置xml

<!--建立目标類-->
  <bean id="userServiceImplId" class="com.zamao.springAopProgramme.UserServiceImpl"></bean>
  <!--建立切面類-->
  <bean id="myAspect" class="com.zamao.springAopProgramme.MyAspect"></bean>
  <!--aop程式設計
      1. 引入命名空間
      2. 使用<aop:config> 進行配置
             <aop:pointcut> 切入點,從目标對象中獲得具體方法
             <aop:advisor> 特殊的切面,隻有一個通知和一個切入點
             advice-ref 通知引用
             point-ref 切入點引用
     3. 切入點表達式:
          execution(傳回值 包 類名 (方法名) 參數名)

  -->
  <aop:config>
      <aop:pointcut id="MyPointCut" expression="execution(* com.zamao.springAopProgramme.*.*(..))"/>
      <aop:advisor advice-ref="myAspect" pointcut-ref="MyPointCut"></aop:advisor>
  </aop:config>