代理
我們拿事務(wù)的實現(xiàn)來舉例。首先,我們模擬一下事務(wù),編寫一個類
public class TransactionManager {
public void begin() {
System.out.println("開啟事務(wù)");
}
public void commit() {
System.out.println("提交事務(wù)");
}
public void rollback() {
System.out.println("回滾事務(wù)");
}
}
沒有代理
沒有代理的事務(wù)實現(xiàn)方式 -裝飾模式
可以在service實現(xiàn)類中開啟事務(wù),代碼類似于如下
public class EmployeeServiceImplWapper implements IEmployeeService {
private EmployeeServiceImpl service;
private TransactionManager txManager = new TransactionManager();
public EmployeeServiceImplWapper(EmployeeServiceImpl service) {
this.service = service;
}
@Override
public void add() {
try {
txManager.begin();
service.add();
txManager.commit();
} catch (Exception e) {
e.printStackTrace();
txManager.rollback();
}
}
}
在每次增加一個方法的時候,都要添加事務(wù),顯得比較麻煩?;谪?zé)任分離的原則,service層的責(zé)任就是關(guān)注于邏輯,代碼應(yīng)當(dāng)只關(guān)注于邏輯,只編寫邏輯代碼。
在調(diào)用service方法的時候,使用如下代理
public class App {
@Test
public void testExtends() throws Exception {
EmployeeServiceImpl empSer = new EmployeeServiceImpl();
IEmployeeService serviceWapper = new EmployeeServiceImplWapper(empSer);
serviceWapper.add();
}
}
至此,沒有使用代理的事務(wù)實現(xiàn)方式的完成
這種方式是不安全的。因為調(diào)用者完全可以跳過EmployeeServiceImplWapper類,直接調(diào)用EmployeeServiceImpl的add方法,這樣調(diào)用的方法就沒有事務(wù)了。
代理的事務(wù)實現(xiàn)方式
靜態(tài)代理
@Component
public class EmployeeServiceImplWapper implements IEmployeeService {
private EmployeeServiceImpl service;
private TransactionManager txManager = new TransactionManager();
public void setService(EmployeeServiceImpl service) {
this.service = service;
}
@Override
public void add() {
try {
txManager.begin();
service.add();
txManager.commit();
} catch (Exception e) {
e.printStackTrace();
txManager.rollback();
}
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="ser" class="com.test.EmployeeServiceImplWapper">
<property name="service">
<bean class="com.test.service.impl.EmployeeServiceImpl"></bean>
</property>
</bean>
</beans>
上述使用了spring的管理工具,service不再暴露在外。
靜態(tài)代理的優(yōu)點:
- 業(yè)務(wù)類只需要關(guān)注業(yè)務(wù)邏輯本身,保證了業(yè)務(wù)類的重用性。
靜態(tài)代理的缺點:
- 代理對象的某個接口只服務(wù)于某一種類型的對象,也就是說每一個真實對象都得創(chuàng)建一個代理對象。
- 如果需要代理的方法很多,則要為每一種方法都進(jìn)行代理處理。
- 如果接口增加一個方法,除了所有實現(xiàn)類需要實現(xiàn)這個方法外,所有代理類也需要實現(xiàn)此方法。增加了代碼維護(hù)的復(fù)雜度。
動態(tài)代理
@Component
public class TransactionManagerInvocationHandler implements InvocationHandler {
@Resource
private TransactionManager txManager;
@Resource
private EmployeeServiceImpl service;
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object obj = null;
try {
txManager.begin();
obj = method.invoke(service, args);
txManager.commit();
} catch (Exception e) {
e.printStackTrace();
txManager.rollback();
}
return obj;
}
public T getProxy() {
Object proxy = (IEmployeeService) Proxy.newProxyInstance(Thread
.currentThread().getContextClassLoader(),
new Class[] { IEmployeeService.class }, this);
return (T) proxy;
}
}
動態(tài)代理類是在程序運行期間由JVM通過反射等機制動態(tài)的生成的,所以不存在代理類的字節(jié)碼文件。代理對象和真實對象的關(guān)系是在程序運行時候才確定的。
接口InvocationHandler的invoke方法簡介:
Object proxy --> 生成的代理對象
Method method --> 代理對象調(diào)用的方法
Object[] args --> 代理對象調(diào)用的方法的參數(shù)
return obj --> 調(diào)用方法Method method返回的值
jdk動態(tài)代理總結(jié):
JAVA動態(tài)代理是使用java.lang.reflect包中的Proxy類與InvocationHandler接口這兩個來完成的。
要使用JDK動態(tài)代理,必須要定義接口。
JDK動態(tài)代理將會攔截所有pubic的方法(因為只能調(diào)用接口中定義的方法),這樣即使在接口中增加了新的方法,不用修改代碼也會被攔截。
如果只想攔截一部分方法,可以在invoke方法中對要執(zhí)行的方法名進(jìn)行判斷。
第三方代理CGLIB
@Component
public class TransactionManagerEnhancerHandler implements InvocationHandler {
@Resource
private TransactionManager txManager;
@Resource
private EmployeeServiceImpl service;
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object obj = null;
try {
txManager.begin();
obj = method.invoke(service, args);
txManager.commit();
} catch (Exception e) {
e.printStackTrace();
txManager.rollback();
}
return obj;
}
public T getProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(EmployeeServiceImpl.class);
enhancer.setCallback(this);
return (T) enhancer.create();
}
}
cglib對指定的目標(biāo)類生成一個子類,并覆蓋其中方法實現(xiàn)增強,但因為采用的是繼承,所以不能對final修飾的類進(jìn)行代理。
CGLIB動態(tài)代理總結(jié):
- CGLIB可以生成目標(biāo)類的子類,并重寫父類非final修飾符的方法。
- 要求類不能是final的,要攔截的方法要是非final、非static、非private的。
- 動態(tài)代理的最小單位是類(所有類中的方法都會被處理)。
spring對代理的選擇
當(dāng)代理的類沒有實現(xiàn)接口的時候,使用的代理方式是CGLIB。如果代理的類有實現(xiàn)的接口的時候,使用的代理方式就是jdk動態(tài)代理