Spring学习十五----------Spring AOP API的Pointcut、advice及 ProxyFactoryBean相关内容

© 版权声明:本文为博主原创文章,转载请注明出处

实例:

1.项目结构

2.pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

	<modelVersion>4.0.0</modelVersion>
	
	<groupId>org.spring</groupId>
	<artifactId>Spring-AOP-API</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>Spring-AOP-API Maven Webapp</name>
	<url>http://maven.apache.org</url>
	
	<properties>
		<spring.version>4.3.8.RELEASE</spring.version>
	</properties>
	
	<dependencies>
		<!-- junit -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>
		<!-- Spring Core -->
		<dependency>
		    <groupId>org.springframework</groupId>
		    <artifactId>spring-core</artifactId>
		    <version>${spring.version}</version>
		</dependency>
		<dependency>
		    <groupId>org.springframework</groupId>
		    <artifactId>spring-beans</artifactId>
		    <version>${spring.version}</version>
		</dependency>
		<dependency>
		    <groupId>org.springframework</groupId>
		    <artifactId>spring-context</artifactId>
		    <version>${spring.version}</version>
		</dependency>
	</dependencies>
	
	<build>
		<finalName>Spring-AOP-API</finalName>
	</build>
</project>

3.BizLogic.java

package org.spring.aop.api;

public interface BizLogic {

	String save();
	
	String saveEx();
	
}

4.BizLogicImpl.java

package org.spring.aop.api;

public class BizLogicImpl implements BizLogic {

	public String save() {
		
		System.out.println("BizLogicImpl:BizLogicImpl save.");
		return "BizLogicImpl save";
		
	}
	
	public String saveEx() {
		
		System.out.println("BizLogicImpl:BizLogicImpl save.");
		throw new RuntimeException();
		
	}

}

5.CustomBeforeAdvice

package org.spring.aop.api;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

public class CustomBeforeAdvice implements MethodBeforeAdvice {

	public void before(Method method, Object[] args, Object target) throws Throwable {
		
		System.out.println("CustomBeforeAdvice:" + method.getName() + "		" +
				target.getClass().getName());

	}

}

6.CustomAfterReturnAdvice.java

package org.spring.aop.api;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

public class CustomAfterReturnAdvice implements AfterReturningAdvice {

	public void afterReturning(Object returnValue, Method method, Object[] args, Object target) 
			throws Throwable {
		
		System.out.println("CustomAfterReturnAdvice:" + method.getName() + "		" 
				+ target.getClass().getName() + "		" + returnValue);

	}

}

7.CustomThrowsAdvice.java

package org.spring.aop.api;

import java.lang.reflect.Method;

import org.springframework.aop.ThrowsAdvice;

public class CustomThrowsAdvice implements ThrowsAdvice {

	public void afterThrowing(Exception ex) throws Throwable {
		
		System.out.println("CustomThrowsAdvice afterThrowing 1");
		
	}
	
	public void afterThrowing(Method method, Object[] args, Object target, Exception ex) 
			throws Throwable {
		
		System.out.println("CustomThrowsAdvice afterThrowing 2:" + method.getName() + "		"
				+ target.getClass().getName());
		
	}
	
}

8.CustomMethodInterceptor.java

package org.spring.aop.api;

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

public class CustomMethodInterceptor implements MethodInterceptor {

	public Object invoke(MethodInvocation invocation) throws Throwable {
		
		System.out.println("CustomMethodInterceptor 1:" + invocation.getMethod().getName()
				+ "		" + invocation.getStaticPart().getClass().getName());
		Object obj = invocation.proceed();
		System.out.println("CustomMethodInterceptor 2:" + obj);
		return obj;
		
	}

}

9.Lockable.java

package org.spring.aop.api.introduction;

/**
 * 接口
 *
 */
public interface Lockable {

	void lock();
	
	void unlock();
	
	boolean locked();
	
}

10.LockMixin.java

package org.spring.aop.api.introduction;

import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;

