1. 程式人生 > >Java動態代理機制詳解(JDK動態代理與CGLIB動態代理區別)

Java動態代理機制詳解(JDK動態代理與CGLIB動態代理區別)

代理是一種常用的設計模式,其目的就是為其他物件提供一個代理以控制對某個物件的訪問。代理類負責為委託類預處理訊息,過濾訊息並轉發訊息,以及進行訊息被委託類執行後的後續處理。在講述動態代理前,我們先通過一個例子瞭解一下什麼是靜態代理,這裡以事務控制為例。

1.靜態代理

1.1 pom.xml檔案配置

<properties>
    <!-- Spring -->
    <spring.version>4.1.3.RELEASE</spring.version>
</properties>
<dependencies>
    <!-- Spring Begin -->
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version
>
</dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <!-- Spring End --> </dependencies>

1.2 業務介面程式碼

public interface UserService {
    /**
     * 
     * 功能描述: <br>
     * 〈儲存使用者〉
     *
     * @since [1.0.0](可選)
     */
    public void saveUser();

    /**
     * 
     * 功能描述: <br>
     * 〈更新使用者〉
     *
     * @since [1.0.0](可選)
     */
    public void updateUser();
}

1.3 業務實現類程式碼

@Service("userService")
public class UserServiceImpl implements UserService {
    @Override
    public void saveUser() {
        System.out.println("userService saveUser()...");
    }

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

1.4 事務管理類程式碼

@Service("transactionManager")
public class TransactionManager {
    public void beginTransaction() {
        System.out.println("開啟事務....");
    }

    public void commint() {
        System.out.println("提交事務....");
    }

    public void rollback() {
        System.out.println("回滾事務....");
    }
}

1.5 代理類程式碼

@Service("userServiceProxy")
public class UserServiceProxy implements UserService {
    @Resource(name = "userService")
    private UserService userService;

    @Resource(name = "transactionManager")
    private TransactionManager transactionManager;

    @Override
    public void saveUser() {
        try {
            transactionManager.beginTransaction();
            userService.saveUser();
            transactionManager.commint();
        } catch (Exception e) {
            transactionManager.rollback();
        }
    }

    @Override
    public void updateUser() {
        try {
            transactionManager.beginTransaction();
            userService.updateUser();
            transactionManager.commint();
        } catch (Exception e) {
            transactionManager.rollback();
        }
    }
}

1.6 測試類程式碼

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext.xml" })
public class ProxyTest {
    @Resource(name = "userServiceProxy")
    private UserService userService;

    @Test
    public void testStaticProxy() {
        userService.saveUser();
    }
}

1.7 測試結果

開啟事務....
userService saveUser()...
提交事務....

從上面的程式碼,可以看出靜態代理給我們帶來的一系列問題,如在代理物件中包含了真實物件的引用,如果我們需要為不同的業務進行代理,就需要為每個業務都建立一個代理物件,甚是麻煩,所以引出了動態代理。

2.JDK動態代理

2.1 業務類介面

public interface UserService {
    /**
     * 
     * 功能描述: <br>
     * 〈儲存使用者〉
     *
     * @since [1.0.0](可選)
     */
    public void saveUser();

    /**
     * 
     * 功能描述: <br>
     * 〈更新使用者〉
     *
     * @since [1.0.0](可選)
     */
    public void updateUser();
}

2.2 業務實現類

@Service("userService")
public class UserServiceImpl implements UserService {
    @Override
    public void saveUser() {
        System.out.println("userService saveUser()...");
    }

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

2.3 事務管理類程式碼

@Service("transactionManager")
public class TransactionManager {
    public void beginTransaction() {
        System.out.println("開啟事務....");
    }

    public void commint() {
        System.out.println("提交事務....");
    }

    public void rollback() {
        System.out.println("回滾事務....");
    }
}

2.4 代理類程式碼

@Service("transactionManagerProxy")
public class TransactionManagerProxy implements InvocationHandler {
    private Object target;

    @Resource(name = "transactionManager")
    private TransactionManager transactionManager;

    public Object createProxyObject(Object object) {
        this.target = object;
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        transactionManager.beginTransaction();
        Object object = null;
        try {
            object = method.invoke(target, args);
            transactionManager.commint();
        } catch (Exception e) {
            transactionManager.rollback();
        }
        return object;
    }
}

2.5 測試類程式碼

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext.xml" })
public class ProxyTest {
    @Resource(name = "transactionManagerProxy")
    private TransactionManagerProxy transactionManagerProxy ;

    @Test
    public void testStaticProxy() {
        UserService userService = (UserService) transactionManagerProxy.createProxyObject(new UserServiceImpl());
        userService.saveUser();
    }
}

2.6 測試結果

開啟事務....
userService saveUser()...
提交事務....

但是,JDK的動態代理依靠介面實現,如果有些類並沒有實現介面,則不能使用JDK代理,這就要使用cglib動態代理了。

3.CGLIB動態代理

3.1 業務介面類

public interface UserService {
    /**
     * 
     * 功能描述: <br>
     * 〈儲存使用者〉
     *
     * @since [1.0.0](可選)
     */
    public void saveUser();

    /**
     * 
     * 功能描述: <br>
     * 〈更新使用者〉
     *
     * @since [1.0.0](可選)
     */
    public void updateUser();
}

3.2 業務實現類

@Service("userService")
public class UserServiceImpl implements UserService {
    @Override
    public void saveUser() {
        System.out.println("userService saveUser()...");
    }

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

3.3 事務管理類

@Service("transactionManager")
public class TransactionManager {
    public void beginTransaction() {
        System.out.println("開啟事務....");
    }

    public void commint() {
        System.out.println("提交事務....");
    }

    public void rollback() {
        System.out.println("回滾事務....");
    }
}

3.4 事務代理類

public class TransactionManagerProxy implements org.springframework.cglib.proxy.InvocationHandler {
    private Object target;
    private TransactionManager transactionManager;

    public TransactionManagerProxy(Object target, TransactionManager transactionManager) {
        this.target = target;
        this.transactionManager = transactionManager;
    }

    // 建立代理物件
    public Object getProxyInstance() {
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(this.getClass().getClassLoader());
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        transactionManager.beginTransaction();
        Object object = null;
        try {
            object = method.invoke(target, args);
            transactionManager.commint();
        } catch (Exception e) {
            transactionManager.rollback();
        }
        return object;
    }
}

3.5 測試類

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext.xml" })
public class ProxyTest {
    @Resource(name = "transactionManager")
    private TransactionManager transactionManager;

    @Test
    public void testStaticProxy() {
        UserServiceImpl userServiceImpl = (UserServiceImpl) new TransactionManagerProxy(new UserServiceImpl(), transactionManager).getProxyInstance();
        userServiceImpl.saveUser();
    }
}

3.6 測試結果

開啟事務....
userService saveUser()...
提交事務....

1.CGLIB可以生成目標類的子類,並重寫父類非final修飾符的方法。
2.要求類不能是final的,要攔截的方法要是非final,非static,非private的
3.動態代理的最小單位是類(所有類中的方法都會被處理)

注:若目標物件實現了若干介面,Spring就會使用JDK動態代理,若目標物件沒有實現任何介面,Spring就使用CGLIB庫生成目標物件的子類。