背景:
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");
}
}
到此,這樣實現是否就足夠優雅了呢?不同的方法使用的場景不同,每種方法都有屬於自己的那個舞臺~