`
hehaibo
  • 浏览: 416869 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
社区版块
存档分类
最新评论

spring aop探寻之Before Advice

 
阅读更多
	/**
	 * Return the AopProxyFactory that this ProxyConfig uses.
	 */
	public AopProxyFactory getAopProxyFactory() {
		return this.aopProxyFactory;
	}

 1 接口的声明

 

/**
 * 应用服务管理
 * 
 * @author hehaibo
 * 
 */
public interface BiapAppService {

	public void insertApp(BiapApp biapApp);

	public void updateApp(BiapApp biapApp);

	public void deleteApp(BiapApp biapApp);

	public Paginator<BiapApp, BiapApp> getPaginatorApp(
			Paginator<BiapApp, BiapApp> biapApp);
	
	public BiapApp getBiapAppById(String appId);

}

 2 接口的实现

 

public class BiapAppServiceImpl implements BiapAppService {

	@Autowired
	private BiapAppDAO biapAppDAO;
	
	public void insertApp(BiapApp biapApp) {
		this.biapAppDAO.insertApp(biapApp);
	}

	public void updateApp(BiapApp biapApp) {
		this.biapAppDAO.updateApp(biapApp);
	}

	public void deleteApp(BiapApp biapApp) {
		this.biapAppDAO.deleteAppById(biapApp.getId());
	}
	
	public Paginator<BiapApp, BiapApp> getPaginatorApp(
			Paginator<BiapApp, BiapApp> biapApp) {
		biapApp.setData(this.biapAppDAO.getObjectList(biapApp));
		biapApp.setCount(this.biapAppDAO.getCount(biapApp));
		return biapApp;
	}

	public BiapApp getBiapAppById(String appId) {
		return this.biapAppDAO.getBiapAppById(appId);
	}
}

 3 BeforeAdvice 实现,简单打印调用方法,模拟记录

 

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

/**
 * 方法调用记录
 * 
 * @author hehaibo
 * 
 */
public class InvokeRecordBeforeAdvice implements MethodBeforeAdvice {

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

}

 4 测试代码

 

import org.springframework.aop.framework.ProxyFactory;

import com.hhb.biap.base.test.BiapBizBaseTest;
import com.hhb.biap.interceptor.before.InvokeRecordBeforeAdvice;
import com.hhb.biap.model.BiapApp;
import com.hhb.biap.service.BiapAppService;
import com.hhb.biap.service.impl.BiapAppServiceImpl;

public class InvokeRecordBeforeAdviceTest extends BiapBizBaseTest {
	@Test
	public void testBeforeInterceptor() {
		//获得容器中的bean对象
		BiapAppServiceImpl biapAppService = (BiapAppServiceImpl) this.applicationContext
				.getBean("biapAppService");
		System.out.println(biapAppService);
		// 创建代理工厂
		ProxyFactory proxyFactory = new ProxyFactory();
//		proxyFactory.setInterfaces(new Class[]{BiapAppService.class});
		proxyFactory.setTarget(biapAppService);
//		proxyFactory.addAdvice(new InvokeRecordBeforeAdvice());
		BiapAppServiceImpl proxyBiapAppService = (BiapAppServiceImpl) proxyFactory
				.getProxy();
		BiapApp biapApp = proxyBiapAppService.getBiapAppById("biap");
		System.out.println(biapApp);
	}
}

 5 运行结果

 

java.lang.ClassCastException: $Proxy12 cannot be cast to com.hhb.biap.service.impl.BiapAppServiceImpl
	at com.hhb.biap.interceptor.test.InvokeRecordBeforeAdviceTest.testBeforeInterceptor(InvokeRecordBeforeAdviceTest.java:16)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at org.springframework.test.context.junit4.SpringTestMethod.invoke(SpringTestMethod.java:160)
	at org.springframework.test.context.junit4.SpringMethodRoadie.runTestMethod(SpringMethodRoadie.java:233)
	at org.springframework.test.context.junit4.SpringMethodRoadie$RunBeforesThenTestThenAfters.run(SpringMethodRoadie.java:333)
	at org.springframework.test.context.junit4.SpringMethodRoadie.runWithRepetitions(SpringMethodRoadie.java:217)
	at org.springframework.test.context.junit4.SpringMethodRoadie.runTest(SpringMethodRoadie.java:197)
	at org.springframework.test.context.junit4.SpringMethodRoadie.run(SpringMethodRoadie.java:143)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.invokeTestMethod(SpringJUnit4ClassRunner.java:160)
	at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
	at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44)
	at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
	at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
	at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:97)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:46)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

 以上我们运行可以看出ClassCastException异常,因为我之前对biapAppService做了aop事务的拦截,所以从BeanFactory中获得的对象已经是代理对象[JDK动代理的对象]

 

<aop:config>
			<aop:advisor pointcut="execution(public * com.hhb.biap.service.impl.*.*(..))"
				advice-ref="biap_txAdvice" />
		</aop:config>

		<tx:advice id="biap_txAdvice" transaction-manager="biap_transactionManager" >
			<tx:attributes>
				<tx:method name="insert*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
				<tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
				<tx:method name="delete*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
				<tx:method name="get*" read-only="true" propagation="SUPPORTS" />
				<tx:method name="query*" read-only="true" propagation="SUPPORTS" />
				<tx:method name="*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
			</tx:attributes>
		</tx:advice>

 所以会报错,现在我们将aop的配置下使之不会被spring拦截到。再运行,输出结果

 

 com.hhb.biap.service.impl.BiapAppServiceImpl@6d3b92
 com.hhb.biap.model.BiapApp@cd83d8

 从上面我们看出,并没有我想要输出的接口,因为我们没有调用增加一个增强

proxyFactory.addAdvice(new InvokeRecordBeforeAdvice())

 结果再运行,打印出了我们想要的结果。

 

com.hhb.biap.service.impl.BiapAppServiceImpl@17b1d64
invock method:com.hhb.biap.service.impl.BiapAppServiceImpl.toString
invock method:com.hhb.biap.service.impl.BiapAppServiceImpl.getBiapAppById
 com.hhb.biap.model.BiapApp@13a0212

 toString()怎么会调用呢,因为我们调用代理对象的toString()方法,这是我后面加上去的,

 

System.out.println("代理对象:"+ proxyBiapAppService);

 

 

这里Spring默认帮我们使用的是CGLIB代理机制,因为CGLIB代理拦截每个方法的调用

 而这一切是怎么做到的呢,接下来我们看ProxyFactory这个类里面做了什么事情......

public class ProxyFactory extends ProxyCreatorSupport implements AopProxy {
/**
  * Create a new ProxyFactory.
  */
 public ProxyFactory() {
 }
//.......
}

 我们看它父类的构造函数

 

public class ProxyCreatorSupport extends AdvisedSupport {

	/** The AopProxyFactory to use */
	private AopProxyFactory aopProxyFactory;

	/** List of AdvisedSupportListener */
	private List listeners = new LinkedList();

	/** Set to true when the first AOP proxy has been created */
	private boolean active = false;


	/**
	 * Create a new ProxyCreatorSupport instance.
	 */
	public ProxyCreatorSupport() {
		this.aopProxyFactory = new DefaultAopProxyFactory();
	}
//......

}

ProxyCreatorSupport 这个类我想我们很熟悉......

 它为我们默认初始化了一个DefaultAopProxyFactory对象,而这个对象负责代理对象创建。接下来我们看

 proxyFactory.getProxy()所干的事情

public Object getProxy() {
		return createAopProxy().getProxy();
	}

  接下来我们看createAopProxy()所干的事情,调用时父类ProxyCreatorSupport的方法

/**
	 * Subclasses should call this to get a new AOP proxy. They should <b>not</b>
	 * create an AOP proxy with <code>this</code> as an argument.
	 */
	protected final synchronized AopProxy createAopProxy() {
		if (!this.active) {
			activate();
		}
		return getAopProxyFactory().createAopProxy(this);
	}

 getAopProxyFactory()返回的就是spring默认给我们创建的一个对象,接下我们看看DefaultAopProxyFactory

这个类又是怎么回事

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
          public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
			Class targetClass = config.getTargetClass();
			if (targetClass == null) {
				throw new AopConfigException("TargetSource cannot determine target class: " +
						"Either an interface or a target is required for proxy creation.");
			}
			if (targetClass.isInterface()) {
				return new JdkDynamicAopProxy(config);
			}
			if (!cglibAvailable) {
				throw new AopConfigException(
						"Cannot proxy target class because CGLIB2 is not available. " +
						"Add CGLIB to the class path or specify proxy interfaces.");
			}
			return CglibProxyFactory.createCglibProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	} 
}

 从上面我们可以看出,这个是真正创建代理对象的地方,而参数AdvisedSupport config就是我们ProxyFactory,因为ProxyFactory间接继承了AdvisedSupport

public class ProxyCreatorSupport extends AdvisedSupport {
//...
}

 分析下这个方法,

 

config.isOptimize()  这个表示是否启用优化,是否可以用cglib创建对象,从方法可以看出,如我们指定了创建代理对象使用的接口,

proxyFactory.setInterfaces(new Class[]{BiapAppService.class});

 

将将会覆盖cglib的代理,这个config.isProxyTargetClass()也是同理,表示是否能代理类对象。这个hasNoUserSuppliedProxyInterfaces(config) 。从我们的测试代码中可以看出,这个我们都没有指定,所以这个条件hasNoUserSuppliedProxyInterfaces(config) 满足,所以我们看到是打印对象的时候也被拦截了。

接下来我们分析前面2个条件为什么没有满足,我们看AdvisedSupport 的继承和实现

public class AdvisedSupport extends ProxyConfig implements Advised {
      //...
}

 我们再看

public class ProxyConfig implements Serializable {

	/** use serialVersionUID from Spring 1.2 for interoperability */
	private static final long serialVersionUID = -8409359707199703185L;


	private boolean proxyTargetClass = false;

	private boolean optimize = false;
                 //......
} 

 我想我们知道原因了。这就是spring美妙的设计给我们所带来的,我们只有去看看他们的源代码我们窥探到它有血有肉的地方。

最终的测试代码

public void testBeforeInterceptor() {
		//获得容器中的bean对象
		BiapAppServiceImpl biapAppService = (BiapAppServiceImpl) this.applicationContext
				.getBean("biapAppService");
		System.out.println(biapAppService);
		// 创建代理工厂
		ProxyFactory proxyFactory = new ProxyFactory();
//		proxyFactory.setOptimize(Boolean.TRUE);
//		proxyFactory.setProxyTargetClass(Boolean.TRUE);
		proxyFactory.setInterfaces(new Class[]{BiapAppService.class});
		proxyFactory.setTarget(biapAppService);
		proxyFactory.addAdvice(new InvokeRecordBeforeAdvice());
		//注意这里不能再用BiapAppServiceImpl转换,否则会报错,
		//因为我们返回的是代理对象
		BiapAppService proxyBiapAppService = (BiapAppService) proxyFactory
				.getProxy();
		System.out.println("代理对象:"+ proxyBiapAppService);
		BiapApp biapApp = proxyBiapAppService.getBiapAppById("biap");
		System.out.println(biapApp);
	}

 

接下来我们看看类的设计图纸:

其他类的图片

 

分享到:
评论

相关推荐

    spring aop jar 包

    Spring AOP支持五种类型的通知:前置通知(Before)、后置通知(After)、返回后通知(After Returning)、异常后通知(After Throwing)和环绕通知(Around)。 3. **切点(Pointcut)**:切点是程序执行过程中的...

    Spring AOP 16道面试题及答案.docx

    Spring AOP中有五种不同类型的的通知(Advice): 1. 前置通知(Before Advice):在方法执行前执行,使用`@Before`注解。 2. 返回后通知(After Returning Advice):在方法正常返回后执行,使用`@AfterReturning`...

    简单spring aop 例子

    Spring AOP(面向切面编程)是Spring框架的重要组成部分,它提供了一种模块化和声明式的方式来处理系统中的交叉关注点问题,如日志、事务管理、安全性等。本示例将简要介绍如何在Spring应用中实现AOP,通过实际的...

    spring aop依赖jar包

    Spring AOP,全称Aspect-Oriented Programming(面向切面编程),是Spring框架的重要组成部分,它为Java应用程序提供了声明式的企业级服务,如事务管理、性能监控等。在Spring AOP中,我们可以通过定义切面(Aspect...

    死磕Spring之AOP篇 - Spring AOP两种代理对象的拦截处理(csdn)————程序.pdf

    Spring AOP 是一种面向切面编程的技术,它允许我们在不修改源代码的情况下,对应用程序的特定部分(如方法调用)进行增强。在 Spring 中,AOP 的实现主要依赖于代理模式,有两种代理方式:JDK 动态代理和 CGLIB 动态...

    Spring AOP完整例子

    Spring AOP(面向切面编程)是Spring框架的核心特性之一,它允许开发者在不修改源代码的情况下,通过插入切面来增强或改变程序的行为。在本教程中,我们将深入探讨Spring AOP的不同使用方法,包括定义切点、通知类型...

    Spring Aop四个依赖的Jar包

    Spring AOP,全称Aspect-Oriented Programming(面向切面编程),是Spring框架的一个重要模块,它通过提供声明式的方式来实现面向切面编程,从而简化了应用程序的开发和维护。在Spring AOP中,我们无需深入到每个...

    Spring AOP面向方面编程原理:AOP概念

    Spring框架作为Java开发领域的领头羊之一,提供了强大的AOP支持。本文旨在深入探讨Spring AOP的核心概念及其原理。 #### 二、AOP基本概念 AOP是一种编程范式,其目的是提高模块化程度,特别是将那些对很多类都具有...

    spring-aop.jar各个版本

    spring-aop-1.1.1.jar spring-aop-1.2.6.jar spring-aop-1.2.9.jar spring-aop-2.0.2.jar spring-aop-2.0.6.jar spring-aop-2.0.7.jar spring-aop-2.0.8.jar spring-aop-2.0.jar spring-aop-2.5.1.jar spring-aop-...

    Spring AOP实现机制

    Spring AOP(面向切面编程)是Spring框架的核心特性之一,它允许程序员在不修改源代码的情况下,通过“切面”来插入额外的业务逻辑,如日志、事务管理等。AOP的引入极大地提高了代码的可复用性和可维护性。 ### 1. ...

    spring源码--AOP流程--笔记.docx

    5. 解析 Advice:在解析 Aspect 配置时,Spring AOP 也会解析 Advice 配置,例如 `&lt;aop:before&gt;` 标签。在这个过程中,Spring AOP 使用 adviceParser 来解析 Advice 配置,并生成相应的 Bean 定义对象。 6. 注册 ...

    spring AOP依赖三个jar包

    在这个版本中,Spring AOP支持基于注解的切面定义,比如`@Aspect`、`@Before`、`@After`等,同时也支持基于XML配置的切面定义。这个jar包使得Spring应用程序能够方便地利用AOP特性,无需引入额外的编译工具或构建...

    spring-aop实例

    通过以上介绍,我们可以看到Spring AOP的强大之处在于它简化了代码的组织和维护,使得横切关注点的实现更加优雅。了解并掌握这些概念,开发者可以有效地利用Spring AOP提升应用程序的可维护性和可扩展性。

    spring aop 经典例子(原创)

    在Spring AOP中,切面通常由一个或多个通知(Advice)组成,通知是在特定连接点(Join Point)执行的代码片段。连接点是指程序执行过程中能够插入切面的特定位置,如方法调用、异常抛出等。 在Spring AOP中,有五种...

    Spring Aop的简单实现

    通知(Advice)是在特定切点执行的代码,可以是前置通知(before advice)、后置通知(after advice)、返回通知(return advice)、异常通知(exception advice)和环绕通知(around advice)。最后,切入点表达式...

    springaop.zip

    Spring支持五种类型的Advice:Before、After、After Returning、After Throwing、Around。 3. **切入点(Pointcut)**:切入点是定义通知将被应用到的精确位置,通常是方法的签名。Spring使用表达式语言(如XPath)...

    spring aop spring aop

    Spring AOP,全称Aspect-Oriented Programming(面向切面编程),是Spring框架的重要组成部分,用于实现横切关注点的模块化。它允许开发者定义“切面”,将那些与业务逻辑无关,却为多个对象共有的行为(如日志、...

    spring aop 自定义注解保存操作日志到mysql数据库 源码

    一、适合人群 1、具备一定Java编程基础,初级开发者 2、对springboot,mybatis,mysql有基本认识 3、对spring aop认识模糊的,不清楚如何实现Java 自定义注解的 ...4、spring boot,mybatis,druid,spring aop的使用

    spring aop demo 两种实现方式

    Spring AOP(面向切面编程)是Spring框架的重要组成部分,它允许程序员在不修改源代码的情况下,对应用程序的特定部分(如方法调用)进行拦截和处理。这为日志、事务管理、性能监控等提供了方便。本示例提供了一种...

    基于注解实现SpringAop

    基于注解实现SpringAop基于注解实现SpringAop基于注解实现SpringAop

Global site tag (gtag.js) - Google Analytics