AOP從理論到實踐(一)

背景:

AOP(Aspect-Oriented Programming,面向方面編程),可以說是OOP(Object-Oriented Programing,面向對象編程)的補充和完善。OOP允許你定義從上到下的關係,但並不適合定義從左到右的關係。簡單地說,AOP就是將那些與業務無關,卻爲業務模塊所共同調用的邏輯或責任封裝起來,便於減少系統的重複代碼,降低模塊間的耦合度,並有利於未來的可操作性和可維護性。

實現原理:

AOP 代理其實是由 AOP 框架動態生成的一個對象,該對象可作爲目標對象使用。AOP 代理包含了目標對象的全部方法,但 AOP 代理中的方法與目標對象的方法存在差異:AOP 方法在特定切入點添加了增強處理,並回調了目標對象的方法。縱觀 AOP 編程,其中需要程序員參與的只有 3 個部分:定義普通業務組件定義切入點,一個切入點可能橫切多個業務組件。定義增強處理,增強處理就是在 AOP 框架爲普通業務組件織入的處理動作。
以上三部分重點就是定義切入點和增強處理,不難發現Spring AOP的實現原理就是:AOP 框架負責動態地生成 AOP 代理類,這個代理類的方法則由 Advice 和回調目標對象的方法所組成。

從歷史到現代

寫死代碼

我們每研究一個技術,總是會從最簡單,最常用的例子開始,下面看看這個例子:
接口:

package com.service;

public interface Greeting {
    void sayHello(String name);
}

實現類:

package com.serviceImp;

import com.service.Greeting;

public class GreetingImpl implements Greeting {

    @Override
    public void sayHello(String name) {
        before();
        after();
    }
    private void before(){
        System.out.print("Before");;
    }
    private void after(){
        System.out.println("After");
    }
}

before() 與 after() 方法寫死在 sayHello() 方法體中了,這樣的代碼的味道非常不好。如果哪位仁兄大量寫了這樣的代碼,肯定要被你的架構師罵個夠嗆。

比如:我們要統計每個方法的執行時間,以對性能作出評估,那是不是要在每個方法的一頭一尾都做點手腳呢?

再比如:我們要寫一個 JDBC 程序,那是不是也要在方法的開頭去連接數據庫,方法的末尾去關閉數據庫連接呢?

這樣的代碼只會把程序員累死,把架構師氣死!

一定要想辦法對上面的代碼進行重構,首先給出三個解決方案:

靜態代理:

在上面的基礎上添加代理類:

package com.serviceImp;

import com.service.Greeting;

public class GreetingProxy implements Greeting {

    private GreetingImpl greetingImpl;

    public GreetingProxy(GreetingImpl greetingImpl){
        this.greetingImpl=greetingImpl;
    }
    @Override
    public void sayHello(String name) {
        before();
        greetingImpl.sayHello(name);
        after();

    }
    private void before(){
        System.out.println("Before");
    }
    private void after(){
        System.out.println("After");
    }

}

客戶端調用:

import com.service.Greeting;
import com.serviceImp.GreetingImpl;
import com.serviceImp.GreetingProxy;


public class Client {

    public static void main(String[] args) {
        Greeting greetingProxy=new GreetingProxy(new GreetingImpl());
        greetingProxy.sayHello("jack");
    }

}

問題:
但是如果這樣沒增加一個類就增加一個代理類,豈不是有n個類等着我們去添加呢?可不可以用一個代理類呢?於是我們想到了jdk動態代理

jdk動態代理:

package com.jdkProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JDKDynamicProxy implements InvocationHandler{

    private Object target;

    public JDKDynamicProxy(Object target){
        this.target=target;
    }

    @SuppressWarnings("unchecked")
    public <T>T getProxy(){
        return(T)Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        before();
        Object result=method.invoke(target, args);
        after();
        return result;
    }
    private void before(){
        System.out.println("Before");
    }
    private void after(){
        System.out.println("After");
    }

}

客戶端:

import com.jdkProxy.JDKDynamicProxy;
import com.service.Greeting;
import com.serviceImp.GreetingImpl;
import com.serviceImp.GreetingProxy;


public class Client {

    public static void main(String[] args) {
        Greeting greetingProxy=new JDKDynamicProxy(new GreetingImpl()).getClass();
        greetingProxy.sayHello("jack");
    }

}

問題:jdk代理只能代理有接口的,因此可以說,如果要用jdk動態代理的,我們所有的類都必須有接口,但是在實際中並不是這樣的,因此我們引用了另一種方式:CGLib動態代理

CGLib動態代理

package com.CGLibProxy;

import java.lang.reflect.Method;


import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CGLibDynamicProxy implements MethodInterceptor{

    private static CGLibDynamicProxy instance=new CGLibDynamicProxy();
    private CGLibDynamicProxy(){

    }
    public static CGLibDynamicProxy getInstance(){
        return instance;
    }
    @SuppressWarnings("unchecked")
    public <T>T getProxy(Class<T> cls){
        return (T) Enhancer.create(cls,this);
    }
    @Override
    public Object intercept(Object target, Method method, Object[] arg2,
            MethodProxy proxy) throws Throwable {
        before();
        Object result=proxy.invokeSuper(target, arg2);
        after();
        return result;
    }
    private void before(){
        System.out.println("Before");
    }
    private void after(){
        System.out.println("After");
    }

}

客戶端調用:

import com.CGLibProxy.CGLibDynamicProxy;
import com.jdkProxy.JDKDynamicProxy;
import com.service.Greeting;
import com.serviceImp.GreetingImpl;
import com.serviceImp.GreetingProxy;


public class Client {

    public static void main(String[] args) {
        Greeting greetingProxy=CGLibDynamicProxy.getInstance().getProxy(GreetingImpl.class);
        greetingProxy.sayHello("jack");
    }

}

到此,這樣實現是否就足夠優雅了呢?不同的方法使用的場景不同,每種方法都有屬於自己的那個舞臺~

發佈了184 篇原創文章 · 獲贊 304 · 訪問量 41萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章