/**
 * 实现类
 *
 */
public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable {

	private static final long serialVersionUID = 1L;
	
	private boolean locked;

	public void lock() {
	
		this.locked = true;

	}

	public void unlock() {

		this.locked = false;
		
	}

	public boolean locked() {
		
		return this.locked;
		
	}
	
	/**
	 * 被锁定后不能使用setter方法改变值
	 */
	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		
		if(locked && invocation.getMethod().getName().indexOf("set") == 0){
			throw new RuntimeException();
		}
		return super.invoke(invocation);
		
	}

}

11.LockMixinAdvisor.java

package org.spring.aop.api.introduction;

import org.springframework.aop.support.DefaultIntroductionAdvisor;

/**
 * Introduction,在不改变代码的情况下,添加一个父类
 *
 */
public class LockMixinAdvisor extends DefaultIntroductionAdvisor {

	private static final long serialVersionUID = 1L;

	public LockMixinAdvisor() {
		
		super(new LockMixin(), Lockable.class);
		
	}

}

12.Spring-aop-api.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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
        
	<!-- Before advice
			-一个简单的通知类型
			-只是进方法之前被调用,不需要MethodInvocation对象
			-前置通知可以在连接点执行之前插入自定义行为,但不能改变返回值
	 -->
	<bean id="customBeforeAdvice" class="org.spring.aop.api.CustomBeforeAdvice"/>
    
    <!-- Throws advice
    		-如果连接点抛出异常,throws advice在连接点返回后被调用
    		-如果throws-advice的方法抛出异常,那么它将覆盖原有异常
    		-接口org.springframework.aop.ThrowsAdvice不包含任何方法,仅仅是一个声明,实现类需要实现类似下面的方法
    		-void afterThrowing([Method, args, target], ThrowableSubclass)
     -->
    <bean id="customThrowsAdvice" class="org.spring.aop.api.CustomThrowsAdvice"/>
    
    <!-- After Returning advice 
    		-后置通知必须实现org.springframework.aop.AfterReturningAdvice几口
    		-可以访问返回值(但不能进行修改)、被调用的方法、方法的参数和目标
    		-如果抛出异常,将会抛出拦截器链,替代返回值
     -->
	<bean id="customAfterReturnAdvice" class="org.spring.aop.api.CustomAfterReturnAdvice"/>
    
    <!-- Interception around advice
    		-Spring的切入点模型使得切入点可以独立与advice重用,以针对不同的advice可以使用相同的切入点
     -->
	<bean id="customMethodInterceptor" class="org.spring.aop.api.CustomMethodInterceptor"/>
     
	<!-- Introduction advice
			-Spring把引入通知作为一种特殊的拦截通知
			-需要IntroductionAdvisor和IntroductionInterceptor
			-仅适用于类,不能和任何切入点一起使用
			-实例:如果调用lock()方法,希望所有的setter方法抛出LockedException异常。
			-代码:Lockalbe.java	LockMixin.java	LockMixinAdvisor.java
	-->
	<!-- Advisor API
			-Advisor是仅包含一个切入点表达式关联的单个通知的方面
			-除了introductions,advisor可以用于任何通知
			-org.springframework.aop.support.DefaultIntroductionAdvisor是最常用的advisor类,
				它可以与MethodInterceptor,BeforeAdvice或者ThrowsAdvice一起使用
			-它可以混合在Spring同一个AOP代理的advisor和advice
	 -->
	
    <!-- 当使用方式三时,屏蔽该bean的定义 -->
    <!-- <bean id="bizLogicImplTarget" class="org.spring.aop.api.BizLogicImpl"/> -->
	
	<!-- ###########################方式一:使用pointcutBean开始############################# -->
	<!-- 根据方法名匹配切入点pointcut -->
	<!-- <bean id="pointcutBean" class="org.springframework.aop.support.NameMatchMethodPointcut">
		<property name="mappedNames">成员变量,匹配的方法名集合
			<list>
				<value>sa*</value>
			</list>
		</property>
	</bean>
    
    <bean id="defaultAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
		<property name="advice" ref="customBeforeAdvice"/>接入点通知,若非接入点,则该通知不生效
		<property name="pointcut" ref="pointcutBean"/>接入点
    </bean> -->
    
    <!-- ProxyFactoryBean
    		-创建Spring AOP代理的基本方法是使用org.springframework.aop.framework.ProxyFactoryBean
    		-这可以完全控制切入点和通知(advice)以及它们的顺序
    		-ProxyFactoryBean实现里getObject()方法创建一个AOP代理包装一个目标对象
    		-使用ProxyFactoryBean或者其它IOC相关类来创建AOP代理的最重要的好处是通知和切入点也可以由IOC来管理
    		-被代理类没有实现任何接口,使用CGLIB代理,否则JDK代理
    		-通过设置proxyTargetClass为true,可强制使用CGLIB
    		-如果目标类实现了一个(或者多个)接口,那么创建代理的类型将依赖ProxyFactoryBean的配置
    		-如果ProxyFactoryBean的proxyInterfaces属性设置为一个或多个全限定接口名,基于JDK的代理将被创建
    		-如果ProxyFactoryBean的proxyInterfaces属性没有被设置,但是目标类实现了一个(或者更多)接口,那么proxyInterfaces
    			将自动检测到这个目标类已经实现了至少一个接口,创建一个基于JDK的代理
     -->
    <!-- <bean id="bizLogicImpl" class="org.springframework.aop.framework.ProxyFactoryBean">
    	<property name="target">为该类创建代理,由ProxyFactoryBean自己去判断是否实现了接口
    		<ref bean="bizLogicImplTarget"/>
    	</property>
    	<property name="interceptorNames">执行该代理的时候执行的interceptor
    		<list>
    			<value>defaultAdvisor</value>
    			<value>customAfterReturnAdvice</value>
    			<value>customMethodInterceptor</value>
    			<value>customThrowsAdvice</value>
    		</list>
    	</property>
    </bean> -->
    <!-- ###############################方式一结束######################################### -->
    <!-- ############################方式二:使用pointcutBean开始############################ -->
    <!-- <bean id="bizLogicImpl" class="org.springframework.aop.framework.ProxyFactoryBean">
    	<property name="proxyInterfaces">直接指定接口
    		<value>org.spring.aop.api.BizLogic</value>
    	</property>
    	<property name="target">为该类创建代理,因为直接指定了接口,所以肯定是使用JDK代理
    		<ref bean="bizLogicImplTarget"/>
    	</property>
    	<property name="interceptorNames">执行该代理的时候执行的interceptor
    		<list>
    			<value>customBeforeAdvice</value>
    			<value>customAfterReturnAdvice</value>
    			<value>customMethodInterceptor</value>
    			<value>customThrowsAdvice</value>
    		</list>
    	</property>
    </bean> -->
    <!-- ###############################方式二结束######################################### -->
    <!-- ##############################方式三:使用匿名内部Bean############################### -->
    <bean id="bizLogicImpl" class="org.springframework.aop.framework.ProxyFactoryBean">
    	<property name="proxyInterfaces"><!-- 直接指定接口 -->
    		<value>org.spring.aop.api.BizLogic</value>
    	</property>
    	<property name="target"><!-- 上面可以直接使用bean id获取bean对象,此时该bean对象没有被代理,配置的advice不会被执行 -->
    		<bean class="org.spring.aop.api.BizLogicImpl"/>
    	</property>
    	<property name="interceptorNames"><!-- 执行该代理的时候执行的interceptor -->
    		<list>
    			<value>customBeforeAdvice</value>
    			<value>customAfterReturnAdvice</value>
    			<value>customMethodInterceptor</value>
    			<value>customThrowsAdvice</value>
    		</list>
    		<!-- 使用global advisors
    				-用*做通配,匹配所有拦截器加入通知链
    			  此处只会执行customMethodInterceptor,因为只有该advice实现了Interceptor接口
    		 -->
    		 <!-- <list>
    		 	<value>custom*</value>
    		 </list> -->
    	</property>
    </bean>
    <!-- ###############################方式三结束######################################### -->
    <!-- ##############################方式四:简化的proxy定义############################### -->
    <!-- 简化的proxy定义
    		-使用父子bean定义以及内部bean定义,可能会带来更清洁和更简洁的代理定义
     -->
    <!-- <bean id="baseProxyBean" class="org.springframework.aop.framework.ProxyFactoryBean"
    	lazy-init="true" abstract="true"/>
    	
    <bean id="bizLogicImpl" parent="baseProxyBean">
    	<property name="proxyInterfaces">直接指定接口
    		<value>org.spring.aop.api.BizLogic</value>
    	</property>
    	<property name="target">上面可以直接使用bean id获取bean对象,此时该bean对象没有被代理,配置的advice不会被执行
    		<bean class="org.spring.aop.api.BizLogicImpl"/>
    	</property>
    	<property name="interceptorNames">执行该代理的时候执行的interceptor
    		<list>
    			<value>customBeforeAdvice</value>
    			<value>customAfterReturnAdvice</value>
    			<value>customMethodInterceptor</value>
    			<value>customThrowsAdvice</value>
    		</list>
    	</property>
    </bean> -->
    <!-- ###############################方式四结束######################################### -->
    
