Spring笔记 - Spring AOP入门

一、什么是AOP?

AOP(Aspect Oriented Programming),即面向切面编程。

在我们的项目代码中,有大量与日志、事务、权限(AOP称之为横切关注点)相关的代码镶嵌在业务代码当中,造成大量代码的重复与代码的冗余。
虽然可以将这些重复的代码封装起来再进行调用,但是这样的调用方式比较单一,不够灵活,无法更好地以模块化的方式,对这些横切关注点进行组织和实现。

AOP提出切面(Aspect)的概念,以模块化的方式对横切关注点进行封装,再通过织入的方式将切面织入到业务逻辑代码当中。这样横切关注点与业务逻辑代码分离,业务逻辑代码中就不再含有日志、事务、权限等代码的调用,可以很好的进行管理。

 

 

二、AOP中关键性概念 

连接点(Joinpoint):程序执行过程中明确的点,如方法的调用,或者异常的抛出.

目标(Target):被通知(被代理)的对象.
注1:完成具体的业务逻辑

通知(Advice):在某个特定的连接点上执行的动作,同时Advice也是程序代码的具体实现,例如一个实现日志记录的代码(通知有些书上也称为处理)
注2:完成切面编程

代理(Proxy):将通知应用到目标对象后创建的对象(代理=目标+通知),
              例子:外科医生+护士
注3:只有代理对象才有AOP功能,而AOP的代码是写在通知的方法里面的

切入点(Pointcut):多个连接点的集合,定义了通知应该应用到那些连接点。
                 (也将Pointcut理解成一个条件 ,此条件决定了容器在什么情况下将通知和目标组合成代理返回给外部程序)

适配器(Advisor):适配器=通知(Advice)+切入点(Pointcut)

 

三、AOP在项目中的使用。

下面示例代码为模拟项目开发中的操作(这里以买书和评论为例):

public interface IBookBiz {
	/**
	 * 买书
	 * @param book 书名
	 * @return
	 */
	Integer buy(String book);
	/**
	 * 评论
	 * @param comment 要评论的内容
	 * @return
	 */
	Integer comment(String comment);
}
public class BookBizImpl implements IBookBiz{
	@Override
	public Integer buy(String book) {
		System.out.println("买书操作");
		return 0;
	}
	@Override
	public Integer comment(String comment) {
		System.out.println("评论操作");
		return 1;
	}
}

1、前置通知:

/**
 * 前置通知,需实现MethodBeforeAdvice接口
 * @author lenovo
 *
 */
public class MyMethodBeforeAdvice implements MethodBeforeAdvice{

	@Override
	public void before(Method method, Object[] args, Object target) throws Throwable {
		String targetName = target.getClass().getName();	//被调用方法的所属类
		String methodName = method.getName();	//被调用的方法
		String params = Arrays.toString(args);	//被调用方法的参数
		System.out.println("{前置日志}系统日志:"+targetName+"."+methodName+"被调用,携带了参数:"+params);
	}
}

spring的配置文件:

<?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 class="com.zking.biz.impl.BookBizImpl" id="bookBiz"></bean>
        <!-- 通知 -->
        <!-- 前置通知 -->
	<bean class="com.zking.aop.MyMethodBeforeAdvice" id="myMethodBeforeAdvice"></bean>
        <!-- 代理工厂(这个类为spring自带的) -->
	<bean class="org.springframework.aop.framework.ProxyFactoryBean" id="bookProxy">
        <!--  目标 -->
		<property name="target" ref="bookBiz"></property>
        <!-- 代理所需要实现目标对象所实现的接口 -->
		<property name="proxyInterfaces">
			<list>
				<value>com.zking.biz.IBookBiz</value>
			</list>
		</property>
        <!-- 需要应用到目标对象上的通知Bean的名字。(List) -->
		<property name="interceptorNames">
			<list>
				<value>myMethodBeforeAdvice</value>
			</list>
		</property>
	</bean>

	
</beans>

测试方法(后面用的都是这一个测试方法,在这里提前说明一下):

public static void main(String[] args) throws ServletException, IOException {
    ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring-context.xml");
    BookBiz iBookBiz = (IBookBiz)applicationContext.getBean("bookProxy");
    System.out.println(iBookBiz.buy("圣墟"));
    System.out.println(iBookBiz.comment("圣墟"));
}

打印结果:

    

2、后置通知
 

/**
 * 后置通知,需实现AfterReturningAdvice接口
 * @author lenovo
 *
 */
public class MyAfterReturningAdvice implements AfterReturningAdvice{

	@Override
	public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
		String targetName = target.getClass().getName();    //被调用方法的所属类
		String methodName = method.getName();    //被调用的方法
		String params = Arrays.toString(args);    //被调用方法的参数
		System.out.println("{后置日志}买书返利日志:"+targetName+"."+methodName+"被调用,系带了参数:"+params+",返回值:"+returnValue);
	}
}

spring的配置文件:

<?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 class="com.zking.biz.impl.BookBizImpl" id="bookBiz"></bean>
        <!-- 通知 -->
       <!-- 后置通知 -->
	<bean class="com.zking.aop.MyAfterReturningAdvice" id="myAfterReturningAdvice"></bean>       
 <!-- 代理工厂(这个类为spring自带的) -->
	<bean class="org.springframework.aop.framework.ProxyFactoryBean" id="bookProxy">
        <!--  目标 -->
		<property name="target" ref="bookBiz"></property>
        <!-- 代理所需要实现目标对象所实现的接口 -->
		<property name="proxyInterfaces">
			<list>
				<value>com.zking.biz.IBookBiz</value>
			</list>
		</property>
        <!-- 需要应用到目标对象上的通知Bean的名字。(List) -->
		<property name="interceptorNames">
			<list>
				<value>myAfterReturningAdvice</value>
			</list>
		</property>
	</bean>

	
