天天看點

java spring AOP

AOP(Aspect Oriented Programming):面向切面程式設計

一、AOP的思想

正常的傳統的程式執行流程都是縱向執行流程,AOP(面向切面程式設計)在原有的縱向執行流程中添加橫切面。

二、AOP的優點

1、AOP的使用不需要修改原有程式代碼,

2、具有高擴充性,

3、原有的功能相當于釋放了部分邏輯,讓職責更加明确。

三、面向切面程式設計到底是什麼?

在程式原有縱向執行流程中,針對某個或某一些方法添加通知,形成橫切面過程就叫做面向切面程式設計

四、常用的概念

java spring AOP

編号2:切點 需要添加額外功能的方法,

編号3:前置通知 在切點之前執行的功能,before advice,

編号4:後置通知 在切點之後執行的功能,after advice,

異常通知: 如果切點執行過程中出現異常,會觸發異常通知 throws advice,

編号1:切面 所有功能總稱為切面,

織入: 把切面嵌入到原有功能的過程叫做織入。

五、spring 提供了2種AOP實作方式

1、Schema-based

(1)每一個通知都需要實作接口或者類,

(2)配置spring配置檔案時,切面在aop:config标簽下配置。

2、AspectJ

(1)每個通知不需要實作接口或類,

(2)配置spring配置檔案時切面在aop:config的子标簽aop:aspect下配置。

六、實作

方式一:Schema-based方式

1、導包

aopalliance-1.0.jar

aspectjrt-1.8.10.jar

aspectjweaver-1.8.10.jar

commons-logging-1.1.1.jar

spring-aop-4.3.10.RELEASE.jar

spring-beans-4.3.10.RELEASE.jar

spring-context-4.3.10.RELEASE.jar

spring-core-4.3.10.RELEASE.jar

spring-expression-4.3.10.RELEASE.jar

2.建立通知類

2.1 建立前置通知類

2.1.1 arg0 切點方法對象 Method 對象

2.1.2 arg1 切點方法參數

2.1.3 arg2 切點在哪個對象中

public class MyBeforeAdvice implements MethodBeforeAdvice{
    @Override
    public void before(Method arg0, Object[] arg1, Object arg2){
        System.out.println("執行前置通知");
    }
}

           

2.2 建立後置通知類

2.2.1 arg0 切點方法傳回值

2.2.2 arg1 切點方法對象

2.2.3 arg2 切點方法參數

2.2.4 arg3 切點方法所在類的對象

public class MyAfterAdvice implements AfterReturningAdvice{
    @Override
public void afterReturning(Object arg0, Method arg1,Object[] arg2, Object 
arg3){
        System.out.println("執行後置通知");
    } 
}

           

3.配置 spring 配置檔案

3.1 引入 aop 命名空間

3.2 配置通知類的

3.3 配置切面

3.4 * 通配符,比對任意方法名,任意類名,任意一級包名

3.5 如果希望比對任意方法參數 (…)

<?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/sc hema/beans
		http://www.springframework.org/schema/beans/spring-be ans.xsd
		http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-aop. xsd">
	<!-- 配置通知類對象,在切面中引入 -->
	<bean id="mybefore" class="com.woniuxy.advice.MyBeforeAdvice"></bean>
	<bean id="myafter" class="com.woniuxy.advice.MyAfterAdvice"></bean>
	<!-- 配置切面 -->
	<aop:config>
		<!-- 配置切點 -->
		<aop:pointcut expression="execution(* com.woniuxy.test.Demo.demo2())"
			id="mypoint" />
		<!-- 通知 -->
		<aop:advisor advice-ref="mybefore" pointcut-ref="mypoint" />
		<aop:advisor advice-ref="myafter" pointcut-ref="mypoint" />
	</aop:config>
	
	<!-- 配置 Demo 類,測試使用 -->
	<bean id="demo" class="com.woniuxy.test.Demo"></bean>
</beans>



           

4.編寫測試代碼

public class Test {
    public static void main(String[] args){
        // Demo demo = new Demo();
        // demo.demo1();
        // demo.demo2();
        // demo.demo3();
        ApplicationContext ac = 
new ClassPathXmlApplicationContext("applicationContext.xml");
        Demo demo = ac.getBean("demo", Demo.class);
        demo.demo1();
        demo.demo2();
        demo.demo3();
    }
}


           

通知類型:

前置通知

後置通知

環繞通知

異常通知

傳回後通知

方式二:Aspectj方式

1、IUserDao

//IUserService接口
public interface IUserService {
	void save();
}
           

2、UserService實作類(實作接口)

UserDao實作類(有實作接口)

@Component   // 加入容器
public class UserServiceImpl implements IUserService{
	@Override
	public void save() {
		System.out.println("-----調用DAO核心業務:儲存!!!------");
	}
}
           

3、切面類

public class Aop {
	public void begin() {
		System.out.println("開始事務/異常");
	}
	public void after() {
		System.out.println("無論程式正常執行還是有異常,隻要程式執行完就會執行這個通知");
	}
	public void afterReturning() {
		System.out.println("程式執行到return,代表程式正常執行完畢,是以隻有程式正常執行完,沒有異常才會執行這個通知");
	}
	public void afterThrowing() {
		System.out.println("afterThrowing()");
	}
	public void around(ProceedingJoinPoint pjp) throws Throwable {
		System.out.println("環繞前....");
		pjp.proceed(); // 執行目标方法
		System.out.println("環繞後....");
	}
}
           

