1. 程式人生 > >Spring(2)之 (AOP 面向切面程式設計)(AOP目錄)

Spring(2)之 (AOP 面向切面程式設計)(AOP目錄)

Spring Core:

Spring的核心功能即IOC容器,解決物件的建立及物件之間的依賴關係

Spring WEB:

Spring對WEB模組的支援

Spring AOP: 面向切面程式設計

  1. AOP的概述
  2. AOP的底層實現
  3. Spring AOP開發
    (網址: https://blog.csdn.net/qq_41029923/article/details/84230164
  4. 使用 AspectJ實現 AOP
    (網址: https://blog.csdn.net/qq_41029923/article/details/84250237
  5. Spring JDBC Template的使用

    (網址: https://blog.csdn.net/qq_41029923/article/details/84291336
     

1. AOP程式設計基本概念:
AOP: Aspect Object Programming 面向切面程式設計
      功能:讓關注點程式碼與業務程式碼分離
關注點: 重複的程式碼(方法)
切面: 關注點形成的類
面向切面程式設計就是指對很多功能重複的程式碼的抽象,再在執行時向業務方法中動態的植入“切面類程式碼”;
切入點: 通過切入點表示式,指定攔截哪些類的哪些方法,給指定的類在執行時植入切面類程式碼
 

2. 代理模式:


代理模式是一種設計模式,提供了對目標物件的另外的訪問方式即通過代理物件訪問目標物件;
好處: 可以在目標物件實現的基礎上(即不改變目標物件的程式碼),增加額外的功能——擴充套件目標物件的功能

  1. 靜態代理
    eg:儲存資料到資料庫,Dao直接儲存; 新增事務;代理物件要實現與目標物件一樣的介面;
    優點: 可以做到在不修改目標物件功能的前提下(即目標物件程式碼不做更改),對目標物件進行擴充套件;
    缺點: 代理物件要實現與目標物件一樣的介面,會有很多代理類,一旦介面增加方法,目標物件和代理物件都需要維護(即因為兩者都實現了此介面,所以程式碼都要做更改)
    解決方式: 使用代理工廠、動態代理;
  2. 動態代理(也叫JDK代理、介面代理)
    目標物件一定要實現介面, 否則不能使用動態代理;
    代理物件不需要實現介面;
    代理物件的生成使用到 JDK API,動態的在記憶體中建立物件 (即 class $Proxy0 implements XXX);
    API中:
    Proxy
    public static Object newProxyInstance(
          ClassLoader loader, //指定當前目標物件的使用類的載入器
          Class<?> [] interfaces,//目標物件實現的介面型別
          InvocationHandler h //事件處理器
    )
  3. Cglib代理(子類代理)
    子類代理,在記憶體中構建一個子類物件從而實現對目標物件的擴充套件,Cglib代理許多AOP框架使用;
    步驟:
          應用 .jar檔案—Spring Core包中包含;
          在記憶體中動態建立目標物件的子類;
          目標物件不能是 final;
          目標物件方法時 final、static,則不會攔截;

在 Spring AOP 程式設計中:
動態代理:目標物件要實現介面,代理物件不需要實現介面(直接在代理物件中新增程式碼);
Cglib代理:目標物件不需要實現介面(代理物件實現了 MethodInterceptor 介面),(直接在代理物件中新增程式碼);
如果加入容器中的目標物件實現了介面,會自動使用JDK代理;
如果目標物件 沒有 實現介面(且目標物件不是 final),會自動使用 Cglib代理;

 
 

eg:1. 靜態代理
介面IUserDao.java中有一個方法; UserDao.java是目標物件:實現IUserDao介面,實現裡面的方法;想新增事務,若直接在目標物件中新增, 這樣不好,所以要新增一個代理物件UserDaoProxy; UserDaoProxy.java是代理物件,要實現與目標物件 UserDao一樣的介面 IUserDao。代理物件中新增目標物件,新增介面,因為目標物件實現了介面,這樣可以通過新增介面來新增實現類,來呼叫目標物件中的方法,在呼叫方法前後新增事務即可;)

1.1 IUserDao.java(介面)

public interface IUserDao{
	public void save() throws Exception;
}

1.2 UserDao.java(目標物件)
(UserDao.java中實現IUserDao介面,實現裡面的方法;想新增事務,若直接在目標物件中新增, 這樣不好)

public class UserDao implements IUserDao{
	public void save() throws Exception{
		//System.out.println("----開始事務----");
		System.out.println("----資料已儲存----");
		//System.out.println("----提交事務----");
	}
}

1.3 UserDaoProxy.java(代理物件_靜態代理)
(代理物件(UserDaoProxy)要實現與目標物件(UserDao)一樣的介面(IUserDao)。代理物件中新增目標物件,新增介面,因為目標物件實現了介面,這樣可以呼叫目標物件中的方法,在呼叫方法前後新增事務即可;)

public class UserDaoProxy implements IUserDao{
	private IUserDao target;
	public UserDaoProxy(IUserDao target){
		this.target=target;
	}
	public void save() throws Exception{
		System.out.println("----開始事務----");
		target.save();
		System.out.println("----提交事務----");
	}
}

1.4 Test.java
(通過介面 IUserDao 生成一個目標物件 target,生成一個代理物件 proxy將 target傳入,代理物件呼叫方法)

public class Test{
	public static void main(String[] args) {
		IUserDao target=new UserDao();//目標物件
		UserDaoProxy proxy=new UserDaoProxy(target);//代理物件
		try{
			proxy.save();
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}

執行結果:
在這裡插入圖片描述
eg:2. 動態代理
(若介面中增加方法,目標物件實現了介面所以也要增加方法,但代理物件中不需要再增加方法;直接在測試類中呼叫增加的方法即可)
2.1 IUserDao.java(介面)

public interface IUserDao{
	public void save() throws Exception;
	
	public void update() throws Exception;
}

2.2 UserDao.java(目標物件)
(UserDao.java中實現IUserDao介面,實現裡面的方法;想新增事務,若直接在目標物件中新增, 這樣不好)

public class UserDao implements IUserDao{
	public void save() throws Exception{
		//System.out.println("----開始事務----");
		System.out.println("----資料已儲存----");
		//System.out.println("----提交事務----");
	}
	
	public void update() throws Exception{
		System.out.println("----資料已修改----");
	}
}

2.3 ProxyFactory.java
(代理工廠 ProxyFactory中有目標物件,private IUserDao target,因為目標物件所實現的介面不止有IUserDao,IUserDao換成Object會更好;目標物件要實現介面,代理物件不需要實現介面(直接在代理物件中新增程式碼));

public class ProxyFactory{
	private Object target;//目標物件
	public ProxyFactory(Object target){
		this.target=target;
	}
	public Object getProxyInstance(){//生成代理物件
		Proxy.newProxyInstance(
			target.getClass().getClassLoader(),
			target.getClass().getInterfaces(),
			new InvocationHandler(){
				public Object invoke(Object proxy,Method method,Object[] args) throws Exception{
					System.out.println("開始事務");
					Object result=method.invoke(target,args);//呼叫目標物件方法
					System.out.println("提交事務");
					return result;
				}
			}
		);
	}
}

2.4 Test.java

public class Test{
	public static void main(String[] args) {
		//目標物件
		UserDao target=new UserDao();
		//代理物件
		IUserDao proxy=(IUserDao)new ProxyFactory(target).getProxyInstance();
		//執行
		try{
			proxy.save();
			
			proxy.update();
		}catch(Exception e){
			e.printStackTrace();
		}
		System.out.println(proxy.getClass());
	}
}

執行結果:
在這裡插入圖片描述
介面中新增方法後執行結果:
在這裡插入圖片描述
eg:3. Cglib 代理
3.1 IUserDao.java(介面)

public interface IUserDao{
	public void save() throws Exception;
	public void update() throws Exception;
}

3.2 UserDao.java(目標物件)
(UserDao.java不需要實現IUserDao介面)

public class UserDao{ //不能final(public final class UserDao)
	public void save() throws Exception{ //不能是final、static(public final、static void save())
		System.out.println("----資料已儲存----");
	}
	public void update() throws Exception{
		System.out.println("----資料已修改----");
	}
}

3.3 ProxyFactory.java
(代理工廠 ProxyFactory中有目標物件,private IUserDao target,因為目標物件所實現的介面不止有IUserDao,IUserDao換成Object會更好;)

/*Cglib子類代理工廠(對UserDao在記憶體中建立一個代理)*/
public class ProxyFactory implements MethodInterceptor{
	private Object target;//目標物件
	public ProxyFactory(Object target){
		this.target=target;
	}

	//代理物件
	public Object getProxyInstance(){
		//1.工具類
		Enhancer en=new Enhancer();
		//2.設定父類
		en.setSuperclass(target.getClass());
		//3.設定回撥方法(呼叫下方的intercept方法)
		en.setCallback(this);
		//4.建立代理物件
		return en.create();
	}
	public Object intercept(Object arg0,Method arg1,Object[] arg2,MethodProxy arg3) throws Throwable{
		System.out.println("開啟事務------");
		//執行目標物件的方法
		Object resultValue=method.invoke(target,arg2);
		System.out.println("結束事務");
		return resultValue;
	}
}

3.4 Test.java

public class Test{
	public static void main(String[] args) {
		//目標物件
		UserDao target=new UserDao();
		//代理物件
		IUserDao proxy=(IUserDao)new ProxyFactory(target).getProxyInstance();
		//執行
		try{
			proxy.save();
		}catch(Exception e){
			e.printStackTrace();
		}
		System.out.println(proxy.getClass());
	}
}

執行結果:
在這裡插入圖片描述
目標物件 UserDao 是final的執行結果:
(目標物件不能是 final)
會報錯
目標物件中的方法 save 是final、static的執行結果:
(目標物件的方法如果是 final、static,則不會攔截(沒有“開始事務、結束事務”))
在這裡插入圖片描述