zoukankan      html  css  js  c++  java
  • Spring整合AspectJ的AOP

    学而时习之,不亦说乎!

                                 --《论语》

    看这一篇之前最好先看前面关于AOP的两篇。

    http://www.cnblogs.com/zby9527/p/6945756.html (JDK代理和CGLIB代理)

     http://www.cnblogs.com/zby9527/p/6946952.html (Spring的AOP)

    AspectJ:

    1.AspectJ是一个基于Java语言的AOP框架。

    2.Spring2.0以后新增了对AspectJ切点表达式支持。

    3.@AspectJ是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面新版本Spring框架,建议使用AspectJ方式来开发

    AspectJ最强大的地方在于他的切入点表达式:

    语法:execution(修饰符  返回值  包.类.方法名(参数) throws异常)

      修饰符,一般省略

        public 公共方法

        * 任意

      返回值,不能省略

        void 返回没有值

        String 返回值字符串

        * 任意

      包

        com.zby.service  固定包

        com.zby.oa.*.service oa包下面子包 (例如:com.zby.oa.flow.service)

        com.zby.oa..   oa包下面的所有子包(含自己)

        com.zby.oa.*.service.. oa包下面任意子包,固定目录service,service目录任意包

      类

        UserServiceImpl 指定类

        *Impl 以Impl结尾

        User* 以User开头

        * 任意

      方法名,不能省略

        addUser 固定方法

        add* 以add开头

        *Do 以Do结尾

        * 任意

      (参数)

        () 无参

        (int) 一个整型

        (int ,int) 两个

        (..) 参数任意

      throws ,可省略,一般不写。

    当然,execution也是可以变得,但是一般用这个就够了,更详细的表达式用法,当然是查看专业文档。

    AspectJ和aopalliance通知的区别:

    AOP联盟的通知类型具有特性接口,必须实现,从而确定方法名称,而AspectJ的通知类型只定义了类型名称和方法格式,这意味着,我们的切面不需要实现任何方法!!!。

     AspectJ通知:

      before:前置通知(应用:各种校验)

        在方法执行前执行,如果通知抛出异常,阻止方法运行

      afterReturning:后置通知(应用:常规数据处理)

        方法正常返回后执行,如果方法中抛出异常,通知无法执行,必须在方法执行后才执行,所以可以获得方法的返回值。

      around:环绕通知(应用:十分强大,可以做任何事情)

        方法执行前后分别执行,可以阻止方法的执行,必须手动执行目标方法

      afterThrowing:抛出异常通知(应用:包装异常信息)

        方法抛出异常后执行,如果方法没有抛出异常,无法执行

      after:最终通知(应用:清理现场)

        方法执行完毕后执行,无论方法中是否出现异常

    当然,最重要也最常用的还是环绕通知,因为环绕通知必须手动执行目标方法,所以,可以代替其他几个通知。

    使用XML配置Spring整合AspectJ的AOP:

    1)项目整体结构如下:

    2)创建maven项目,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/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    	<groupId>com.zby</groupId>
    	<artifactId>aop</artifactId>
    	<version>0.0.1-SNAPSHOT</version>
    	<dependencies>
    		<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-context</artifactId>
    			<version>4.3.8.RELEASE</version>
    		</dependency>
    		<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-aop</artifactId>
    			<version>4.3.8.RELEASE</version>
    		</dependency>
    		<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-aspects</artifactId>
    			<version>4.3.8.RELEASE</version>
    		</dependency>
    		<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-test</artifactId>
    			<version>4.3.8.RELEASE</version>
    		</dependency>
    		<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
    		<dependency>
    			<groupId>org.aspectj</groupId>
    			<artifactId>aspectjweaver</artifactId>
    			<version>1.8.10</version>
    		</dependency>
    	</dependencies>
    </project>
    

    3)创建目标类UserService:

    package com.zby.service;
    
    public class UserService {
    
    	public void saveUser(String username, String password) {
    		System.out.println("save user[username=" + username + ",password=" + password + "]");
    	}
    
    }
    

    4)创建切面类:

    package com.zby.interceptor;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    
    public class MyAspect {
    
    	public void myBefore(JoinPoint joinPoint) {
    		System.out.println("前置通知 : " + joinPoint.getSignature().getName());
    	}
    
    
    
    	public void myAfterReturning(JoinPoint joinPoint, Object ret) {
    		System.out.println("后置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret);
    	}
    
    
    
    	public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {
    		System.out.println("环绕通知执行方法前");
    		// 手动执行目标方法
    		Object obj = joinPoint.proceed();
    
    		System.out.println("环绕通知执行方法后");
    		return obj;
    	}
    
    
    
    	public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
    		System.out.println("抛出异常通知 : " + e.getMessage());
    	}
    
    
    
    	public void myAfter(JoinPoint joinPoint) {
    		System.out.println("最终通知");
    	}
    }
    

    切面类没有实现接口,但是有几种方法参数,这些不是必须的。这些传入的对象是什么?当然是我们在切面点需要的信息!用脑壳想,在给一个方法进行增强的时候,前置方法,或者后置方法,或者环绕方法,有可能需要得到原方法的哪些信息,这里面都有。

    5)编写配置文件applicationContext.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"
    	xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
    		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    	<!-- 目标类 -->
    	<bean id="userService" class="com.zby.service.UserService"></bean>
    
    	<!-- 切面类 -->
    	<bean id="myInterceptor" class="com.zby.interceptor.MyAspect"></bean>
    
    	<aop:config>
    		<aop:aspect ref="myInterceptor">
    			<aop:pointcut expression="execution(* com.zby.service.UserService.*(..))"
    				id="myPointcut" />
    			<!--环绕通知 
    				<aop:around method="" pointcut-ref=""/> 
    				通知方法格式:public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ }
    				返回值类型:Object 方法名:任意 
    				参数:org.aspectj.lang.ProceedingJoinPoint 
    				抛出异常 
    				执行目标方法:Object obj = joinPoint.proceed(); 
    				例如: <aop:around method="myAround" pointcut-ref="myPointCut"/> -->
    			<aop:around method="myAround" pointcut-ref="myPointcut" />
    			<!-- 最终通知 -->
    
    			<aop:after method="myAfter" pointcut-ref="myPointcut" />
    			<!--后置通知 ,目标方法后执行,获得返回值
    			 	<aop:after-returning method="" pointcut-ref="" returning=""/> 
    			 	returning 通知方法第二个参数的名称 通知方法格式:
    			 	public void myAfterReturning(JoinPoint joinPoint,Object ret){}
    			 	 参数1:连接点描述 
    			 	 参数2:类型Object,参数名 returning="ret" 配置的 例如: 
    				<aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret" /> -->
    			<aop:after-returning method="myAfterReturning"
    				pointcut-ref="myPointcut" returning="ret" />
    			<!--抛出异常 <aop:after-throwing method="" pointcut-ref="" throwing=""/> 
    				throwing :通知方法的第二个参数名称 
    				通知方法格式:public void myAfterThrowing(JoinPoint joinPoint,Throwable e){ }
    				参数1:连接点描述对象 
    				参数2:获得异常信息,类型Throwable ,参数名由throwing="e" 配置 例如: <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/> -->
    			<aop:after-throwing method="myAfterThrowing"
    				pointcut-ref="myPointcut" throwing="e" />
    			<!--前置通知 
    				<aop:before method="" pointcut="" pointcut-ref=""/> 
    				method : 通知,及方法名
    				pointcut :切入点表达式,此表达式只能当前通知使用。 
    				pointcut-ref : 切入点引用,可以与其他通知共享切入点。 
    				通知方法格式:public void myBefore(JoinPoint joinPoint){} 
    				参数1:org.aspectj.lang.JoinPoint 用于描述连接点(目标方法),获得目标方法名等 
    				例如: <aop:before method="myBefore" pointcut-ref="myPointCut"/> -->
    			<aop:before method="myBefore" pointcut-ref="myPointcut" />
    		</aop:aspect>
    	</aop:config>
    </beans>
    

      

    6)编写测试类:

    package com.zby.test;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    import com.zby.service.UserService;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = { "classpath:applicationContext.xml" })
    public class AOPTest {
    
    	@Autowired
    	private UserService userService;
    
    
    
    	@Test
    	public void testProxy() {
    		System.out.println("After Proxy......");
    		userService.saveUser("zby", "1234567890");
    	}
    }
    

    7)控制台打印结果:

    六月 09, 2017 2:07:56 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper getDefaultTestExecutionListenerClassNames
    信息: Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]
    六月 09, 2017 2:07:56 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper instantiateListeners
    信息: Could not instantiate TestExecutionListener [org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/springframework/transaction/interceptor/TransactionAttribute]
    六月 09, 2017 2:07:56 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper instantiateListeners
    信息: Could not instantiate TestExecutionListener [org.springframework.test.context.transaction.TransactionalTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/springframework/transaction/interceptor/TransactionAttributeSource]
    六月 09, 2017 2:07:56 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper instantiateListeners
    信息: Could not instantiate TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [javax/servlet/ServletContext]
    六月 09, 2017 2:07:56 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper getTestExecutionListeners
    信息: Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@6e983d8d, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@4cf12cb4, org.springframework.test.context.support.DirtiesContextTestExecutionListener@6dae04e2]
    六月 09, 2017 2:07:56 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
    信息: Loading XML bean definitions from class path resource [applicationContext.xml]
    六月 09, 2017 2:07:56 下午 org.springframework.context.support.GenericApplicationContext prepareRefresh
    信息: Refreshing org.springframework.context.support.GenericApplicationContext@24024ad8: startup date [Fri Jun 09 14:07:56 CST 2017]; root of context hierarchy
    After Proxy......
    环绕通知执行方法前
    前置通知 : saveUser
    save user[username=zby,password=1234567890]
    后置通知 : saveUser , -->null
    最终通知
    环绕通知执行方法后
    

    这个DEMO就是一个大杂烩,其实使用时使用一个环绕通知就够了。再环绕通知里面必须手动执行方法,因此我们用try-catch把方法执行包裹起来,然后在执行前和执行后写增强代码即可。

    使用注解配置Spring整合AspectJ的AOP:

    1)上面的一二步骤不变。

    2)编写目标类UserService:

    package com.zby.service;
    
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserService {
    
    	public void saveUser(String username, String password) {
    		System.out.println("save user[username=" + username + ",password=" + password + "]");
    	}
    
    }
    

    3)编写切面类,使用注解:

    package com.zby.interceptor;
    
    import org.aspectj.lang.JoinPoint;
    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
    @Aspect
    public class MyAspect {
    	// 多个方法需要使用这个切入点表达式,定义为一个公用的
    	@Pointcut("execution(* com.zby.service..*(..))")
    	public void myPointCut() {
    
    	}
    
    
    
    	// 这里注解里面的值为上面的方法名
    	@Before("myPointCut()")
    	public void myBefore(JoinPoint joinPoint) {
    		System.out.println("前置通知 : " + joinPoint.getSignature().getName());
    	}
    
    
    
    	// 当你只有一个方法,或者只在这儿用,可以直接写切入点表达式
    	@AfterReturning(value = "execution(* com.zby.service..*(..))", returning = "ret")
    	public void myAfterReturning(JoinPoint joinPoint, Object ret) {
    		System.out.println("后置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret);
    	}
    
    
    
    	//
    	@Around("myPointCut()")
    	public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {
    		System.out.println("环绕通知执行方法前");
    		// 手动执行目标方法
    		Object obj = joinPoint.proceed();
    
    		System.out.println("环绕通知执行方法后");
    		return obj;
    	}
    
    
    
    	@AfterThrowing(value = "myPointCut()", throwing = "e")
    	public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
    		System.out.println("抛出异常通知 : " + e.getMessage());
    	}
    
    
    
    	@After("myPointCut()")
    	public void myAfter(JoinPoint joinPoint) {
    		System.out.println("最终通知");
    	}
    }
    

    4)编写配置文件applicationContext.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/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
    		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
    
    <context:component-scan base-package="com.zby"></context:component-scan>
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    </beans>
    

    5)编写测试类:

    package com.zby.test;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    import com.zby.service.UserService;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = { "classpath:applicationContext.xml" })
    public class AOPTest {
    
    	@Autowired
    	private UserService userService;
    
    
    
    	@Test
    	public void testProxy() {
    		System.out.println("After Proxy......");
    		userService.saveUser("zby", "1234567890");
    	}
    }
    

      6)控制台打印结果:

    六月 09, 2017 2:29:21 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper getDefaultTestExecutionListenerClassNames
    信息: Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]
    六月 09, 2017 2:29:21 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper instantiateListeners
    信息: Could not instantiate TestExecutionListener [org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/springframework/transaction/interceptor/TransactionAttribute]
    六月 09, 2017 2:29:21 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper instantiateListeners
    信息: Could not instantiate TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [javax/servlet/ServletContext]
    六月 09, 2017 2:29:21 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper instantiateListeners
    信息: Could not instantiate TestExecutionListener [org.springframework.test.context.transaction.TransactionalTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/springframework/transaction/interceptor/TransactionAttributeSource]
    六月 09, 2017 2:29:21 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper getTestExecutionListeners
    信息: Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@6dae04e2, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@3bc2c9af, org.springframework.test.context.support.DirtiesContextTestExecutionListener@71471ecf]
    六月 09, 2017 2:29:21 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
    信息: Loading XML bean definitions from class path resource [applicationContext.xml]
    六月 09, 2017 2:29:21 下午 org.springframework.context.support.GenericApplicationContext prepareRefresh
    信息: Refreshing org.springframework.context.support.GenericApplicationContext@69f31d: startup date [Fri Jun 09 14:29:21 CST 2017]; root of context hierarchy
    After Proxy......
    环绕通知执行方法前
    前置通知 : saveUser
    save user[username=zby,password=1234567890]
    环绕通知执行方法后
    最终通知
    后置通知 : saveUser , -->null
    

    总结:对比起来,可以看出来使用最后一种方式开发AOP很方便,这也是我们最常用的,至于spring原生的AOP,大多在一些框架里面看到。使用整合AspectJ的方式,最主要的是要注意切面表达式的书写和方法参数传入,以及怎么使用这些参数。

  • 相关阅读:
    秋意浓浓回成都2月杂记
    验证表单的js代码段
    C#算法小程序(1)
    C#数据结构之单向链表
    Excel VBA编程的常用代码
    我的漫漫系分路
    2007年下半年系统分析师上午试卷及参考答案
    「2014年間休日カレンダー」のご案内
    Eclipse的几点使用技巧
    沧海一粟小组(第一次作业)
  • 原文地址:https://www.cnblogs.com/zby9527/p/6951344.html
Copyright © 2011-2022 走看看