Spring?AOP原理及動態(tài)代理
一、什么是代理?
指為一個目標對象提供一個代理對象, 并由代理對象控制對目標對象的引用. 使用代理對象, 是為了在不修改目標對象的基礎上,增強目標對象的業(yè)務邏輯.

1、靜態(tài)代理
靜態(tài)代理的特點是, 為每一個業(yè)務增強都提供一個代理類, 由代理類來創(chuàng)建代理對象. 下面我們通過靜態(tài)代理來實現(xiàn)對轉賬業(yè)務進行身份驗證.
(1) 轉賬業(yè)務
public interface IAccountService {
//主業(yè)務邏輯: 轉賬
void transfer();
}
public class AccountServiceImpl implements IAccountService {
@Override
public void transfer() {
System.out.println("調用dao層,完成轉賬主業(yè)務.");
}
}
(2) 代理類
public class AccountProxy implements IAccountService {
//目標對象
private IAccountService target;
public AccountProxy(IAccountService target) {
this.target = target;
}
/**
* 代理方法,實現(xiàn)對目標方法的功能增強
*/
@Override
public void transfer() {
before();
target.transfer();
}
/**
* 前置增強
*/
private void before() {
System.out.println("對轉賬人身份進行驗證.");
}
}(3) 測試
public class Client {
public static void main(String[] args) {
//創(chuàng)建目標對象
IAccountService target = new AccountServiceImpl();
//創(chuàng)建代理對象
AccountProxy proxy = new AccountProxy(target);
proxy.transfer();
}
}結果: 對轉賬人身份進行驗證.調用dao層,完成轉賬主業(yè)務.
2、動態(tài)代理
靜態(tài)代理會為每一個業(yè)務增強都提供一個代理類, 由代理類來創(chuàng)建代理對象, 而動態(tài)代理并不存在代理類, 代理對象直接由代理生成工具動態(tài)生成.
2.1、JDK動態(tài)代理
JDK動態(tài)代理是使用 java.lang.reflect 包下的代理類來實現(xiàn). JDK動態(tài)代理動態(tài)代理必須要有接口.
(1) 轉賬業(yè)務
public interface IAccountService {
//主業(yè)務邏輯: 轉賬
void transfer();
}
public class AccountServiceImpl implements IAccountService {
@Override
public void transfer() {
System.out.println("調用dao層,完成轉賬主業(yè)務.");
}
}(2) 增強
因為這里沒有配置切入點, 稱為切面會有點奇怪, 所以稱為增強.
public class AccountAdvice implements InvocationHandler {
//目標對象
private IAccountService target;
public AccountAdvice(IAccountService target) {
this.target = target;
}
/**
* 代理方法, 每次調用目標方法時都會進到這里
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
return method.invoke(target, args);
}
/**
* 前置增強
*/
private void before() {
System.out.println("對轉賬人身份進行驗證.");
}
}(3) 測試
public class Client {
public static void main(String[] args) {
//創(chuàng)建目標對象
IAccountService target = new AccountServiceImpl();
//創(chuàng)建代理對象
IAccountService proxy = (IAccountService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new AccountAdvice(target)
);
proxy.transfer();
}
}結果: 對轉賬人身份進行驗證.調用dao層,完成轉賬主業(yè)務.
2.2、 CGLIB動態(tài)代理
JDK動態(tài)代理必須要有接口, 但如果要代理一個沒有接口的類該怎么辦呢? 這時我們可以使用CGLIB動態(tài)代理. CGLIB動態(tài)代理的原理是生成目標類的子類, 這個子類對象就是代理對象, 代理對象是被增強過的.
注意: 不管有沒有接口都可以使用CGLIB動態(tài)代理, 而不是只有在無接口的情況下才能使用
(1) 轉賬業(yè)務
public class AccountService {
public void transfer() {
System.out.println("調用dao層,完成轉賬主業(yè)務.");
}
}(2) 增強
因為這里沒有配置切入點, 稱為切面會有點奇怪, 所以稱為增強.
public class AccountAdvice implements MethodInterceptor {
/**
* 代理方法, 每次調用目標方法時都會進到這里
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
before();
return methodProxy.invokeSuper(obj, args);
// return method.invoke(obj, args); 這種也行
}
/**
* 前置增強
*/
private void before() {
System.out.println("對轉賬人身份進行驗證.");
}
}(3) 測試
public class Client {
public static void main(String[] args) {
//創(chuàng)建目標對象
AccountService target = new AccountService();
//
//創(chuàng)建代理對象
AccountService proxy = (AccountService) Enhancer.create(target.getClass(),
new AccountAdvice());
proxy.transfer();
}
}結果: 對轉賬人身份進行驗證.調用dao層,完成轉賬主業(yè)務.
二、模擬Spring AOP場景
了解了動態(tài)代理后, 我們就可以自己來實現(xiàn)Spring AOP功能了, 所以下面我們來模擬下Spring AOP場景.
(1) 轉賬業(yè)務
public interface IAccountService {
//主業(yè)務邏輯: 轉賬
void transfer();
}
public class AccountServiceImpl implements IAccountService {
@Override
public void transfer() {
System.out.println("調用dao層,完成轉賬主業(yè)務.");
}
}(2) 切面抽象類
定義一個切面抽象類, 該類使用了模板方法的設計模式, 為開始, 結束, 異常, 前置增強, 后置增強提供了默認實現(xiàn), 當我們定義切面類時只需要按需重寫它們就行. isIntercept() 方法用來判斷切入點是否正確, 切面類需要重寫這個方法.
public abstract class BaseAspect implements MethodInterceptor {
private static final Logger logger = LoggerFactory.getLogger(BaseAspect.class);
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object result = null;
begin();
try {
if (isIntercept(method, args)) {
before();
result = methodProxy.invokeSuper(obj, args);
after();
} else {
result = methodProxy.invokeSuper(obj,args);
}
} catch (Exception e) {
logger.error("proxy failure", e);
error(e);
throw e;
} finally {
end();
}
return result;
}
/**
* 開始增強
*/
public void begin() {
}
/**
* 切入點判斷
*/
public boolean isIntercept(Method method, Object[] args) throws Throwable {
return true;
}
/**
* 前置增強
*/
public void before() throws Throwable {
}
/**
* 后置增強
*/
public void after() throws Throwable {
}
/**
* 異常增強
*/
public void error(Throwable e) {
}
/**
* 最終增強
*/
public void end() {
}
}(3) 切面類
創(chuàng)建一個切面類, 類中配置切入點和增強.
public class AccountAspect extends BaseAspect {
/**
* 切入點
*/
public boolean isIntercept(Method method, Object[] args) throws Throwable {
return method.getName().equals("transfer");
}
/**
* 前置增強
*/
public void before() throws Throwable {
System.out.println("對轉賬人身份進行驗證.");
}
}(4) 代理工廠類
定義一個工廠類來創(chuàng)建代理, 其實不創(chuàng)建這個類也行, 但為了模仿Spring還是創(chuàng)建了. @SuppressWarnings是為了抑制警告, 就是編譯器上面的黃線.
public class ProxyFactory {
@SuppressWarnings("unchecked")
public static <T> T createProxy(final Class<?> targetClass, final MethodInterceptor methodInterceptor) {
return (T) Enhancer.create(targetClass,methodInterceptor);
}
}(5) 測試
public class Client {
public static void main(String[] args) {
//創(chuàng)建目標對象
IAccountService target = new AccountServiceImpl();
//切面
BaseAspect accountAspect = new AccountAspect();
//創(chuàng)建代理對象
IAccountService proxy = (IAccountService) ProxyFactory.createProxy(target.getClass(), accountAspect);
proxy.transfer();
}
}結果:對轉賬人身份進行驗證.調用dao層,完成轉賬主業(yè)務.
到此這篇關于Spring AOP原理及動態(tài)代理的文章就介紹到這了,更多相關Spring AOP 內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
詳解Maven 搭建spring boot多模塊項目(附源碼)
這篇文章主要介紹了詳解Maven 搭建spring boot多模塊項目(附源碼),具有一定的參考價值,有興趣的可以了解一下2017-09-09