4、spring-context.xml

<!-- dao執行個體 -->
<bean id="userService" class="com.my.g_aop_xml.UserService"></bean>
<bean id="otherService" class="com.my.g_aop_xml.OtherService"></bean>
	
<!-- 切面類 -->
<bean id="aop" class="com.my.g_aop_xml.Aop"></bean>

<!-- AOP配置 -->
<aop:config>
	<!-- 定義一個切入點表達式 -->
	<aop:pointcut expression="execution(* com.my.g_aop_xml.*.*(..))" id="pt"/>
	<!-- 配置切面 -->
	<aop:aspect ref="aop">
		<!-- 前置通知 -->
		<aop:before method="begin" pointcut-ref="pt"/>
		<!-- 後置通知 -->
		<aop:after method="after" pointcut-ref="pt"/>
		<!-- 環繞通知 -->
		<aop:around method="around" pointcut-ref="pt"/>
		<!-- 傳回後通知 -->
		<aop:after-returning method="afterReturning" pointcut-ref="pt"/>
		<!-- 異常通知 -->
		<aop:after-throwing method="afterThrowing" pointcut-ref="pt"/>
	</aop:aspect>
</aop:config>
           

5、測試類

public static void main(String[] args) {
	ApplicationContext ac = 
new ClassPathXmlApplicationContext("com/my/f_aop_anno/bean.xml");
		
	//目标對象有實作接口,spring會預設采用JDK代理
	IUserService userService = (IUserService) ac.getBean("userService");
	System.out.println(userService.getClass());//$Proxy001  
	userDao.save();
}
           

注解方式實作Aspectj

1、IUserService接口

public interface IUserService {
	void save();
}
           

2、UserDao實作類(實作接口)

@Component   // 加入容器
public class UserServiceImpl implements IUserService{
	@Override
	public void save() {
		System.out.println("-----調用DAO核心業務:儲存!!!------");
	}
}
	
           

3、AOP切面類

@Component // 加入IOC容器
@Aspect // 指定目前類為切面類
public class Aop {
	// 指定切入點表達式:攔截哪些方法(為哪些類生成代理對象)
	@Pointcut("execution(* com.my.f_aop_anno.*.*(..))")
	public void pointCut_() {
	}
//JoinPoint對象封裝了SpringAop中切面方法的資訊,在切面方法中添加JoinPoint參數,就
//可以擷取到封裝了該方法資訊的JoinPoint對象
	@Before("pointCut_()")
	public void begin(JoinPoint point) {
		System.out.println("開始事務/異常");
        //方法簽名擷取封裝了署名資訊的對象,在該對象中可以擷取到目标方法名,所屬類的Class
//等資訊
		MethodSignature methodSignature = 
(MethodSignature)point.getSignature();
		System.out.println("調用"+methodSignature.getMethod().getName()+"方法");
	}
	// 後置/最終通知:在執行目标方法之後執行 【無論是否出現異常最終都會執行】
	@After("pointCut_()")
	public void after() {
		System.out.println("送出事務/關閉");
	}
	// 傳回後通知: 在調用目标方法結束後執行 【出現異常不執行】
	@AfterReturning("pointCut_()")
	public void afterReturning() {
		System.out.println("afterReturning()");
	}
	// 異常通知: 當目标方法執行異常時候執行此關注點代碼
	@AfterThrowing("pointCut_()")
	public void afterThrowing() {
		System.out.println("afterThrowing()");
	}
	// 環繞通知:環繞目标方式執行
    //ProceedingJoinPoint對象是JoinPoint的子接口,該對象隻用在@Around的切面方法中
	@Around("pointCut_()")
	public void around(ProceedingJoinPoint pjp) {
		System.out.println("環繞前....");
        try{
		    pjp.proceed(); // 執行目标方法
        }catch (Throwable exception){

        }finaly{

        }
		System.out.println("環繞後....");
	}
}
           

4.4、bean.xml配置檔案

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    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/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
	
	<!-- 開啟spring注解掃描 -->
	<context:component-scan 
base-package="com.my.f_aop_anno"></context:component-scan>
	
	<!-- 開啟aop注解方式 -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
           

4.5、測試類

public static void main(String[] args) {
		ApplicationContext ac = 
new ClassPathXmlApplicationContext("com/my/f_aop_anno/bean.xml");
		
		//目标對象有實作接口,spring會預設采用JDK代理
		IUserService userService = 
(IUserService) ac.getBean("userDaoServiceImpl");
		System.out.println(userServiceImpl.getClass());//$Proxy001  
		userServiceImpl.save();
		
		//目标對象沒有實作接口,spring預設采用cglib代理
		OtherServiceImpl otherServiceImpl = 
(OtherServiceImpl)ac.getBean("otherServiceImpl");
		System.out.println(otherServiceImpl.getClass());
		otherImpl.save();
}
           

4.6、OtherService類(未實作接口)

@Component
public class OtherServiceImpl {
	public void save() {
		System.out.println("執行沒有接口的dao");
	}
}
           

補充:aop是基于代理模式實作的,spring中本身就嵌入了CGLIB,當包含切點的類實作了接口時,aop預設使用JDK代理;當包含切點的類沒有實作接口那麼aop就使用CGLIB代理。