三大组件之Spring 第三章Spring与AOP

3.1 AOP的通知

(1 )定义目标类
定义目标类,就是定义之前的普通 Bean 类,也就是即将被增强的 Bean 类。
(2 )定义通知类
通知类是指,实现了相应通知类型接口的类。当前,实现了这些接口,就要实现这些接
口中的方法,而这些方法的执行,则是根据不同类型的通知,其执行时机不同。
前置通知:在目标方法执行之前执行
后置通知:在目标方法执行之后执行
环绕通知:在目标方法执行之前与之后均执行
异常处理通知:在目标方法执行过程中,若发生指定异常,则执行通知中的方法
(3 )注册目标类
即在 Spring 配置文件中注册目标对象 Bean。
4 )注册通知切面
即在 Spring 配置文件中注册定义的通知对象 Bean。
(5 )注册代理厂 工厂 Bean 类对象 ProxyFactoryBean
这里的代理使用的是 ProxyFactoryBean 类。代理对象的配置,是与 JDK 的 Proxy 代理参数是一致的,都需要指定三部分:目标类,接口,切面。
<property name=“target” ref=“目标对象 Bean 的 id" />
指定目标对象的 Bean 的 id。也可写为如下形式:
<property name=“targetName” value=“目标对象 Bean 的 id" />
<property name=“proxyInterfaces” value=“接口全限定性名" />
设置目标对象所实现的业务接口,要求给出接口的全既定性类名。此属性可以不进行设
置,因为打开 ProxyFactoryBean 的源码,可以看到其有个自动检测目标类的所有接口属性
autodetectInterfaces,默认值为 true。即不设置也可以自动检测到。当然,此时使用的是 jdk
的 Proxy 动态代理。

3.1.1前置通知

07-package com.bjpowernode.aop01;
定义接口省略
实现接口类

public class SomeServiceImpl implements ISomeService {

	@Override
	public void doFirst() {

		System.out.println("执行doFirst()方法");


	}

	@Override
	public void doSecond() {

		System.out.println("执行doSecond()方法");


	}

}

AOP前置通知类

//前置通知
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
	
	//当前方法在目标方法执行之前执行
	//method:目标方法
	//args:目标方法的参数列表
	//target:目标对象
	
	@Override
	public void before(Method method, Object[] args, Object target) 
			throws Throwable {
		//对于目标方法的增强代码写在这里
		System.out.println("执行前置通知方法");
		
	}

	

}

配置文件注册
注册Bean对象,注册Service前置通知,注册动态代理,并把前两个放入代理中。

<!-- 注册目标对象  -->
    <bean id="someService" class="com.bjpowernode.aop01.SomeServiceImpl"/>

    <!-- 注册切面:通知  -->
    <bean id="myAdvice" class="com.bjpowernode.aop01.MyMethodBeforeAdvice"/>
 
    <!-- 生成代理对象 -->
    <bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    <!--  <property name="targetName" value ="someService"/>   -->
     <!-- 指定目标对象 -->
    	<property name="target" ref="someService"/> 	
     <!-- 指定切面 -->
    	<property name="interceptorNames" value="myAdvice"/>
    	
    </bean>

测试类
这样getBean直接获取代理就可以实现接口的方法了

	@Test
	public void test01() {
		
		//前置通知
		
		String resource = "com/bjpowernode/aop01/applicationContext.xml";
		ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
		ISomeService service = (ISomeService) ac.getBean("serviceProxy");
		service.doFirst();
		System.out.println("==============");
		service.doSecond();

		
	}

3.1.2 后置通知

后置通知:可以获取到目标方法的返回结果,但无法改变目标方法的结果 因为这个方法是void
07-package com.bjpowernode.aop02;
实现接口类

public class SomeServiceImpl implements ISomeService {

	@Override
	public void doFirst() {

		System.out.println("执行doFirst()方法");


	}

	@Override
	public String doSecond() {

		System.out.println("执行doSecond()方法");
		
		return "abcde";


	}

}

