通知(Advice),切面的一种实现,可以完成简单织入功能(织入功能就是在这里完成的)。常用通知有:前置通知、后置通知、环绕通知、异常处理通知
一、前置通知(MethodBeforeAdvice)
定义前置通知需要实现MethodBeforeAdvice接口。该接口中有一个方法before(),会在目标方法执行之前执行。前置通知的特点:
- 在目标方法执行之前先执行
- 不改变目标方法的执行流程,前置通知代码不能阻止目标方法执行
- 不改变目标方法执行的结果
接口类
//主业务接口
public interface ISomeService {
String doFirst();
void doSecond();
}
实现类
//目标类:代理类要增强的类
public class SomeServiceImpl implements ISomeService {
@Override
public String doFirst() {
//SystemService.doTx();
System.out.println("执行doFirst()方法");
//SystemService.doLog();
return "abcde";
}
@Override
public void doSecond() {
//SystemService.doTx();
System.out.println("执行doSecond()方法");
//SystemService.doLog();
}
}
前置通知类
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
//当前方法在目标方法执行之前执行
/*
method:目标方法
args:目标方法的参数列表
target:目标对象
*/
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
//对于目标方法的增强代码就应该写在这里
System.out.println("执行前置通知方法!!!");
}
}
xml配置
<!--注册切面:前置通知-->
<bean id="myAdvice" class="com.chen.service.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>
测试及结果
@Test
public void test01() {
//创建容器对象,加载Spring配置文件
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
ISomeService service = (ISomeService) ac.getBean("serviceProxy");
service.doFirst();
System.out.println("= = =");
service.doSecond();
}
/*
执行前置通知方法!!!
执行doFirst()方法
= = =
执行前置通知方法!!!
执行doSecond()方法
*/
二、后置通知(AfterReturuningAdvice)
后置通知类
public class MyAfterRreturningAdvice implements AfterReturningAdvice {
//在目标方法执行之后执行
//returnValue:目标方法的返回值
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行后置通知方法 returnValue = " + returnValue);
}
}
xml配置
<!--注册切面:后置通知-->
<bean id="myAfterRreturningAdvice" class="com.chen.service.MyAfterRreturningAdvice"/>
<!--生成代理对象-->
<bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--<property name="targetName" value="someService"/>-->
<!--指定目标对象-->
<property name="target" ref="someService"/>
<!--指定切面-->
<!--<property name="interceptorNames" value="myAdvice"/>-->
<property name="interceptorNames" value="myAfterRreturningAdvice"/>
</bean>
测试及结果
//后置通知测试
@Test
public void test02() {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
ISomeService service = (ISomeService) ac.getBean("serviceProxy");
service.doFirst();
System.out.println("= = = =");
String result = service.doSecond();
System.out.println(result);
/*
执行doFirst()方法
执行后置通知方法 returnValue = abcde
= = = =
执行doSecond()方法
执行后置通知方法 returnValue = abcde
abcde
*/
}
三、环绕通知(MethodInterceptor)
环绕通知类
//环绕通知:可以修改目标方法的返回结果
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("执行环绕通知:目标方法执行之前");
//执行目标方法
Object result = methodInvocation.proceed();
System.out.println("执行环绕通知:目标方法执行之前之后");
if (result != null) {
result = ((String)result).toUpperCase();
}
return result;
}
}
xml配置
<!--注册切面:环绕通知-->
<bean id="myMethodInterceptor" class="com.chen.service.MyMethodInterceptor"/>
<!--生成代理对象-->
<bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--<property name="targetName" value="someService"/>-->
<!--指定目标对象-->
<property name="target" ref="someService"/>
<!--指定切面-->
<!--<property name="interceptorNames" value="myAdvice"/>-->
<property name="interceptorNames" value="myMethodInterceptor"/>
</bean>
测试及结果
@Test
public void test03() {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
ISomeService service = (ISomeService) ac.getBean("serviceProxy");
service.doFirst();
System.out.println("= = = = =");
String result = service.doSecond();
System.out.println(result);
}
/*
执行环绕通知:目标方法执行之前
执行doFirst()方法
执行环绕通知:目标方法执行之前之后
= = = = =
执行环绕通知:目标方法执行之前
执行doSecond()方法
执行环绕通知:目标方法执行之前之后
ABCDE
*/
四、异常通知(ThrowsAdvice)
异常类
- UserException.java
/*
* 异常分为两种:
* 1、运行时异常,不进行处理也可以通过编译;
* 若一个类继承自RunTimeException,则该异常就是运行时异常
* 2、编译时异常、受查异常,Checked Exception,不进行处理,将无法通过编译;
* 若一个类继承自Exception,则该异常就是受查异常
* */
public class UserException extends Exception {
public UserException(){
super();
}
public UserException(String message) {
super(message);
}
}
- UsernameException.java
public class UsernameException extends UserException {
public UsernameException() {
super();
}
public UsernameException(String message) {
super(message);
}
}
- PasswordException.java
public class PasswordException extends UserException {
public PasswordException() {
super();
}
public PasswordException(String message){
super(message);
}
}
接口
public interface IUserService {
//目标方法
boolean login(String username, String password) throws UserException;
}
实现类
public class UserServiceImpl implements IUserService {
@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;
}
}
异常通知类
//异常通知
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());
}
//当目标方法抛出Exception异常时,执行当前方法
public void afterThrowing(Exception ex) {
System.out.println("发生异常 ex = " + ex.getMessage());
}
}
xml配置
<!--注册切面:异常通知-->
<bean id="myThrowsAdvice" class="com.chen.service.MyThrowsAdvice"/>
<!--注册目标对象-->
<bean id="userService" class="com.chen.service.UserServiceImpl"/>
<!--生成代理对象-->
<bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--<property name="targetName" value="someService"/>-->
<!--指定目标对象-->
<property name="target" ref="userService"/>
<!--指定切面-->
<!--<property name="interceptorNames" value="myAdvice"/>-->
<property name="interceptorNames" value="myThrowsAdvice"/>
</bean>
测试
@Test
public void test05() throws Exception{
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
IUserService service = (IUserService) ac.getBean("serviceProxy");
service.login("beijing", "111");
}