Spring之AOP

AOP介绍

AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。
  面向切面是面向对象中的一种方式而已。在代码执行过程中,动态嵌入其他代码,叫做面向切面编程。常见的使用场景:
i :日志
ii: 事务
iii:数据库操作

AOP中关键性概念

术语说明
连接点(Joinpoint)程序执行过程中明确的点,如方法的调用,或者异常的抛出.
目标(Target)被通知(被代理)的对象
通知(Advice)在某个特定的连接点上执行的动作,同时Advice也是程序代码的具体实现,例如一个实现日志记录的代码(通知有些书上也称为处理)
代理(Proxy)将通知应用到目标对象后创建的对象(代理=目标+通知),
切入点(Pointcut)多个连接点的集合,定义了通知应该应用到那些连接点。
适配器(Advisor)适配器=通知(Advice)+切入点(Pointcut)

AOP理解图

在这里插入图片描述

通知案例

这边案例使用了几个工具类
BookBizImpl.java

package com.liuchunming.aop.biz;

public class BookBizImpl implements IBookBiz {

	public BookBizImpl() {
		super();
	}

	public boolean buy(String userName, String bookName, Double price) {
		// 通过控制台的输出方式模拟购书
		if (null == price || price <= 0) {
			throw new PriceException("book price exception");
		}
		System.out.println(userName + " buy " + bookName + ", spend " + price);
		return true;
	}

	public void comment(String userName, String comments) {
		// 通过控制台的输出方式模拟发表书评
		System.out.println(userName + " say:" + comments);
	}

}

IBookBiz.java

package com.liuchunming.aop.biz;

public interface IBookBiz {
	// 购书
	public boolean buy(String userName, String bookName, Double price);

	// 发表书评
	public void comment(String userName, String comments);
}

PriceException.java

package com.liuchunming.aop.biz;

public class PriceException extends RuntimeException {

	public PriceException() {
		super();
	}

	public PriceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
		super(message, cause, enableSuppression, writableStackTrace);
	}

	public PriceException(String message, Throwable cause) {
		super(message, cause);
	}

	public PriceException(String message) {
		super(message);
	}

	public PriceException(Throwable cause) {
		super(cause);
	}
	
}


通知类型实现接口实现功能
前置通知实现org.springframework.aop.MethodBeforeAdvice接口买书、评论前加系统日志
后置通知实现org.springframework.aop.AfterReturningAdvice接口买书返利(存在bug)
环绕通知org.aopalliance.intercept.MethodInterceptor类似拦截器,会包括切入点,目标类前后都会执行代码。
异常通知org.springframework.aop.ThrowsAdvice出现异常执行系统提示,然后进行处理。价格异常为例
过滤通知(适配器)org.springframework.aop.support.RegexpMethodPointcutAdvisor处理买书返利的bug

注意:这里过滤通知不需要创建类来实现接口,直接在spring.xml配置文件中写就行了

前置通知

创建MyMethodBeforeAdvice类并实现MethodBeforeAdvice接口

package com.liuchunming.aop.advice;

import java.lang.reflect.Method;
import java.util.Arrays;

import org.springframework.aop.MethodBeforeAdvice;

/**
 * 
 * 需求:买书,评论前加系统日志
*
*/
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
	//前置通知
	@Override
	public void before(Method method, Object[] args, Object target) throws Throwable {
			String clzName=target.getClass().getName();//哪个类被调用了
			String methodName=method.getName();//哪个方法被调用了
			String params=Arrays.toString(args);
			System.out.println("【前置通知,买书,评论前加系统日志】:"+clzName+"."+methodName+"("+params +")");
	}

}

spring.xml配置文件

<?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"
	xmlns:context="http://www.springframework.org/schema/context"
	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
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
		
		<!-- 目标对象 -->
		<bean id="bookBiz" class="com.liuchunming.aop.biz.BookBizImpl"></bean>
		<!--  前置通知 --> 
		<bean id="myBefore" class="com.liuchunming.aop.advice.MyMethodBeforeAdvice"></bean>
		
		
		<!-- 由代理工厂组装目标对象及通知  -->
		<bean id="bookProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
			<property name="target" ref="bookBiz"></property><!-- 目标对象 -->
			<property name="proxyInterfaces"><!--代理应该实现的接口列表  -->
				<list>
					<value>com.liuchunming.aop.biz.IBookBiz</value>
				</list>
			</property>
			<!-- 代理通知 -->
			<property name="interceptorNames">
				<list>
					<value>myBefore</value>
				</list>
			</property>
			
		</bean>
</beans>

测试类

package com.liuchunming.test;

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