后置通知

public class MyAfterReturningAdvice implements org.springframework.aop.AfterReturningAdvice {

	//在目标方法执行之后执行
	//returnValue:目标方法的返回值
	@Override
	public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
		 
		
		System.out.println("执行后置通知方法 returnValue = " + returnValue);
		
		if (returnValue != null) {//因为第一个方法是void 所以需要加判断
			returnValue = ((String) returnValue).toUpperCase();
			System.out.println("修改过的结果 returnValue = " + returnValue);
		}
		

	}

}

配置文件稍作改动 改通知那一行

<!-- 注册目标对象  -->
    <bean id="someService" class="com.bjpowernode.aop02.SomeServiceImpl"/>

    <!-- 注册切面:通知  -->
    <bean id="myAdvice" class="com.bjpowernode.aop02.MyAfterReturningAdvice"/>
 
    <!-- 生成代理对象 -->
    <bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">

     <!-- 指定目标对象 -->
    	<property name="target" ref="someService"/> 	
     <!-- 指定切面 -->
    	<property name="interceptorNames" value="myAdvice"/>
    	
    </bean>

测试类

	@Test
	public void test01() {
		
		//前置通知
		
		String resource = "com/bjpowernode/aop02/applicationContext.xml";
		ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
		ISomeService service = (ISomeService) ac.getBean("serviceProxy");
		service.doFirst();
		System.out.println("==============");
		String result = service.doSecond();
		System.out.println(result);
		
	}

*结果

执行doFirst()方法
执行后置通知方法 returnValue = null
“===========”
执行doSecond()方法
执行后置通知方法 returnValue = ABCDE
abcde

3.13 环绕通知

环绕通知:可以修改目标方法的返回结果

07-package com.bjpowernode.aop03;

注册文件也是只修改advice那一行

<!-- 注册目标对象  -->
    <bean id="someService" class="com.bjpowernode.aop03.SomeServiceImpl"/>

    <!-- 注册切面:通知  -->
    <bean id="myAdvice" class="com.bjpowernode.aop03.MyMethodInterceptor"/>
 
    <!-- 生成代理对象 -->
    <bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">

     <!-- 指定目标对象 -->
    	<property name="target" ref="someService"/> 	
     <!-- 指定切面 -->
    	<property name="interceptorNames" value="myAdvice"/>
    	
    </bean>

环绕通知:可以修改目标方法的返回结果

public class MyMethodInterceptor implements MethodInterceptor {

	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		System.out.println("执行环绕通知:目标方法执行之前");
		//执行目标方法
		Object result = invocation.proceed();
		System.out.println("执行环绕通知:目标方法执行之后");
		
		if(result != null) {
			return ((String)result).toUpperCase();
		}
		
		return result;
	}

}

测试类不变

@Test
	public void test01() {
		
		//环绕通知
		
		String resource = "com/bjpowernode/aop03/applicationContext.xml";
		ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
		ISomeService service = (ISomeService) ac.getBean("serviceProxy");
		service.doFirst();
		System.out.println("==============");
		String result = service.doSecond();
		System.out.println(result);
		
	}

结果

执行环绕通知:目标方法执行之前
执行doFirst()方法
执行环绕通知:目标方法执行之后
“==============”
执行环绕通知:目标方法执行之前
执行doSecond()方法
执行环绕通知:目标方法执行之后
ABCDE

3.14异常通知

当目标方法抛出与指定类型的异常具有is-a(子类)关系的异常时,执行当前方法
07-package com.bjpowernode.aop04;

异常通知类

public class MyThrowsAdvice implements ThrowsAdvice {
	
		//当目标方法抛出与指定类型的异常具有is-a(子类)关系的异常时,执行当前方法
		public void afterThrowing(Exception ex) {
			 System.out.println("执行异常通知方法");

		}
}

注册文件修改