</beans>

打印结果,(细心的话就不难发现,评论的时候也有返利的操作(亏钱亏大发了),这个bug后面会得到解决):

 3、环绕通知
 

/**
 * 环绕通知,需实现MethodInterceptor接口
 * @author lenovo
 *
 */
public class MyMethodInterceptor implements MethodInterceptor{
	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		String targetName = invocation.getThis().getClass().getName();	// 目标名
		String methodName = invocation.getMethod().getName();	// 方法名
		String params = Arrays.toString(invocation.getArguments());	// 参数
		System.out.println("{环绕日志}买书返利日志:"+targetName+"."+methodName+"被调用,系带了参数:"+params);
		//	proceed方法的作用是让目标方法执行,且返回值就是目标方法的返回值
		Object returnValue = invocation.proceed();	// 方法放行
		System.out.println("{环绕日志}目标对象对应的方法调用返回值:"+returnValue);
		// 而本方法的返回值,即为目标方法在实际被调用时的放回值
		return returnValue;
	}
}

spring的配置文件:

<?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 class="com.zking.biz.impl.BookBizImpl" id="bookBiz"></bean>
        <!-- 通知 -->
        <!-- 环绕通知 -->
	<bean class="com.zking.aop.MyMethodInterceptor" id="myMethodInterceptor"></bean>       
 <!-- 代理工厂(这个类为spring自带的) -->
	<bean class="org.springframework.aop.framework.ProxyFactoryBean" id="bookProxy">
        <!--  目标 -->
		<property name="target" ref="bookBiz"></property>
        <!-- 代理所需要实现目标对象所实现的接口 -->
		<property name="proxyInterfaces">
			<list>
				<value>com.zking.biz.IBookBiz</value>
			</list>
		</property>
        <!-- 需要应用到目标对象上的通知Bean的名字。(List) -->
		<property name="interceptorNames">
			<list>
				<value>myMethodInterceptor</value>
                        </list>
		</property>
	</bean>
	
</beans>

打印结果:

4、适配器(这里可以解决之前买书返利的bug):
适配器是Spring自带的,所以无需创建类

spring的配置文件:
 

<?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 class="com.zking.biz.impl.BookBizImpl" id="bookBiz"></bean>
        <!-- 通知 -->
        <!-- 后置通知 -->
	<bean class="com.zking.aop.MyAfterReturningAdvice" id="myAfterReturningAdvice"></bean>   
        <!-- 过滤通知 -->
	<bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor" id="myRegexp">
                <!-- 这里指过滤哪个通知 -->
		<property name="advice" ref="myAfterReturningAdvice"></property>
                <!-- 这里为表达式,指匹配方法名为buy的方法 -->
		<property name="pattern" value=".*buy"></property>
	</bean>    
 <!-- 代理工厂(这个类为spring自带的) -->
	<bean class="org.springframework.aop.framework.ProxyFactoryBean" id="bookProxy">
        <!--  目标 -->
		<property name="target" ref="bookBiz"></property>
        <!-- 代理所需要实现目标对象所实现的接口 -->
		<property name="proxyInterfaces">
			<list>
				<value>com.zking.biz.IBookBiz</value>
			</list>
		</property>
        <!-- 需要应用到目标对象上的通知Bean的名字。(List) -->
		<property name="interceptorNames">
			<list>
                                <value>myRegexp</value>
			</list>
		</property>
	</bean>

	
</beans>

打印结果(结果很明显,评论并返利):

 5、异常通知
 

/**
 * 异常通知,需实现ThrowsAdvice接口,方法名必须叫afterThrowing
 * 	出现异常执行系统提示,然后进行处理。买书异常为例
 * @author lenovo
 *
 */
public class MyThrowsAdvice implements ThrowsAdvice{

	public void afterThrowing(RuntimeException re) {
		System.out.println("买书异常!!!!");
	}
}

对BookBizImpl.java稍做修改:

public class BookBizImpl implements IBookBiz{
	@Override
	public Integer buy(String book) {
		System.out.println("买书");
		//这里抛出异常,模拟程序运行时意外出现的错误
		if(true) {
			throw new RuntimeException();
		}
		return 0;
	}
	@Override
	public Integer comment(String comment) {
		System.out.println("评论");
		return 1;
	}
}

spring的配置文件:
 

<?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 class="com.zking.biz.impl.BookBizImpl" id="bookBiz"></bean>
        <!-- 通知 -->
        <!--异常通知 -->
	<bean class="com.zking.aop.MyThrowsAdvice" id="myThrowsAdvice"></bean>       
 <!-- 代理工厂(这个类为spring自带的) -->
	<bean class="org.springframework.aop.framework.ProxyFactoryBean" id="bookProxy">
        <!--  目标 -->
		<property name="target" ref="bookBiz"></property>
        <!-- 代理所需要实现目标对象所实现的接口 -->
		<property name="proxyInterfaces">
			<list>
				<value>com.zking.biz.IBookBiz</value>
			</list>
		</property>
        <!-- 需要应用到目标对象上的通知Bean的名字。(List) -->
		<property name="interceptorNames">
			<list>
				<value>myThrowsAdvice</value>
			</list>
		</property>
	</bean>

</beans>

猜你喜欢

转载自blog.csdn.net/qq_41097820/article/details/84893694