</beans>

13.TestBase.java

package org.spring.aop.api.test;

import org.junit.After;
import org.junit.Before;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.util.StringUtils;

public class TestBase {

	private ClassPathXmlApplicationContext context;
	private String springXmlPath;
	
	/**
	 * 无参构造器
	 */
	public TestBase() {
		
	}

	/**
	 * 含参构造器,初始化spring配置文件路径
	 * 
	 * @param springXmlPath
	 * 						spring配置文件路径
	 */
	public TestBase(String springXmlPath) {
		
		this.springXmlPath = springXmlPath;
		
	}
	
	/**
	 * 初始化spring配置文件并加载到IOC容器中
	 */
	@Before
	public void before() {
		
		if(StringUtils.isEmpty(springXmlPath)) {
			springXmlPath = "classpath:spring-*.xml";
		}
		context = new ClassPathXmlApplicationContext(springXmlPath.split("[,\s]+"));
		context.start();
		
	}
	
	/**
	 * 销毁IOC容器
	 */
	@After
	public void after() {
		
		if(context != null){
			context.destroy();
		}
		
	}
	
	/**
	 * 根据bean ID获取bean对象
	 * 
	 * @param beanId
	 * 					bean ID
	 * @return
	 */
	public Object getBean(String beanId) {
		
		return context.getBean(beanId);
		
	}
	
}

14.TestSpringAOPAPI.java

package org.spring.aop.api.test;

import org.junit.Test;
import org.spring.aop.api.BizLogic;

public class TestSpringAOPAPI extends TestBase {

	/**
	 * 通过构造器初始化spring配置文件
	 */
	public TestSpringAOPAPI() {
		
		super("classpath:spring-aop-api.xml");
		
	}
	
	/**
	 * 测试正常方法
	 */
	@Test
	public void testSave() {
		
		BizLogic logic = (BizLogic) super.getBean("bizLogicImpl");
		logic.save();
		
	}
	
	/**
	 * 测试存在异常的方法
	 */
	@Test
	public void testsaveEx() {
		
		BizLogic logic = (BizLogic) super.getBean("bizLogicImpl");
		logic.saveEx();
		
	}

}

15.效果预览

  15.1 执行testSave方法

  15.2 执行testsaveEx方法

说明:其他几种方式已在spring-aop-api.xml中注明,可自行测试。

参考:http://www.imooc.com/video/4597

     http://www.imooc.com/video/4598

   http://www.imooc.com/video/4599

原文地址:https://www.cnblogs.com/jinjiyese153/p/6763602.html