<!-- 注册切面:通知  -->
    <bean id="myAdvice" class="com.bjpowernode.aop04.MyThrowsAdvice"/>

在实现类中写一个3 / 0异常

public class SomeServiceImpl implements ISomeService {

	@Override
	public void doFirst() {

		System.out.println("执行doFirst()方法" + 3 / 0);


	}

	@Override
	public String doSecond() {

		System.out.println("执行doSecond()方法");
		
		return "abcde";


	}

}

测试类不变
结果:
执行异常通知方法

3.15捕获自定义异常

07-package com.bjpowernode.aop05;
接口类 登录方法

public interface ISomeService {
	boolean login(String username, String password) throws UserException;
	
}

实现接口 并判断传入的参数是否正确,不正确抛出异常信息
在这里添加一行double a =3 / 0;的其他异常

public class SomeServiceImpl implements ISomeService {

	@Override
	public boolean login(String username, String password) throws UserException {
		if(!"beijing".equals(username)) {
			throw new UsernameException("用户名输错了!");
		}
		
		if(!"111".equals(password)) {
			throw new PasswordException("密码输错了!");
		}
		
		double a =3 / 0;
		return true;
		
	}

	
}

异常分两种:
1)运行时异常,不进行处理,也可以通过编译。
若一个类继承自RunTimeException,则该异常就是运行时异常
2)编译时异常,受查异常,Checked Exception。不进行处理,将无法通过编译。
若一个类继承自Exception,则该异常就是受查异常

自定义异常类

public class UserException extends Exception {

	public UserException() {
		super();
		 
	}

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

自定义用户名异常类和密码异常类分别都继承自UserException
密码

public class PasswordException extends UserException {

	public PasswordException() {
		super();
		// TODO Auto-generated constructor stub
	}

	public PasswordException(String message) {
		super(message);
		// TODO Auto-generated constructor stub
	}
		
		
	
}

用户名

public class UsernameException extends UserException {

	public UsernameException() {
		super();
		// TODO Auto-generated constructor stub
	}

	public UsernameException(String message) {
		super(message);
		// TODO Auto-generated constructor stub
	}
	
}

异常通知类
根据不同异常 输出不同结果

public class MyThrowsAdvice implements ThrowsAdvice {
				
		//当目标方法抛出UsernameException异常时,执行当前方法
		public void afterThrowing(UsernameException ex) {
			 System.out.println("发生用户名异常 ex = " + ex.getMessage());

		}
		
		//当目标方法抛出PasswordException异常时,执行当前方法
		public void afterThrowing(PasswordException ex) {
			System.out.println("发生密码异常 ex = " + ex.getMessage());
			
		}
		
		//当目标方法抛出其他异常时,执行当前方法
		public void afterThrowing(Exception ex) {
			System.out.println("发生密码异常 ex = " + ex.getMessage());
			
		}
}

测试类

@Test
	public void test01() throws UserException {
		
		//异常通知
		
		String resource = "com/bjpowernode/aop05/applicationContext.xml";
		ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
		ISomeService service = (ISomeService) ac.getBean("serviceProxy");
		
		service.login("beijing","111");

	
		
	}

这里用throws是将异常抛给虚拟机,程序会报错,输出异常结果;
如果用try catch 虚拟机是不会收到异常的,异常会输出在Console里。
结果
发生密码异常 ex = / by zero
如果是用户名错了,结果是:发生用户名异常 ex = 用户名输错了!

3.16 给目标方法织入多个切面

07-package com.bjpowernode.aop06;
其他省略,只写配置文件,导入了前置通知后后置通知。
两种写法

  <!-- 注册目标对象  -->
    <bean id="someService" class="com.bjpowernode.aop06.SomeServiceImpl"/>

    <!-- 注册切面:通知  -->
    <bean id="myBeforeAdvice" class="com.bjpowernode.aop06.MyAfterReturningAdvice"/>
    <bean id="myAfterAdvice" class="com.bjpowernode.aop06.MyMethodBeforeAdvice"/>
 
