spring的aop

spring的aop(面向切面编程),他可以在不改变源码的情况下,对已有的方法进行增强操作。

AOP中的常用术语

Joinpoint(连接点):连接点是那些被连接到的点;在spring中,这些连接点指的是方法,因为因为 spring 只支持方法类型的连接点。
Pointcut(切入点):切入点是指要对那些连接点进行拦截的定义。
Advice(通知/增强):通知是指拦截到连接点之后所要做的事情,通知可以分为前置通知、后置通知、异常通知、最终通知和环绕通知。
Introduction(引介):引介是一种特殊的通知,在不修改类代码的前提条件下,引介可以在运行期为类动态地添加一些方法或Filed。
Target(目标对象):代理的目标对象。
Weaving(织入):指定把增强应用到目标对象来创建新的代理对象的过程。
Proxy(代理):一个类被AOP织入增强后,就会产生一个结果代理类。
Aspect(切面):他是切入点和通知(引介)的结合。

通知类型

前置通知 :在目标方法执行之前执行.。

后置通知 :在目标方法正常执行之后执行,他与异常抛出通知互斥。

环绕通知 :因为在环绕通知中可以手动调用切入点方法,所以环绕通知可以在目标方法执行前或执行后执行。

异常抛出通知:在目标方法执行出现异常的时候执行,他与后置通知互斥。

最终通知 :无论目标方法是否出现异常最终通知都会执行.

切入点表达式

在spring中,这样切入点表达式中的方法才会被增强,spring使用execution关键字来定义切入点表达式

表达式的写法:
表达式写法:
访问修饰符 返回值 包名.包名…包名.类名.方法名(参数列表)
1.全匹配方式:

	 public void com.yuxiang.service.impl.AccountServiceImpl.saveAccount()
 2.访问修饰符可以省略:
	 void com.yuxiang.service.impl.AccountServiceImpl.saveAccount()
 3.返回值可以使用*代替,表示任意符合值类型
	 * com.yuxiang.service.impl.AccountServiceImpl.saveAccount()
 4.包名可以使用*代替,表示任意包;但是有几级包,需要写几个*
	 * *.*.*.*.AccountServiceImpl.saveAccount()
 5.包名可以使用..代替,表示当前包及其子包
	 * *..AccountServiceImpl.saveAccount()
 6.类名可以使用*,表示任意类
	 * *..*.saveAccount()
 7.方法名可以使用*代替,表示任意方法
	 * *..*.*()
 8.参数类型可以指定具体类型

 		基本数据类型直接写类型名称:
	 * *..*.*(int)
 		引用类型必须写包名.类名:
	* *..*.*(java.lang.String)
 9.参数列表可以使用*代替,表示任意参数类型,但是必须有参数
	 * ..*.*(*)
 10.参数列表可以使用..代替,表示有无参数均可
	 * *..*.*(..)
 11.全通配
	 * *..*.*(..)
 ><font color =red>在实际的开发中,切入点表达式的使用一般都是对业务层的方法增强
    * com.yuxiang,service,impl.*.*(..)

Java代码

基于xml配置文件

public interface IAccountService {
	void saveAccount();
	void updateAccount(int i);
	void deleteAccount();
}
import com.yuxiang.service.IAccountService;

public class AccountServiceImpl implements IAccountService {

	@Override
	public void saveAccount() {
		System.out.println("保存账号");
		
	}

	@Override
	public void updateAccount(int i) {
		System.out.println("更新账户");
	}

	@Override
	public void deleteAccount() {
		System.out.println("删除账户");		
	}

}

import org.aspectj.lang.ProceedingJoinPoint;

public class Logger {

	public void beforePrintLog(){
		System.out.println("Loggle类中的beforePrintLog方法开始记录日志      前置通知");
	}
	
	public void beforeReturningPrintLog(){
		System.out.println("Loggle类中的beforePrintLog方法开始记录日志       后置通知");
	}
	
