天天看點

Spring架構學習筆記2---AOP1、AOP介紹2、手動方式3、AOP聯盟通知類型4、spring編寫代理:半自動5、spring的AOP程式設計:全自動【掌握】

目錄

1、AOP介紹

1.1、什麼是Aop

1.2、AOP的的實作原理

1.3、AOP術語【掌握】

2、手動方式

2.1、JDK動态代理

2.1.1、目标類 

2.1.2、切面類:用于存儲通知MyAspect

2.1.3、工廠類:編寫工廠生成代理

2.1.4、測試類:

2.1.5、測試結果

2.1.6、jdk動态代理現象(設定斷點檢視)

2.2、CGLIB位元組碼增強

2.2.1、目标類(不需要實作接口,直接寫類就行)

2.2.2、切面類

2.2.3、工廠類

2.2.4、測試類

2.2.5、測試結果

2.2.6、cglib位元組碼增強現象

3、AOP聯盟通知類型

4、spring編寫代理:半自動

4.1、目标類

4.2、切面類

4.3、配置檔案

4.4、測試類

4.5、測試結果

5、spring的AOP程式設計:全自動【掌握】

5.1、目标類

5.2、切面類

5.3、配置檔案

5.4、測試類

5.5、測試結果

6、aspect

1、AOP介紹

1.1、什麼是Aop

  • AOP:是Aspect Oriented Programming的縮寫,意思是面向切面程式設計,通過預編譯方式和運作期動态代理實作程式功能的統一維護的一種技術。AOP是OOP(面向對象程式設計)的延續,是軟體開發中的一個熱點,也是spring架構中的一個重要内容,是函數式程式設計的一種衍生範型。利用AOP可以對業務邏輯的各個部分進行隔離,進而是的業務邏輯各個部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。
  • AOP采取橫向抽取機制,取代了傳統縱向繼承體系重複性代碼
  • 經典的應用:性能監視、事務管理、安全檢查、緩存、日志等
  • Spring AOP使用純Java實作,不需要專門的編譯過程和類加載器,在運作期通過代理方式向目标類織入增強代碼
  • AspectJ是一個基于Java語言的AOP架構,spring2.0開始,Spring AOP引入對Aspect的支援,AspectJ擴充了Java語言,提供了專門的編譯器,在編譯時提供橫向代碼的織入

1.2、AOP的的實作原理

  • aop底層将采用代理機制進行實作
  • 接口 + 實作類:spring采用的是jdk的動态代理Proxy
  • 實作類:spring采用cglib位元組碼增強

1.3、AOP術語【掌握】

  • target:目标類,需要被代理的類
  • Jointpoint(連接配接點):是指那些可能被攔截的方法。例如目标類的所有方法
  • PointCut(切入點):已經被增強的連接配接點。(切入點是連接配接點的子集)
  • advice:通知 / 增強,增強的代碼,例如
  • weaving(織入):是指把增強advice應用到目标對象target來建立新的代理對象proxy的過程
  • proxy(代理類):
  • Aspect(切面):是切入點pointcut和通知advice· 的結合(一個線是一個特殊的面,即一個切入點和一個通知能組成一個特殊的面)
Spring架構學習筆記2---AOP1、AOP介紹2、手動方式3、AOP聯盟通知類型4、spring編寫代理:半自動5、spring的AOP程式設計:全自動【掌握】

2、手動方式

2.1、JDK動态代理

  • JDK動态代理是對"裝飾者"設計模式的簡化。使用前提:必須有接口
  • jdk動态代理:
Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)
           
三個參數:
  • 參數1:loader,類加載器,動态代理類運作時建立,任何類都需要類加載器将其加載到記憶體

            方式1:目前類.Class().getClassLoader()

            方式2:目标類執行個體.getClass().getClassLoader()

  • 參數2 : interfaces,代理類需要實作的所有接口

            方式1:目标類執行個體.getClass().getInterfaces()

            方式2:new Class[]{目标類.Class}

  • 參數3 : InvocationHandler 處理類,接口,必須進行實作,一般情況下,采用匿名内部類的方式。該接口中提供invoke方法,代理類的每一個方法執行的時,都将調用一次invoke方法,也有三個參數:

                    參數1:Object proxy:代理對象

                    參數2:Method method :代理對象目前執行的方法的描述(反射)

                                     執行方法名:method.getName()

                                     執行方法:method.invoke(對象 實際參數)

                    參數3:Object[] args:方法實際參數

2.1.1、目标類 

//接口
public interface UserService {
	public void addUser();
	public void updateUser();
	public void deleteUser();
}
//實作類
public class UserServiceImpl implements UserService {
	@Override
	public void addUser() {
		// TODO 自動生成的方法存根
		System.out.println("add a user");
	}
	@Override
	public void updateUser() {
		// TODO 自動生成的方法存根
		System.out.println("upadte user");
	}
	@Override
	public void deleteUser() {
		// TODO 自動生成的方法存根
		System.out.println("delete a user");
	}
}
           