    <!-- 生成代理对象 -->
    <bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">

     <!-- 指定目标对象 -->
    	<property name="target" ref="someService"/> 	
    	**第一种写法**
    	<property name="interceptorNames" value = "myBeforeAdvice, myAfterAdvice"/>
     <!-- 指定切面
     **第二种写法**
    	<property name="interceptorNames">
    			<array>
    				<value>myBeforeAdvice</value>
    				<value>myAfterAdvice</value>
    			
    			</array>
    	</property>
    	 -->
    </bean>

无接口的 CGLIB 代理生成

当去掉接口类时,其他不变,service自动使用CGLIB 代理 可以使用debug查看

07-package com.bjpowernode.aop07;

@Test
	public void test01() {
		
		//前置通知
		
		String resource = "com/bjpowernode/aop07/applicationContext.xml";
		ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
		SomeService service = (SomeService) ac.getBean("serviceProxy");
		service.doFirst();
		System.out.println("==============");
		String result = service.doSecond();
		System.out.println(result);
		
	}

有接口的 CGLIB 代理生成

若存在接口,但又需要使用 CGLIB 生成代理对象,此时,只需要在配置文件中增加一个
proxyTargetClass 属性设置,用于指定强制使用 CGLIB 代理机制。
07-package com.bjpowernode.aop08;
只修改配置文件、
两种方法

   <!-- 注册目标对象  -->
    <bean id="someService" class="com.bjpowernode.aop08.SomeServiceImpl"/>

    <!-- 注册切面:通知  -->
    <bean id="myAdvice" class="com.bjpowernode.aop08.MyAfterReturningAdvice"/>
 
    <!-- 生成代理对象 -->
    <bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">

     <!-- 指定目标对象 -->
    	<property name="target" ref="someService"/> 	
     <!-- 指定切面 -->
    	<property name="interceptorNames" value="myAdvice"/>
    	1<property name="optimize" value="true"/>
    	2<property name="proxyTargetClass" value="true"/>
    	
    </bean>

3.4 顾问 Advisor

通知(Advice)是 Spring 提供的一种切面(Aspect)。但其功能过于简单:只能将切面织
入到目标类的所有目标方法中,无法完成将切面织入到指定目标方法中。
顾问(Advisor)是 Spring 提供的另一种切面。其可以完成更为复杂的切面织入功能。
PointcutAdvisor 是顾问的一种,可以指定具体的切入点。顾问将通知进行了包装,会根据不
同的通知类型,在不同的时间点,将切面织入到不同的切入点。

3.4.1名称匹配方法切入点顾问

顾问是将通知包装了,因此需要配通知属性和切入点(具体增强的方法)。
再把顾问配置到代理里就可以用了。

顾问选择切入点的方式也有很多,可以只增强一个、多个、通配符形式。

07-package com.bjpowernode.aop09;
其他类省略
测试类

@Test
	public void test01() {
		
		//前置通知
		
		String resource = "com/bjpowernode/aop09/applicationContext.xml";
		ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
		ISomeService service = (ISomeService) ac.getBean("serviceProxy");
		service.doFirst();
		System.out.println("==============");

		service.doSecond();
		System.out.println("==============");
		
		service.doThird();
	}

主要是配置文件写法
NameMatchMethodPointcutAdvisor,即名称匹配方法切入点顾问。容器可根据配置文件
中指定的方法名来设置切入点。
代码不用修改,只在配置文件中注册一个顾问,然后使用通知属性 advice 与切入点的
方法名 mappedName 对其进行配置。代理中的切面,使用这个顾问即可。

这里匹配的对象是简单方法名

<!-- 注册目标对象  -->
    <bean id="someService" class="com.bjpowernode.aop09.SomeServiceImpl"/>

    <!-- 注册切面:通知  -->
    <bean id="myAdvice" class="com.bjpowernode.aop09.MyAfterReturningAdvice"/>
    