import com.liuchunming.aop.biz.IBookBiz;

public class AopTest {
	public static void main(String[] args) {
		ApplicationContext springContext=new ClassPathXmlApplicationContext("/spring.xml");
		IBookBiz bean  = (IBookBiz) springContext.getBean("bookProxy");//使用代理
		System.out.println(bean.getClass());
		boolean buy = bean.buy("虚坤", "基尼太美", 66d);
		bean.comment("虚坤", "坤坤打篮球");
	}
}

在这里插入图片描述

后置通知

创建MyAfterReturningAdvice类并实现AfterReturningAdvice接口

package com.liuchunming.aop.advice;

import java.lang.reflect.Method;
import java.util.Arrays;

import org.springframework.aop.AfterReturningAdvice;

/**
 *  买书返利(存在bug)
 * @author zjjt
 *
 */
public class MyAfterReturningAdvice implements AfterReturningAdvice {

	@Override
	public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
		String clzName=target.getClass().getName();//哪个类被调用了
		String methodName=method.getName();//哪个方法被调用了
		String params=Arrays.toString(args);
		System.out.println("【买书返利后置通知】:"+clzName+"."+methodName+"("+params +")"+"\t目标方法调用后的返回值:"+returnValue);
	}
}

配置文件

<?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"
	xmlns:context="http://www.springframework.org/schema/context"
	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
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
		
		<!-- 目标对象 -->
		<bean id="bookBiz" class="com.liuchunming.aop.biz.BookBizImpl"></bean>
		<!--  前置通知 --> 
		<bean id="myBefore" class="com.liuchunming.aop.advice.MyMethodBeforeAdvice"></bean>
		<!-- 后置通知 -->
		 <bean id="myAfter" class="com.liuchunming.aop.advice.MyAfterReturningAdvice"></bean>
		<!-- 由代理工厂组装目标对象及通知  -->
		<bean id="bookProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
			<property name="target" ref="bookBiz"></property><!-- 目标对象 -->
			<property name="proxyInterfaces"><!--代理应该实现的接口列表  -->
				<list>
					<value>com.liuchunming.aop.biz.IBookBiz</value>
				</list>
			</property>
			<!-- 代理通知 -->
			<property name="interceptorNames">
				<list>
					<value>myBefore</value>
					<value>myAfter</value> 
				</list>
			</property>
			
		</bean>
</beans>

这里有个bug,就是评论也会返利,下面过滤通知会解决
在这里插入图片描述

环绕通知

MyMethodInterceptor.java

package com.liuchunming.aop.advice;

import java.util.Arrays;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class MyMethodInterceptor implements MethodInterceptor {

	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		String clzName=invocation.getThis().getClass().getName();//哪个类被调用了
		String methodName=invocation.getMethod().getName();//哪个方法被调用了
		String params=Arrays.toString(invocation.getArguments());
		System.out.println("【环绕通知】:"+clzName+"."+methodName+"("+params +")");
		Object returnValue = invocation.proceed();
		System.out.println("【环绕通知】:\t目标方法调用后的返回值:"+returnValue);
		return returnValue;
	}

}

配置文件

<?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"
	xmlns:context="http://www.springframework.org/schema/context"
	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
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
		
		<!-- 目标对象 -->
		<bean id="bookBiz" class="com.liuchunming.aop.biz.BookBizImpl"></bean>
		<!--  前置通知 --> 
		<bean id="myBefore" class="com.liuchunming.aop.advice.MyMethodBeforeAdvice"></bean>
		<!-- 后置通知 -->
		 <bean id="myAfter" class="com.liuchunming.aop.advice.MyAfterReturningAdvice"></bean> 
		<!-- 环绕通知 -->
		<bean id="myIntercep" class="com.liuchunming.aop.advice.MyMethodInterceptor"></bean> 
		
		<!-- 由代理工厂组装目标对象及通知  -->
		<bean id="bookProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
			<property name="target" ref="bookBiz"></property><!-- 目标对象 -->
			<property name="proxyInterfaces"><!--代理应该实现的接口列表  -->
				<list>
					<value>com.liuchunming.aop.biz.IBookBiz</value>
				</list>
			</property>
			<!-- 代理通知 -->
			<property name="interceptorNames">
				<list>
					<value>myBefore</value>
					 <value>myAfter</value> 
					<value>myIntercep</value>
				</list>
			</property>
			
		</bean>
</beans>


环绕通知与前置后置一样,通常使用环绕通知
在这里插入图片描述

异常通知

