AOP是面向对象编程的一种补充,面向对象编程是从静态角度考虑程序的结构,而面向切面编程是从动态角度考虑程序的运行过程。
3.2.2 AOP编程术语
(1)切面(Aspect)
泛指交叉级业务逻辑。常用的切面有通知advice与顾问advisor,实际是对主业务逻辑的一种增强。
(2)织入(Weaving)
织入就是将切面代码插入到目标对象的过程中。
(3)连接点(JoinPoint)
可以被切面织入的方法。通常业务接口的方法均可被织入。
(4)切入点(Pointcut)
被织入的连接点叫做切入点。被final修饰的方法不能看作连接点,因为其不能被修改,不能被增强。
(5)目标对象(target)
要被增强的对象,即包含主业务逻辑的类的对象。
以下都是切面的一种:
(6)通知(Advice)
是切面的一种形式,可以完成简单的织入功能。通知定义了增强代码切入到目标代码的时间点,阿是目标方法执行之前执行还是执行之后执行。通知类型不同,切入时间不同。
切入点定义切入的位置,通知定义切入的时间。
(7)顾问(Advisor)
是将通知包装为更复杂切面的装配器。可以指定切入点。
3.3 通知Advice
前置通知
package com.bjpowernode.aop01;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
// 前置通知
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
// 当前方法在目标方法执行之前执行
// method:目标方法
// args:目标方法的参数列表
// target:目标对象
@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
// 对于目标方法的增强代码就应该写在这里
System.out.println("执行前置通知方法");
}
}
<?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 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>
</beans>
执行前置通知方法
执行doFirst()方法
====================
执行前置通知方法
执行doSecond()方法
后置通知
package com.bjpowernode.aop02;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
//后置通知:可以获取到目标方法的返回结果,但无法改变目标方法的结果
public class MyAfterReturningAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行后置通知方法 returnValue= "+returnValue);
if(returnValue!=null){
returnValue = ((String)returnValue).toUpperCase();
System.out.println("修改过的值:" + returnValue);
}
}
}
<?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 id="myService" class="com.bjpowernode.aop02.SomeServiceImpl"/>
<!--注册切片:advice -->
<bean id="myAdvice" class="com.bjpowernode.aop02.MyAfterReturningAdvice"/>
<!--生成proxy-->
<bean id="myProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="myService"/>
<property name="interceptorNames" value="myAdvice"/>
</bean>
</beans>
执行doFirst()方法
执行后置通知方法 returnValue= null
====================
执行doSecond()方法
执行后置通知方法 returnValue= abcde
修改过的值:ABCDE
环绕通知
package com.bjpowernode.aop03;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
//环绕通知:可以返回目标方法的返回结果
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) {
result = ((String) result).toUpperCase();
}
return result;
}
}
<?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 id="myService" class="com.bjpowernode.aop03.SomeServiceImpl"/>
<!--注册切片:advice -->
<bean id="myAdvice" class="com.bjpowernode.aop03.MyMethodInterceptor"/>
<!--生成proxy-->
<bean id="myProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="myService"/>
<property name="interceptorNames" value="myAdvice"/>
</bean>
</beans>
执行环绕通知:目标方法执行之前
执行doFirst()方法
执行环绕通知:目标方法执行之后
====================
执行环绕通知:目标方法执行之前
执行doSecond()方法
执行环绕通知:目标方法执行之后
ABCDE
异常通知
(1)
package com.bjpowernode.aop04;
import org.springframework.aop.ThrowsAdvice;
//异常通知
public class MyThrowAdvice implements ThrowsAdvice {
//当目标方法抛出与指定类型的异常具有is-a关系的异常时,执行当前方法
public void afterThrowing(Exception ex){
System.out.println("执行异常通知方法");
}
}
public void doFirst() {
System.out.println("执行doFirst()方法"+3/0);
}
(2)
异常类:
package com.bjpowernode.aop05;
//异常分两种:
//1)运行时异常,不进行处理,也可以通过编译。
// 若一个类继承自RunTimeException,则该异常是运行时异常
//2)编译时异常,受查异常 checked Exception,不进行处理将无法通过编译
// 若一个类继承自Exception,则该异常就是受查异常
public class UserException extends Exception{
public UserException() {
super();
// TODO Auto-generated constructor stub
}
public UserException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
}
package com.bjpowernode.aop05;
public class UsernameException extends UserException {
public UsernameException() {
super();
// TODO Auto-generated constructor stub
}
public UsernameException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
}
package com.bjpowernode.aop05;
public class PasswordException extends UserException {
public PasswordException() {
super();
// TODO Auto-generated constructor stub
}
public PasswordException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
}
定义接口 抛出异常:
package com.bjpowernode.aop05;
public interface ISomeService {
//目标方法
boolean login(String username,String password) throws UserException;
}
实现接口:(实现类抛出相应的异常)
package com.bjpowernode.aop05;
public class SomeServiceImpl implements ISomeService{
@Override
public boolean login(String username, String password) throws UserException {
if(!"root".equals(username)){
throw new UsernameException("用户名输错啦");
}
if(!"123456".equals(password)){
throw new PasswordException("密码输错啦");
}
//double a = 3/0;
return true;
}
}
异常通知切片:(实现类抛出异常时会自动触发切片类afterThrowing方法)
package com.bjpowernode.aop05;
import org.springframework.aop.ThrowsAdvice;
//异常通知
public class MyThrowAdvice implements ThrowsAdvice {
//当目标方法抛出与指定类型的异常具有is-a关系的异常时,执行当前方法
public void afterThrowing(UsernameException ex){
System.out.println("发生用户名异常 ex = "+ex.getMessage());
}
public void afterThrowing(PasswordException ex){
System.out.println("发生密码异常 ex = "+ex.getMessage());
}
public void afterThrowing(Exception ex){
System.out.println("发生其他异常 ex = "+ex.getMessage());
}
}
MyTest:
package com.bjpowernode.aop05;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test01() throws UserException{
String resource = "com/bjpowernode/aop05/applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(resource);
ISomeService service = (ISomeService) applicationContext.getBean("myProxy");
service.login("root", "123456");
}
}