    <!-- 注册切面:顾问  -->
    <bean id="myAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
 		<property name="advice" ref="myAdvice"/>
 		<!-- 指定切入点 
 		1<property name="mappedName" value="doFirst"/>
 		2<property name="mappedNames" value="doFirst, doSecond"/>
 		 -->
 		3<property name="mappedName" value="*ir*"/>
 		
 	</bean>
 	
    <!-- 生成代理对象 -->
    <bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">

     <!-- 指定目标对象 -->
    	<property name="target" ref="someService"/> 	
     <!-- 指定切面 -->
    	<property name="interceptorNames" value="myAdvisor"/>
    	
    </bean>

3.4.2 正则表达式方法切入点顾问

RegexpMethodPointcutAdvisor,即正则表达式方法顾问。容器可根据正则表达式来设置
切入点。注意,与正则表达式进行匹配的对象是接口中的方法名,而非目标类(接口的实现
类)的方法名。

配置文件
这里的正则表达式匹配的对象是全限定性方法名

<!-- 注册目标对象  -->
    <bean id="someService" class="com.bjpowernode.aop10.SomeServiceImpl"/>

    <!-- 注册切面:通知  -->
    <bean id="myAdvice" class="com.bjpowernode.aop10.MyAfterReturningAdvice"/>
    
    <!-- 注册切面:顾问  -->
    <bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
 		<property name="advice" ref="myAdvice"/>
 		<!--  
 		<property name="pattern" value=".*doFirst"/>这里的正则表达式匹配的对象是全限定性方法名
 		<property name="patterns" value=".*doFirst,.*doSecond"/>
 		<property name="patterns" value=".*doFirst|,.*doSecond"/>
 		-->
 		<property name="patterns" value=".*S.*"/>这个是错误的 三个方法会都匹配
 		
 	</bean>
 	
    <!-- 生成代理对象 -->
    <bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">

     <!-- 指定目标对象 -->
    	<property name="target" ref="someService"/> 	
     <!-- 指定切面 -->
    	<property name="interceptorNames" value="myAdvisor"/>
    	
    </bean>

3.4.3 默认advisor自动代理生成器

然而以上的代码存在两个问题:
1)若存在多个目标对象,就需要使用多次ProxyFactoryBean来创建多个代理对象,这会使配置文件变得臃肿,不便于管理。
2)用户真正想调用的是目标对象,而真正可以调用的却是代理对象,这不符合正常的逻辑
以上两个问题,均为ProxyFactoryBean类的功能太简单引起的。
07 package com.bjpowernode.aop11;
配置文件

<!-- 注册目标对象  -->
    <bean id="someService" class="com.bjpowernode.aop11.SomeServiceImpl"/>
    <bean id="someService2" class="com.bjpowernode.aop11.SomeServiceImpl"/>

    <!-- 注册切面:通知  -->
    <bean id="myAdvice" class="com.bjpowernode.aop11.MyAfterReturningAdvice"/>
    
    <!-- 注册切面:顾问  -->
    <bean id="myAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
 		<property name="advice" ref="myAdvice"/>
 		<property name="mappedName" value="doFirst"/>
 		
 	</bean>
 	
    <!-- 注册自动代理生成器 -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

测试类

@Test
	public void test01() {
		
		//前置通知
		
		String resource = "com/bjpowernode/aop11/applicationContext.xml";
		ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
		ISomeService service = (ISomeService) ac.getBean("someService");
		service.doFirst();
		System.out.println("==============");

		service.doSecond();
		System.out.println("==============");
		
		service.doThird();
		
		System.out.println("--------------");
		
		ISomeService service2 = (ISomeService) ac.getBean("someService2");
		service2.doFirst();
		System.out.println("==============");
		
		service2.doSecond();
		System.out.println("==============");
		
		service2.doThird();
	}

结果