2.1.2、切面類:用于存儲通知MyAspect

public class MyAspect {
	public void before() {
		System.out.println("before-method");
	}
	public void after() {
		System.out.println("after-method");
	}
}
           

2.1.3、工廠類:編寫工廠生成代理

public class MyBeanFactory {
	public static UserService createUserService() {
		//1、目标類
		final UserService userService = new UserServiceImpl();
		//2、切面類
		final MyAspect myAspect = new MyAspect();
		/*3、代理類:将目标類(切入點)和切面類(通知)結合--->切面
		 * Proxy.newProxyInstance
		 *      參數1:loader,類加載器,動态代理類運作時建立,任何類都需要類加載器将其加載到記憶體
		 *      	一般情況下:目前類.class.getClassLoader()
		 *      			  目标類執行個體.getClass().getClassLoader()
		 *      參數2:interfaces 代理類需要實作的所有接口
		 *      	方式1:目标類執行個體.getClass().getInterfaces();//注意:隻能獲得自己的接口,不能獲得父元素的接口
		 *      	方式2:new Class[]{UserService.class}
		 *      	例如:jdbc驅動--> DriverManager  獲得接口Connection
		 *      
		 *      參數3:InvocationHandler 處理類,接口,必須進行實作類,一般情況下,采用匿名内部類的方式
		 *      	提供invoke方法,代理類的每一個方法執行的時候,都将調用一次invoke
		 *          參數31:Object proxy :代理對象
		 *          參數32:Method method :代理對象目前執行的方法的描述(反射)
		 *          	執行方法名:method.getName()
		 *          	執行方法:method.invoke(對象 實際參數)
		 *          參數33:Object[] args : 方法實際參數
		 */
		UserService proxService = (UserService)Proxy.newProxyInstance(
				MyBeanFactory.class.getClassLoader(), 
				userService.getClass().getInterfaces(), new InvocationHandler() {
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						/*
						 * Object proxy:代理對象
						 * Method method:代理對象目前執行的方法描述
						 * Object[] args:方法實際參數
						 */
						//将目标類和切面類結合
						//前方法執行 
						System.out.println("**************************");
						myAspect.before();
						//執行目标類的方法
						String str = method.getName();
						System.out.println("目前執行的方法名是:" + str);
						Object obj = method.invoke(userService, args);
						//後方法執行
						myAspect.after();
						return null;
					}
				});
		return proxService;//傳回代理對象
	}
}
           

2.1.4、測試類:

public class TestJDK {
	@Test
	public void demo01() {
		UserService userService = MyBeanFactory.createUserService();
		userService.addUser();
		userService.updateUser();
		userService.deleteUser();
	}
}
           

2.1.5、測試結果

Spring架構學習筆記2---AOP1、AOP介紹2、手動方式3、AOP聯盟通知類型4、spring編寫代理:半自動5、spring的AOP程式設計:全自動【掌握】

2.1.6、jdk動态代理現象(設定斷點檢視)

Spring架構學習筆記2---AOP1、AOP介紹2、手動方式3、AOP聯盟通知類型4、spring編寫代理:半自動5、spring的AOP程式設計:全自動【掌握】

2.2、CGLIB位元組碼增強

  • 沒有接口,隻能有實作類
  • 采用位元組碼增強架構,cglib,在運作時建立目标類的子類(繼承),進而對目标類進行增強
  • 導入jar包

               方式1:核心:hibernate-distribution-3.6.10.Final\lib\bytecode\cglib-2.2.jar

                            依賴:struts-2.3.15.3\apps\struts2-blank\WEB-INF\lib\asm.3.3.jar

               方式2:spring-core.jar已經整合了以上兩個内容

2.2.1、目标類(不需要實作接口,直接寫類就行)

public class UserServiceImpl {
	public void addUser() {
		System.out.println("add a user");
	}
	public void updateUser() {
		System.out.println("update a user");
	}
	public void deleteUser() {
		System.out.println("delete a user");
	}
}
           

2.2.2、切面類

public class MyAspect {
	public void before() {
		System.out.println("before-method");
	}
	public void after() {
		System.out.println("after-method");
	}
}
           

2.2.3、工廠類

