Spring07-AOP
切面
切面泛指交叉业务逻辑。比如事务处理、日志处理就可以理解为切面。常用的切面
有通知与顾问。实际就是对主业务逻辑的一种增强。
通知(Advice)
通知是切面的一种实现,可以完成简单织入功能(织入功能就是在这里完成的)。
通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之
后执行等。通知类型不同,切入时间不同。
Spring对AOP的实现
1. 通知的用法(基于Schema-based方式)
- 定义通知类
//切面:前置通知
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
/**
* method:目标方法
* args:目标方法参数列表
* target:目标对象
*/
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("前置方法的before()方法执行!");
}
}
//切面:后置通知
public class MyAfterReturningAdvice implements AfterReturningAdvice{
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("后置通知的afterReturning()方法执行!returnValue: " + returnValue);
if (returnValue != null) {
System.out.println(((String)returnValue).toUpperCase());
}
}
}
//切面:环绕通知
public class MyMethodInterceptor implements MethodInterceptor {
/**
* invocation: 方法调用器
*/
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("环绕通知:目标方法执行之前!");
//调用执行目标方法
Object result = invocation.proceed();
if (result != null) {
result = ((String)result).toUpperCase();
}
System.out.println("环绕通知:目标方法执行之后!");
return result;
}
}
//切面:异常通知(发生异常,目标方法不再执行)
public class MyThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(Exception ex) {
System.out.println("异常通知执行!" + ex);
}
}
- 定义目标类
package com.caorui.service;
public interface SomeService {
void doSome();
String doOther();
}
package com.caorui.service.impl;
import com.caorui.service.SomeService;
public class SomeServiceImpl implements SomeService {
public SomeServiceImpl() {
System.out.println("SomeServiceImpl.SomeServiceImpl()");
}
@Override
public void doSome() {
// System.out.println("SomeServiceImpl.doSome()");
System.out.println("SomeServiceImpl.doSome()" + 1/0);
}
@Override
public String doOther() {
System.out.println("SomeServiceImpl.doOther()");
return "love";
}
}
- 注册目标类,注册通知切面,注册代理工厂Bean类对象ProxyFactoryBean
<?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">
<!-- 注册目标类 -->
<!-- bean的定义:以下配置相当于SomeService service = new SomeServiceImpl(); id相当于service这个引用 -->
<bean id="someServiceImpl" class="com.caorui.service.impl.SomeServiceImpl"></bean>
<!-- 注册切面,前置通知 -->
<bean id="myMethodBeforeAdvice" class="com.caorui.aspects.MyMethodBeforeAdvice"></bean>
<!-- 注册切面,后置通知 -->
<bean id="myAfterReturningAdvice" class="com.caorui.aspects.MyAfterReturningAdvice"></bean>
<!-- 注册切面,环绕通知 -->
<bean id="myMethodInterceptor" class="com.caorui.aspects.MyMethodInterceptor"></bean>
<!-- 注册切面,异常通知 -->
<bean id="myThrowsAdvice" class="com.caorui.aspects.MyThrowsAdvice"></bean>
<!-- 注册代理 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 指定目标对象 -->
<property name="target" ref="someServiceImpl"></property>
<!-- 指定目标类实现的所有接口 -->
<property name="interfaces" value="com.caorui.service.SomeService"></property>
<!-- 指定切面 -->
<property name="interceptorNames" value="myThrowsAdvice"></property>
</bean>
</beans>
注意:针对interceptorNames、interfaces后面为什么使用value?
- proxyInterfaces(底层传值类型Class[])通过标签array可以设置多个值,只有一个值时,value=""
底层:
proxyInterfaces-ProxyFactoryBean.setProxyInterfaces(Class[] proxyInterfaces) //.xml文件中给不了class,只能给全限定名
- target(底层传值类型Object target)
底层:
target-Advised.setTarget(Object object)//Object所以使用ref传对象
- interceptorNames:通知切面类的名称,类型是String[],如果设置一个值value=""
底层:
interceptorNames-ProxyFactoryBean.setInterceptorNames(String[] interceptorNames)
所以interceptorNames后面使用value传值,如果是多个值,则加一个array或者list。
- 客户端访问动态代理对象
package com.caorui.test;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import com.caorui.service.SomeService;
import com.caorui.service.impl.SomeServiceImpl;
public class SomeTest {
@Test
public void testSome01() {
//创建容器对象,ApplicationContext初始化时,所有容器中beans创建完毕
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
SomeService service = ac.getBean("proxyFactoryBean", SomeService.class);
service.doSome();
String result = service.doOther();
System.out.println(result);
}
}