動態代理"碼"上實現

動態代理"碼"上實現

代理模式

提到動態代理,就不得不先提一下代理模式。當咱們不想修改原有對象A的方法A1,但卻想在使用該方法時,增長多一些功能時,就可使用代理模式。ide

代理模式的邏輯很簡單,例如上述的場景,咱們會先建立一個代理對象B,經過B,來間接調用對象A的方法A1,而且在調用方法A1的先後,增長一些代碼邏輯,由此提升擴展性。工具

咱們運行一個應用時,會經歷編譯-運行幾個階段。通常咱們根據代理的建立時期,將代理分紅兩類:靜態代理、動態代理。性能

靜態代理會在編譯期,生成代理類class文件。而動態代理,則是在程序運行時,經過反射機制,在內存中動態建立出代理類class文件。this

使用靜態代理

如下演示如何使用靜態代理。代理

  1. 建立服務接口。
public interface UserService {
    void createUser(String userName);}

2. 建立服務類,實現服務接口。code

public class UserServiceImpl implements UserService {
    @Override
    public void createUser(String userName) {
        System.out.println(userName + " is created");
    }}

3. 建立代理類,實現服務接口,而且將原服務類注入到該代理類中。對象

public class UserServiceProxy implements UserService {

    private UserService userService;
    //注入原服務類    public UserServiceProxy(UserService userService) {
        this.userService = userService;
    }

    @Override
    public void createUser(String userName) {
        //前置加強        System.out.println(userName +" will be created");
        // 調用原方法        userService.createUser(userName);
        //後置加強        System.out.println("finish");
    }}

4. 當須要使用方法時,不直接調用服務類對象,而是經過代理類對象。blog

//建立服務類對象UserService userService = new UserServiceImpl();//建立代理類對象UserService userServiceProxy = new UserServiceProxy(userService);//調用代理類對象,間接調用原方法userServiceProxy.createUser("王二");

優缺點

優勢: 能夠簡單的對目標對象進行功能擴展。繼承

缺點:須要對服務接口都實現一遍,不易管理。一旦接口修改,代理類也須要一併修改。接口

使用動態代理

因爲靜態代理的缺點,由此引出動態代理。動態代理的核心就是反射,經過反射咱們可以直接操做類或者對象,好比獲取某個類的定義,獲取某個類的屬性和方法等。在動態代理中,咱們再也不須要手動建立代理類。而是經過動態代理處理器,在應用運行時,經過反射生成代理類,最終生成咱們須要用到的代理對象。

動態代理種類

目前動態代理技術有不少,經常使用有:JDK動態代理,CGLIB動態代理,Javassist動態代理和ASM動態代理。

JDK動態代理是內置在JDK中的,而不須要引入第三方Jar包。CGLIB和Javassist是高級的字節碼生成工具,聽說是比JDK自帶的動態代理性能更好,功能十分強大。ASM是低級的字節碼生成工具,性能最好,但因爲更底層,對開發水平要求很高。

如下只簡單說明下JDK動態代理和CGLIB動態代理。

JDK動態代理使用

JDK動態代理是基於接口的動態代理。代理處理器類須要注入被代理的接口,而且須要繼承InvocationHandler接口。調用時,使用Proxy類中的newProxyInstance方法動態的建立代理類。

代碼實踐以下:

  1. 建立服務接口。
public interface UserService {
    void createUser(String userName);}

2. 建立服務類,實現服務接口。

public class UserServiceImpl implements UserService {
    @Override
    public void createUser(String userName) {
        System.out.println(userName + " is created");
    }}

3. 建立動態代理處理器類,實現InvocationHandler接口。

public class UserHandler implements InvocationHandler {

    private UserService userService;

    public UserHandler(UserService userService) {
        this.userService = userService;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        start();
        //經過反射調用方法,並獲取返回值        Object result = method.invoke(userService, args);
        end();
        
        return result;
    }
    
    //前置加強    private void start(){
        System.out.println("start");
    }
    //後置加強    private void end(){
        System.out.println("end");
    }}

4. 經過Proxy的newProxyInstance方法動態生成代理對象。

UserService userService = new UserServiceImpl();UserHandler userHandler = new UserHandler(userService);//獲取被代理類的類加載器和方法列表ClassLoader classLoader = userService.getClass().getClassLoader();Class<?>[] interfaces = userService.getClass().getInterfaces();UserService userServiceProxy = (UserService)Proxy.newProxyInstance(classLoader,interfaces,userHandler);//調用方法userServiceProxy.createUser("王二");

CGLIB動態代理使用

CGLIB動態代理,是針對類來實現代理的。經過對目標類繼承,重寫方法,以此實現動態代理。因而可知,CGLIB能夠代理絕大多數類,而JDK代理只能代理實現了接口的類。

代碼實踐以下:

  1. 因爲CGLIB動態代理是第三方庫,須要額外引入jar包。
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.5</version></dependency>

2. 建立服務類。

public class UserServiceImpl {
    @Override
    public void createUser(String userName) {
        System.out.println(userName + " is created");
    }}

3. 自定義攔截器,攔截目標類的方法。

public class UserServiceInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        start();
        
        Object result = methodProxy.invokeSuper(o, args);
        end();
        return result;
    }

    private void start(){
        System.out.println("start");
    }

    private void end(){
        System.out.println("end");
    }}

在該攔截器中,須要實現intercept方法,參數含義以下:

  • Object o: CGLIB 動態生成的代理類實例
  • Method method: method 爲實體類所調用的被代理的方法引用
  • Objectp[] args: 方法的參數列表
  • MethodProxy methodProxy : 這個就是生成的代理類對方法的引用。

4. 使用Enhancer,建立代理對象。Enhancer 是一個很是重要的類,它爲非接口類型建立一個代理,動態地建立給定類的子類。

Enhancer enhancer = new Enhancer();//設置目標類爲動態代理類的父類enhancer.setSuperclass(UserServiceImpl.class);//設置調用時的攔截處理器enhancer.setCallback(new UserServiceInterceptor());//建立代理類對象UserService userService = (UserService)enhancer.create();userService.createUser("王二");

總結靜態代理和動態代理的區別

靜態代理

  1. 代理類class文件,在編譯期生成。
  2. 靜態代理在編譯期就知道代理的對象。
  3. 靜態代理須要實現接口下的全部方法。

動態代理

  1. 代理類class文件,在程序運行時,經過反射機制動態生成。
  2. 代理類在運行期才肯定代理的對象是什麼。
  3. 動態代理不須要實現接口,而是經過同一個方法,經過反射機制動態調用。
相關文章
相關標籤/搜索