public class MyBeanFactory {
	public static UserServiceImpl createUserService(){
		//1.目标類
		final UserServiceImpl userService = new UserServiceImpl();
		//2.切面類
		final MyAspect myAspect = new MyAspect();
		//3.代理類  采用cglib 底層建立目标類的子類
		//3.1 核心類
		Enhancer enhancer = new Enhancer();
		//3.2 确定父類
		enhancer.setSuperclass(userService.getClass());
		//3.3 設定回調函數 MethodInterceptor接口等效 jdk InvocationHandler接口
		enhancer.setCallback(new MethodInterceptor() {
		@Override
		public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
			/*
			 * intercept()等效于jdk中的 invoke()方法
			 * 參數1:代理對象
			 * 參數2:代理對象目前執行的方法描述(反射)
			 * 參數3:方法實際參數
			 * 參數4:方法的代理,
			 */
			System.out.println("****************************");
			//前方法
			myAspect.before();
			//執行目标類的方法
			Object obj = method.invoke(userService, args);
			//執行代理類的父類,執行目标類(目标類和代理類就是父子關系)
			methodProxy.invokeSuper(proxy, args);
			//後方法
			myAspect.after();
			return obj;
			}
			
		});
		//3.4 建立代理
		UserServiceImpl proxyService = (UserServiceImpl)enhancer.create();
		return proxyService;
	}
}
           

2.2.4、測試類

public class TestCglib {
	@Test
	public void demo() {
		UserServiceImpl userService = MyBeanFactory.createUserService();
		userService.addUser();
		userService.updateUser();
		userService.deleteUser();
	}
}
           

2.2.5、測試結果

Spring架構學習筆記2---AOP1、AOP介紹2、手動方式3、AOP聯盟通知類型4、spring編寫代理:半自動5、spring的AOP程式設計:全自動【掌握】

2.2.6、cglib位元組碼增強現象

Spring架構學習筆記2---AOP1、AOP介紹2、手動方式3、AOP聯盟通知類型4、spring編寫代理:半自動5、spring的AOP程式設計:全自動【掌握】

3、AOP聯盟通知類型

  • AOP聯盟為通知Advice定義了org.aopalliance.aop.Advice
  • Spring按照通知Advice在目标類方法的連接配接點位置,可以分為5類

1、前置通知 org.springframework.aop.MethodBeforeAdvice

  • 在目标方法執行前實施增強

2、後置通知 org.springframework.aop.AfterReturningAdvice

  • 在目标方法執行後實施增強

3、環繞通知 org.aopalliance.intercept.MethodInterceptor

  • 在目标方法執行前後實施增強(必須手動執行目标方法)
//環繞通知
try{
    //前置方法:可以阻止目标方法的執行
    //執行目标方法
    //後置通知:可以獲得目标方法的傳回值
}catch{
    //抛出異常通知
}
           

4、異常抛出通知 org.springframework.aop.ThrowsAdvice

  • 在方法抛出異常後實施增強(方法可以為前置、後置、環繞以及目标)

5、引介通知 org.springframework.aop.IntroductionInterceptor

  • 在目标類中添加一些新的方法和屬性

4、spring編寫代理:半自動

  • 讓spring建立代理對象,從spring容器中手動的擷取代理對象
  • 導入jar包

       核心:4+1(core、context、beans、expression)+logging

       AOP:AOP聯盟(規範)、spring aop(實作)

AOP聯盟:
Spring架構學習筆記2---AOP1、AOP介紹2、手動方式3、AOP聯盟通知類型4、spring編寫代理:半自動5、spring的AOP程式設計:全自動【掌握】
六個jar包:
Spring架構學習筆記2---AOP1、AOP介紹2、手動方式3、AOP聯盟通知類型4、spring編寫代理:半自動5、spring的AOP程式設計:全自動【掌握】

4.1、目标類

public interface UserService {
	public void addUser();
	public void updateUser();
	public void deleteUser();
}
public class UserServiceImpl implements UserService {
	@Override
	public void addUser() {
		System.out.println("add a user");
	}
	@Override
	public void updateUser() {
		System.out.println("update a user");
	}
	@Override
	public void deleteUser() {
		System.out.println("delete a user");
	}
}
           

4.2、切面類

/*
 * 切面類中确定通知,需要實作不同的接口,接口就是規範,進而就确定的方法的名稱
 * 采用的是“環繞通知”:需要實作MethodInterceptor接口
 * 環繞通知 必須手動的執行目标方法
 */
public class MyAspect implements MethodInterceptor{

	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		// TODO 自動生成的方法存根
		System.out.println("前");
		//手動執行目标方法:相當于通知spring執行目标方法
		Object obj = mi.proceed();
		System.out.println("後");
		return obj;
	}

}
           

