学习内容
Spring AOP 切面的定义
Spring AOP定义切面有多种方式,例如
1、使用@AspectJ注解
2、使用<aop:aspect>标签
3、使用<aop:advisor>标签
4、使用Advisor类
一、使用@AspectJ注解
a、在配置文件中加<aop:aspectj-autoproxy>
b、编写切面类
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import java.util.Arrays;
@Component
@Aspect
public class LogAspect {
private final String POINT_CUT = "execution(public * com.congrong.service.*.*(..))";
@Pointcut(POINT_CUT)
public void pointCut(){}
/**
* 切点方法之前执行
* @param joinPoint
*/
@Before(value = "pointCut()")
public void before(JoinPoint joinPoint){ System.out.println("@Before通知执行"); }
/**
* 切点方法之后执行
* @param joinPoint
*/
@After(value = POINT_CUT)
public void doAfterAdvice(JoinPoint joinPoint){
System.out.println("After通知执行");
}
/**
* 切点方法返回后执行
* 如果第一个参数为JoinPoint,则第二个参数为返回值的信息
* 如果第一个参数不为JoinPoint,则第一个参数为returning中对应的参数
* returning:限定了只有目标方法返回值与通知方法参数类型匹配时才能执行后置返回通知,否则不执行,
* 参数为Object类型将匹配任何目标返回值
* @param joinPoint
* @param result
*/
@AfterReturning(value = POINT_CUT,returning = "result")
public void doAfterReturningAdvice(JoinPoint joinPoint,Object result){
System.out.println("AfterReturning通知执行");
}
/**
* 切点方法抛异常执行
* 定义一个名字,该名字用于匹配通知实现方法的一个参数名,当目标方法抛出异常返回后,将把目标方法抛出的异常传给通知方法;
* throwing:限定了只有目标方法抛出的异常与通知方法相应参数异常类型时才能执行后置异常通知,否则不执行,
* 对于throwing对应的通知方法参数为Throwable类型将匹配任何异常。
* @param joinPoint
* @param exception
*/
@AfterThrowing(value = POINT_CUT,throwing = "exception")
public void doAfterThrowingAdvice(JoinPoint joinPoint,Throwable exception){
System.out.println("AfterThrowing通知执行");
if(exception instanceof NullPointerException){
System.out.println("NullPointerException!");
}
}
/**
* 环绕通知:
* Spring AOP的环绕通知会影响到AfterThrowing通知的运行,不要同时使用
* 环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。
* 环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型
* @param proceedingJoinPoint
* @return
*/
@Around(value = POINT_CUT)
public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
System.out.println("@Around环绕通知开始执行");
Object obj = null;
try {
obj = proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("@Around环绕通知结束执行");
return obj;
}
}
二、使用<aop:aspect>标签
<!-- 定义目标对象 -->
<bean name="userDao" class="com.congrong.dao.UserDaoImpl" />
<!-- 定义切面 -->
<bean name="logAspect" class="com.congrong.LogAspect" />
<!-- 配置AOP -->
<aop:config>
<!-- 定义切点函数 -->
<aop:pointcut id="pointcut" expression="execution(* com.congrong.dao.*.*(..))" />
<!-- 配置切面-->
<aop:aspect ref="logAspect">
<!-- 前置通知method 指定通知方法名,必须与logAspect中的相同pointcut 指定切点函数-->
<aop:before method="before" pointcut-ref="pointcut" />
<!-- 返回通知 returning="returnVal" 定义返回值 必须与类中声明的名称一样-->
<aop:after-returning method="afterReturn"pointcut-ref="pointcut" returning="returnVal" />
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pointcut" />
<!--异常通知 throwing="throwable" 指定异常通知错误信息变量,必须与类中声明的名称一样-->
<aop:after-throwing method="afterThrowing"pointcut-ref="pointcut" throwing="throwable"/>
<!--后置/最终通知 -->
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
三、使用<aop:advisor>标签
与< aop:aspect>定义切面时不同的是,< aop:aspect>的通知只需要定义一般的bean就行,而
< aop:advisor>中引用的通知,必须实现Advice接口。
定义通知
//定义通知
public class LogAdvice implements MethodBeforeAdvice,AfterReturningAdvice{
@Override
public void before(Method arg0, Object[] arg1, Object arg2)throws Throwable {
System.out.println("开始打印日志!");
}
@Override
public void afterReturning(Object arg0, Method arg1, Object[] arg2,Object arg3) throws Throwable {
System.out.println("结束打印日志!");
}
}
配置
<bean id="logAdvice" class="com.congrong.LogAdvice"></bean>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.congrong.*.*(..))"/>
<aop:advisor advice-ref="logAdvice" pointcut-ref="pointcut"/>
</aop:config>
四、使用Advisor类
//定义一个接口
public interface UserDao {
void addUser();
}
//目标对象实现接口并重写方法
public class UserDaoImpl implements UserDao {
@Override
public void addUser() {
System.out.println("新增用户");
}
}
//实现增强类的接口
public class LogBefore implements MethodBeforeAdvice {
//arg0 目标类的方法
arg1 目标类的入参数
arg2 目标类
@Override
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
System.out.println("添加日志");
}
}
ProxtyFactory以编程形式
public class Test {
public static void main(String[] args) throws Exception{
LogBefore log = new LogBefore();
UserDao userDao = new UserDaoImpl();
ProxyFactory proxyf = new ProxyFactory();
//设置目标对象
proxyf.setTarget(userDao);
//设置增强类对象
proxyf.addAdvice(log);
//获得代理对象
UserDao up = proxyf.getProxy();
up.addUser();
}
}
ProxyFactoryBean通过xml配置代理。
<bean id="logBefore" class="com.cong.LogBefore"/>
<bean id="target" class="com.cong.UserDaoImpl"/>
<bean id="userDao" class="org.springframework.aop.framework.ProxyFactoryBean"
p:proxyInterfaces="com.cong.UserDao"
p:interceptorNames="logBefore"
p:target-ref="target"/>