执行doFirst()方法
执行后置通知方法 returnValue = null
==============
执行doSecond()方法
==============
执行doThird()方法
--------------
执行doFirst()方法
执行后置通知方法 returnValue = null
==============
执行doSecond()方法
==============
执行doThird()方法

3.4.4 Bean名称自动代理生成器

DefaultAdvisorAutoProxyCreator存在三个问题:
1)不能选择目标对象
2)不能选择切面类型,切面只能是advisor
2)不能选择advisor,所有advisor均将被作为切面织入到目标方法
配置文件写法

<!-- 注册目标对象  -->
    <bean id="someService" class="com.bjpowernode.aop12.SomeServiceImpl"/>
    <bean id="someService2" class="com.bjpowernode.aop12.SomeServiceImpl"/>

    <!-- 注册切面:通知  -->
    <bean id="myAdvice" class="com.bjpowernode.aop12.MyAfterReturningAdvice"/>
    
    <!-- 注册切面:顾问  -->
    <bean id="myAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
 		<property name="advice" ref="myAdvice"/>
 		<property name="mappedName" value="doFirst"/>
 		
 	</bean>
 	
    <!-- 注册自动代理生成器 -->
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
		<property name="beanNames" value="someService"/>
		<property name="interceptorNames" value="myAdvisor"/>
	</bean>

最终实现了只增强了第一个service目标对象中的doFirst方法

3.6 AspectJ 对AOP 的实现

AspectJ与spring没有关系,这是两个框架,只不过AspectJ比较小,只实现了AOP。所以Spring将AspectJ引入到自己的框架中来。
对于 AOP 这种编程思想,很多框架都进行了实现。Spring 就是其中之一,可以完成面向
切面编程。然而,AspectJ 也实现了 AOP 的功能,且其实现方式更为简捷,使用更为方便,
而且还支持注解式开发。所以,Spring 又将 AspectJ 的对于 AOP 的实现也引入到了自己的框
架中。
在 Spring 中使用 AOP 开发时,一般使用 AspectJ 的实现方式。

3.6.1AspectJ 中常用的通知有五种类型:

(1)前置通知
(2)后置通知
(3)环绕通知
(4)异常通知
(5)最终通知
其中最终通知是指,无论程序执行是否正常,该通知都会执行。类似于 try…catch 中的
finally 代码块。

3.6.2AspectJ 的切入点表达式

AspectJ 除了提供了六种通知外,还定义了专门的表达式用于指定切入点。表达式的原
型是:
只有加粗的部分是不可省略的
execution ( [modifiers-pattern] 访问权限类型
ret-type-pattern 返回值类型
[declaring-type-pattern] 全限定性类名
name-pattern(param-pattern) 方法名(参数名)
[throws-pattern] 抛出异常类型
)
切入点表达式要匹配的对象就是目标方法的方法名。所以,execution 表达式中明显就
是方法的签名。注意,表达式中加[ ]的部分表示可省略部分,各部分间用空格分开。在其中
可以使用以下符号:
在这里插入图片描述
示例1:

execution(* *.service.*.*(..))

解析:从后往前括号里的”点点“表示任意多个参数,第四个星号挨着括号所以表示方法名,方法名之前的第三个星号则表示所有子类或接口,第二个星表示包,第一个星表示返回值类型

表示前面只能有一级包下的service子包下所有类(接口)中所有方法为切入点

示例2:

execution(* *..service.*.doSome())

指定所有包下的 serivce 子包下所有类中的 doSome()方法为切入点

3.6.3 AspectJ 基于注解的 的 AOP 实现

接口类省略了,看实现类即可

08-package com.bjpowernode.annotation;

public class SomeServiceImpl implements ISomeService {

	@Override
	public void doFirst() {

		System.out.println("执行doFirst()方法");


	}

	@Override
	public String doSecond() {

		System.out.println("执行doSecond()方法");
		
		return "abcde";

	}

	@Override
	public void doThird() {
		System.out.println("执行doThird()方法");
		
	}

}