4.3、配置檔案

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	<!-- 建立目标類 -->
	<bean id = "userServiceId" class = "test_b_factory_bean.UserServiceImpl"></bean>
	<!-- 建立切面類 -->
	<bean id = "myAspectId" class = "test_b_factory_bean.MyAspect"></bean>
	<!-- 建立代理類
		*使用bean FactoryBean,底層調用getObject() 傳回特殊bean
		*ProxyFactoryBean用于建立代理工廠bean,生成特殊的代理對象
		*再在該bean中配置
			*interfaces:确定接口們,通過<array>可以設定多個值,隻有一個值的時候 使用value,值就是接口的全限定名
			*target:确定目标類
			*interceptorNames:通知切面類的名稱,采用String[] 設定一個值采用的是value,設定多個值采用<array>
			*optimize:強制使用cglib
		底層機制:如果目标類有接口,采用jdk動态代理
				如果沒有接口,采用cglib
				如果聲明是optimize = true,無論是否有接口,都采用cglib
	 -->
	<bean id = "proxyUserServiceId" class = "org.springframework.aop.framework.ProxyFactoryBean">
		<property name = "interfaces" value = "test_b_factory_bean.UserService"></property>
		<property name = "target" ref = "userServiceId"></property>
		<property name = "interceptorNames" value = "myAspectId"></property>
		<property name = "optimize" value = "true"></property>
	</bean>	
</beans>
           

4.4、測試類

public class TestFactoryBean {
	@Test
	public void demo() {
		String xmlPath = "test_b_factory_bean/applicationContext.xml";
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
		//在這裡獲得代理類
		UserService userService = applicationContext.getBean("proxyUserServiceId",UserService.class);
		userService.addUser();
		userService.updateUser();
		userService.deleteUser();
	}
}
           

4.5、測試結果

Spring架構學習筆記2---AOP1、AOP介紹2、手動方式3、AOP聯盟通知類型4、spring編寫代理:半自動5、spring的AOP程式設計:全自動【掌握】

5、spring的AOP程式設計:全自動【掌握】

  • 從spring容器中獲得目标類,如果配置了AOP,Spring将自動的生成代理
  • 要确定目标類,aspectj切入點表達式,
  • 導入jar包
Spring架構學習筆記2---AOP1、AOP介紹2、手動方式3、AOP聯盟通知類型4、spring編寫代理:半自動5、spring的AOP程式設計:全自動【掌握】

5.1、目标類

public interface UserService {
	public void addUser();
	public void updateUser();
	public void deleteUser();
}
public class UserServiceImpl implements UserService {
	@Override
	public void addUser() {
		System.out.println("add a user");
	}
	@Override
	public void updateUser() {
		System.out.println("update a user");	
	}
	@Override
	public void deleteUser() {
		System.out.println("delete a user");	
	}
}
           

5.2、切面類

public class MyAspect implements MethodInterceptor{
	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		// TODO 自動生成的方法存根
		System.out.println("************************");
		System.out.println("前");
		Object obj = mi.proceed();
		System.out.println("後");
		return obj;
	}
}
           

5.3、配置檔案

Spring架構學習筆記2---AOP1、AOP介紹2、手動方式3、AOP聯盟通知類型4、spring編寫代理:半自動5、spring的AOP程式設計:全自動【掌握】

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    					http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/aop 
                        http://www.springframework.org/schema/aop/spring-aop.xsd"> 

    <!-- 1、目标類 -->
    <bean id = "userServiceId" class = "test_c_spring_aop.UserServiceImpl"></bean>
    <!-- 2、切面類 -->
    <bean id = "myAspectId" class = "test_c_spring_aop.MyAspect"></bean>
    <!-- 3、aop 
    3.1 導入命名空間
    3.2 使用<aop:config>進行配置
    	proxy-target-class = "true":聲明式使用cglib代理
    	有接口是jdk動态代理,沒有接口cglib代理
    	<aop:pointcut>:切入點,從目标對象獲得具體方法
    	<aop:advisor>:特殊的切面,隻有一個通知和一個切入點
    		advice-ref : 通知引用(通知在切面類中)
    		pointcut-ref :切入點引用
    3.3 切入點表達式
    	execution(* test_c_spring_aop.  *.  *     (...))
    	選擇方法     傳回值任意  包                         類名任意  方法名任意 參數任意
    -->
    <aop:config proxy-target-class = "true">
    	<aop:pointcut expression="execution(* test_c_spring_aop.*.*(..))" id="myPointCut"/>
    	<aop:advisor advice-ref="myAspectId" pointcut-ref="myPointCut"/>
    </aop:config>
</beans>
           

5.4、測試類

public class TestSpringAop {
	@Test
	public void demo() {
		String xmlPath = "test_c_spring_aop/applicationContext.xml";
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
		UserService userService = (UserService) applicationContext.getBean("userServiceId");
		userService.addUser();
		userService.updateUser();
		userService.deleteUser();
	}
}
           

5.5、測試結果

Spring架構學習筆記2---AOP1、AOP介紹2、手動方式3、AOP聯盟通知類型4、spring編寫代理:半自動5、spring的AOP程式設計:全自動【掌握】