MyThrowsAdvice.java
这里随便写了提示语,一般异常通知用于数据回滚
列如:
张三转账给李四500元,张三支付之后发生了异常,张三钱扣了,李四未收到钱。
这种情况,就可以用异常通知做数据回滚!!

package com.liuchunming.aop.advice;

import org.springframework.aop.ThrowsAdvice;

import com.liuchunming.aop.biz.PriceException;
/**
 * 异常通知
 * @author zjjt
 *
 */
public class MyThrowsAdvice implements ThrowsAdvice {
	public void afterThrowing( PriceException ex ) {
		System.out.println("价格输入有误,购买失败,请重新输入!!!");
	}
}

配置

<?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"
	xmlns:context="http://www.springframework.org/schema/context"
	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
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
		
		<!-- 目标对象 -->
		<bean id="bookBiz" class="com.liuchunming.aop.biz.BookBizImpl"></bean>
		<!--  前置通知 --> 
		<bean id="myBefore" class="com.liuchunming.aop.advice.MyMethodBeforeAdvice"></bean>
		<!-- 后置通知 -->
		 <bean id="myAfter" class="com.liuchunming.aop.advice.MyAfterReturningAdvice"></bean> 
		<!-- 环绕通知 -->
		<bean id="myIntercep" class="com.liuchunming.aop.advice.MyMethodInterceptor"></bean> 
		<!-- 异常通知 -->
		 <bean id="myThrows" class="com.liuchunming.aop.advice.MyThrowsAdvice"></bean> 
		
		<!-- 由代理工厂组装目标对象及通知  -->
		<bean id="bookProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
			<property name="target" ref="bookBiz"></property><!-- 目标对象 -->
			<property name="proxyInterfaces"><!--代理应该实现的接口列表  -->
				<list>
					<value>com.liuchunming.aop.biz.IBookBiz</value>
				</list>
			</property>
			<!-- 代理通知 -->
			<property name="interceptorNames">
				<list>
					<value>myBefore</value>
					 <value>myAfter</value> 
					<value>myIntercep</value>
					<value>myThrows</value> 
				</list>
			</property>
			
		</bean>
</beans>

测试

package com.liuchunming.test;

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

import com.liuchunming.aop.biz.IBookBiz;

public class AopTest {
	public static void main(String[] args) {
		ApplicationContext springContext=new ClassPathXmlApplicationContext("/spring.xml");
		IBookBiz bean  = (IBookBiz) springContext.getBean("bookProxy");//使用代理
		System.out.println(bean.getClass());
		boolean buy = bean.buy("虚坤", "基尼太美", -66d);
		bean.comment("虚坤", "坤坤打篮球");
	}
}

在这里插入图片描述

过滤通知
<?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"
	xmlns:context="http://www.springframework.org/schema/context"
	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
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
		
		<!-- 目标对象 -->
		<bean id="bookBiz" class="com.liuchunming.aop.biz.BookBizImpl"></bean>
		<!--  前置通知 --> 
		<bean id="myBefore" class="com.liuchunming.aop.advice.MyMethodBeforeAdvice"></bean>
		<!-- 后置通知 -->
		 <bean id="myAfter" class="com.liuchunming.aop.advice.MyAfterReturningAdvice"></bean> 
		<!-- 环绕通知 -->
		<bean id="myIntercep" class="com.liuchunming.aop.advice.MyMethodInterceptor"></bean> 
		<!-- 异常通知 -->
		 <bean id="myThrows" class="com.liuchunming.aop.advice.MyThrowsAdvice"></bean> 
		<!-- 过滤通知  -->
		<bean id="myAfter2" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
			<property name="advice" ref="myAfter"></property>
			<property name="pattern" value=".*buy"></property>
		</bean>  
		
		<!-- 由代理工厂组装目标对象及通知  -->
		<bean id="bookProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
			<property name="target" ref="bookBiz"></property><!-- 目标对象 -->
			<property name="proxyInterfaces"><!--代理应该实现的接口列表  -->
				<list>
					<value>com.liuchunming.aop.biz.IBookBiz</value>
				</list>
			</property>
			<!-- 代理通知 -->
			<property name="interceptorNames">
				<list>
					<value>myBefore</value>
					 <!-- <value>myAfter</value>  -->
					  <value>myAfter2</value> 
					<value>myIntercep</value>
					<value>myThrows</value> 
				</list>
			</property>
			
		</bean>
</beans>

解决了后置评论返利问题,过滤 .*buy外的请求

<!-- 过滤通知  -->
		<bean id="myAfter2" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
			<property name="advice" ref="myAfter"></property>
			<property name="pattern" value=".*buy"></property>
		</bean>  

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值