切面类 设置doFirst为前置通知

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect // 表示当前类为切面
public class MyAspect {
	
	@Before("execution(* *..ISomeService.doFirst(..))")
	public void before() {
		System.out.println("执行前置通知方法");
	}
	
	@Before("execution(* *..ISomeService.doFirst(..))")
	public void before(JoinPoint jp) {
		System.out.println("执行前置通知方法 jp = " + jp);
	}
}

注册文件

<!-- 注册切面 -->
  	<bean id="myAspect" class="com.bjpowernode.annotation.MyAspect"/>
    
	<!-- 注册目标对象 -->
  	<bean id="someService" class="com.bjpowernode.annotation.SomeServiceImpl"/>
  	
	<!-- 注册AspectJ的自动代理 -->
  	<aop:aspectj-autoproxy/>

测试类

@Test
	public void test01() {
		
		//前置通知
		
		String resource = "com/bjpowernode/annotation/applicationContext.xml";
		ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
		
		ISomeService service = (ISomeService) ac.getBean("someService");
		service.doFirst();
		System.out.println("---------------------");
		service.doSecond();
		System.out.println("---------------------");
		service.doThird();
	}

结果:
执行前置通知方法
执行前置通知方法 jp = execution(void com.bjpowernode.annotation.ISomeService.doFirst())
执行doFirst()方法
---------------------‘
执行doSecond()方法
---------------------’
执行doThird()方法

基于注解的AOP的其他通知类型

加入后置通知,环绕通知,异常通知,最终通知,PointCut()切入点
在实现类里加入一行3/0异常

public class SomeServiceImpl implements ISomeService {

	@Override
	public void doFirst() {

		System.out.println("执行doFirst()方法");


	}

	@Override
	public String doSecond() {

		System.out.println("执行doSecond()方法");
		
		return "abcde";

	}

	@Override
	public void doThird() {
		System.out.println("执行doThird()方法" +  3 / 0);
		System.out.println("执行doThird()方法");
		
	}

}

切面类

@Aspect // 表示当前类为切面
public class MyAspect {
	
	@Before("execution(* *..ISomeService.doFirst(..))")
	public void myBefore() {
		System.out.println("执行前置通知方法");
	}
	
	@Before("execution(* *..ISomeService.doFirst(..))")
	public void myBefore(JoinPoint jp) {
		System.out.println("执行前置通知方法 jp = " + jp);
	}
	
	@AfterReturning("execution(* *..ISomeService.doSecond(..))")
	public void myAfterReturning() {
		System.out.println("执行后置通知方法");
		
	}
	
	@AfterReturning(value="execution(* *..ISomeService.doSecond(..))", returning="result")
	public void myAfterReturning(Object result) {
		System.out.println("执行后置通知方法 result = " + result);
		
	}
	
	@Around("execution(* *..ISomeService.doSecond(..))")
	public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
		System.out.println("执行环绕通知方法,目标方法执行之前");
		//执行目标方法
		Object result = pjp.proceed();
		System.out.println("执行环绕通知方法,目标方法执行之后");
		if(result != null) {
			return ((String)result).toUpperCase();
		}
		return result;
	}
	
	@AfterThrowing("execution(* *..ISomeService.doThird(..))")
	public void myAfterThrowing() {
		System.out.println("执行异常通知方法");
		
	}
	
	@AfterThrowing(value="execution(* *..ISomeService.doThird(..))", throwing = "ex")
	public void myAfterThrowing(Exception ex) {
		System.out.println("执行异常通知方法 ex = " + ex.getMessage());
		
	}
	
	@After("doThirdPointcut()")
	public void myAfter() {
		System.out.println("执行最终通知方法");
	}
	
	//定义了一个切入点,叫doThirdPointcut()
	@Pointcut("execution(* *..ISomeService.doThird(..))")
	public void doThirdPointcut() {}
	
}

结果:

执行前置通知方法
执行前置通知方法 jp = execution(void com.bjpowernode.annotation.ISomeService.doFirst())
执行doFirst()方法
---------------------
执行环绕通知方法,目标方法执行之前
执行doSecond()方法
执行环绕通知方法,目标方法执行之后
执行后置通知方法 result = ABCDE
执行后置通知方法
---------------------
执行最终通知方法

3.6.4 AspectJ 基于 XML 的 AOP 实现

AspectJ 除了提供了基于注解的 AOP 的实现外,还提供了以 XML 方式的实现。
切面就是一个 POJO 类,而用于增强的方法就是普通的方法。通过配置文件,将切面中
的功能增强织入到了目标类的目标方法中。
08-package com.bjpowernode.xml;
切面类取消注解

public class MyAspect {
	
	public void myBefore() {
		System.out.println("执行前置通知方法");
	}
	
	public void myBefore(JoinPoint jp) {
		System.out.println("执行前置通知方法 jp = " + jp);
	}
	
	public void myAfterReturning() {
		System.out.println("执行后置通知方法");
		
	}
	
	public void myAfterReturning(Object result) {
		System.out.println("执行后置通知方法 result = " + result);
		
	}
	
	public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
		System.out.println("执行环绕通知方法,目标方法执行之前");
		//执行目标方法
		Object result = pjp.proceed();
		System.out.println("执行环绕通知方法,目标方法执行之后");
		if(result != null) {
			return ((String)result).toUpperCase();
		}
		return result;
	}
	
	public void myAfterThrowing() {
		System.out.println("执行异常通知方法");
		
	}
	
	public void myAfterThrowing(Exception ex) {
		System.out.println("执行异常通知方法 ex = " + ex.getMessage());
		
	}
	
	public void myAfter() {
		System.out.println("执行最终通知方法");
	}
	
	public void doThirdPointcut() {}
	
}

注册文件

<!-- 注册切面 -->
  	<bean id="myAspect" class="com.bjpowernode.xml.MyAspect"/>
    
	<!-- 注册目标对象 -->
  	<bean id="someService" class="com.bjpowernode.xml.SomeServiceImpl"/>
  	
	<!-- AOP配置 -->
  	<aop:config>
  		<aop:pointcut expression="execution(* *..ISomeService.doFirst(..))" id="doFirstPointcut"/>
  		<aop:pointcut expression="execution(* *..ISomeService.doSecond(..))" id="doSecondPointcut"/>
  		<aop:pointcut expression="execution(* *..ISomeService.doThird(..))" id="doThirdPointcut"/>
  		<aop:aspect ref="myAspect">
  			<aop:before method="myBefore" pointcut-ref="doFirstPointcut"/>
  			<aop:before method="myBefore(org.aspectj.lang.JoinPoint)" pointcut-ref="doFirstPointcut"/>
  			
  			<aop:after method="myAfterReturning" pointcut-ref="doSecondPointcut"/>
  			<aop:after-returning method="myAfterReturning(java.lang.Object)" pointcut-ref="doSecondPointcut" returning="result"/>
  			
  			<aop:around method="myAround" pointcut-ref="doSecondPointcut"/>
  			<aop:after-throwing method="myAfterThrowing" pointcut-ref="doThirdPointcut"/>
  			<aop:after-throwing method="myAfterThrowing(java.lang.Exception)" pointcut-ref="doThirdPointcut" throwing="ex"/>
  			
  			<aop:after method="myAfter" pointcut-ref="doThirdPointcut"/>
  			
  		</aop:aspect>
  	</aop:config>

执行结果:

执行前置通知方法
执行前置通知方法 jp = execution(void com.bjpowernode.xml.ISomeService.doFirst())
执行doFirst()方法
---------------------
执行环绕通知方法,目标方法执行之前
执行doSecond()方法
执行环绕通知方法,目标方法执行之后
执行后置通知方法 result = ABCDE
执行后置通知方法
---------------------
执行异常通知方法 ex = / by zero
执行异常通知方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值