	public void beforeThrowingPrintLog(){
		System.out.println("Loggle类中的beforePrintLog方法开始记录日志      异常通知");
	}
	public void afterPrintLog(){
		System.out.println("Loggle类中的beforePrintLog方法开始记录日志     最终通知");
	}
	/**
	 * 环绕通知:他是spring提供的一种手动控制何时执行切入点方法的方式
	 * 	配置spring的环绕通知后,在执行切入点方法时,只执行类环绕通知,切入点方法并没有执行,
	 *  在动态代理中,invoke方法中有一个明确调用切入点方法的代码,而spring的环绕通知中没有调用切入点方法;
	 *  所以在spring的环绕通知中,如果想在环绕通知执行时也执行切入点方法,就必须在环绕通知中明确调用切入点方法。
	 *  在spring框架中,ProceedingJoinPoint接口可以在环绕通知中通过proceed()方法来明确调用切入点方法。
	 */
	public void aroundPrintLog(ProceedingJoinPoint pjp){
		System.out.println("Loggle类中的beforePrintLog方法开始记录日志     最终通知");
		Object reValue = null;
		
		try {
			System.out.println("前置通知");
			//获取方法所需的参数
			Object[] args = pjp.getArgs();
			//调用切入点方法
			reValue = pjp.proceed(args);
			System.out.println("后置通知");
		} catch (Throwable e) {
			System.out.println("异常通知");
			e.printStackTrace();
		}finally{
			System.out.println("最终通知");
		}
	}
}
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Client {
	public static void main(String[] args) {
	ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
	IAccountService accountService = ac.getBean("accountService", IAccountService.class);
	accountService.saveAccount();
	
	}

}
<?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-4.3.xsd">
						   
						   
	<bean id="accountService" class="com.yuxiang.service.impl.AccountServiceImpl"></bean>					   
		
	<!-- spring基于xml的aop配置步骤:
			前期准备:
				1.拷贝aopjar包
				2.在配置文件中导入aop约束
			配置步骤:
				1.把通知bean也交给spring来管理
				2.使用aop名称空间下的aop:config标签开始aop的配置
				3.使用aop:aspect标签,开始配置切面
	 				id:用于给切面提供唯一标识
	 				ref:用于引用通知bean的id
	 			4.使用aop:before标签配置前置通知
	 				method:指定通知类中那个方法是前置通知		
	 				pointcut:用于指定切入点表达式
	 					切入点表达式:
	 						关键字:execution(表达式)
	 					表达式写法:
	 						访问修饰符  返回值  包名.包名...包名.类名.方法名(参数列表)
	
	 -->							
		
		
	<bean id="logger" class="com.yuxiang.utils.Logger"></bean>
		
	<aop:config>
		<!-- 使用aop:pointcut标签来定义切入点表达式,表示他的位置只能放在aop:aspect标签切面 -->
		<aop:pointcut expression="execution(* com.yuxiang.service.impl.*.*(..))" id="mycut"/>
		
		<!-- 配置切面 -->
		<aop:aspect id="logAdvice" ref="logger">
			<!-- 配置前置通知:他永远都会在切入点方法之前执行 -->
			<!-- <aop:before method="beforePrintLog" pointcut="execution(* *..*.*())"/> -->
			
			<!-- 配置后置通知:当切入点方法正常执行后,后置通知才会执行,并且他和异常通知是互斥的 -->
			<!-- <aop:after-returning method="beforeReturningPrintLog" pointcut="execution(* *..*.*())"/> -->
			
			<!-- 配置异常通知:当切入点方法执行异常时,异常通知才会执行,并且他与后置通知是互斥的 -->
			<!-- <aop:after-throwing method="beforeThrowingPrintLog" pointcut="execution(* *..*.*())"/> -->
			
			<!-- 配置最终通知:无论切入点方法是否正常执行,最终通知都会执行 -->
			<!-- <aop:after method="afterPrintLog" pointcut="execution(* *..*.*())"/> -->

			<!-- 配置环绕通知 -->
			<aop:around method="aroundPrintLog" pointcut-ref="mycut" />
		</aop:aspect>
		<!-- 环绕通知一般不和其他通知结合使用,常单独使用-->
		
	</aop:config>		
</beans>

纯注解

public interface IAccountService {
	void saveAccount();
	void updateAccount(int i);
	void deleteAccount();
}
import com.yuxiang.service.IAccountService;

@Service("accountService")
public class AccountServiceImpl implements IAccountService {

	@Override
	public void saveAccount() {
		System.out.println("保存账号");
		
	}

	@Override
	public void updateAccount(int i) {
		System.out.println("更新账户");
	}

	@Override
	public void deleteAccount() {
		System.out.println("删除账户");		
	}

}

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component("logger")
//把当前类配置成一个切面
@Aspect
public class Logger {
	//配置切入点表达式
	@Pointcut("execution(* com.yuxiang.service.impl.*.*(..))")
	private void pt1(){};
	@Before(value="pt1()")
	public void beforePrintLog(){
		System.out.println("Loggle类中的beforePrintLog方法开始记录日志      前置通知");
	}
	
	@AfterReturning(value="pt1()")
	public void afterReturningPrintLog(){
		System.out.println("Loggle类中的beforePrintLog方法开始记录日志       后置通知");
	}
	
	@AfterThrowing(value="pt1()")
	public void afterThrowingPrintLog(){
		System.out.println("Loggle类中的beforePrintLog方法开始记录日志      异常通知");
	}
	
	@After(value="pt1()")
	public void afterPrintLog(){
		System.out.println("Loggle类中的beforePrintLog方法开始记录日志     最终通知");
	}
	/**
	 * 环绕通知:他是spring提供的一种手动控制何时执行切入点方法的方式
	 * 	配置spring的环绕通知后,在执行切入点方法时,只执行类环绕通知,切入点方法并没有执行,
	 *  在动态代理中,invoke方法中有一个明确调用切入点方法的代码,二spring的环绕通知中没有调用切入点方法;
	 *  所以在spring的环绕通知中,如果想在环绕通知执行时也执行切入点方法,就不行在环绕通知中明确调用切入点方法。
	 *  在spring框架中,ProceedingJoinPoint接口可以在环绕通知中通过proceed()方法来明确调用切入点方法。
	 */
	
	
	@Around(value="pt1()")
	public void aroundPrintLog(ProceedingJoinPoint pjp){
		System.out.println("Loggle类中的beforePrintLog方法开始记录日志     最终通知");
		Object reValue = null;
		
		try {
			System.out.println("前置通知");
			//获取方法所需的参数
			Object[] args = pjp.getArgs();
			//调用切入点方法
			reValue = pjp.proceed(args);
			System.out.println("后置通知");
		} catch (Throwable e) {
			System.out.println("异常通知");
			e.printStackTrace();
		}finally{
			System.out.println("最终通知");
		}
	
	
	}
	
}

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

//指定当前类是spring的配置类
@Configuration
//指定创建容器时,要扫描的包
@ComponentScan("com.yuxiang")
//开启spring对注解AOP的支持
@EnableAspectJAutoProxy
public class SpringConfiguration {

}
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Client {
	public static void main(String[] args) {
	ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
	IAccountService accountService = ac.getBean("accountService", IAccountService.class);
	accountService.saveAccount();
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值