Java動態代理機制詳解(JDK動態代理與CGLIB動態代理區別)
阿新 • • 發佈:2019-02-19
代理是一種常用的設計模式,其目的就是為其他物件提供一個代理以控制對某個物件的訪問。代理類負責為委託類預處理訊息,過濾訊息並轉發訊息,以及進行訊息被委託類執行後的後續處理。在講述動態代理前,我們先通過一個例子瞭解一下什麼是靜態代理,這裡以事務控制為例。
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庫生成目標物件的子類。