首先在学习AOP之前,先了解一下动态代理。
动态代理指的是通过一个代理对象来创建需要的业务对象,然后在这个代理对象中统一进行各种需求的处理
1.写一个类实现相应的InvocationHandler接口,以后创建对象就完全通过这个代理类来进行创建
2.创建要代理的对象
代理模式的详解
public class LogProxy implements InvocationHandler {
//2、创建一个代理对象
private Object target;
private LogProxy( ){ }
//3、创建一个方法来生成对象
//创建一个静态的newInstance方法来创建代理对象
public static Object getInstance(Object o) {
//3.1、创建LogProxy对象
LogProxy proxy = new LogProxy();
//3.2、设置这个代理对象
proxy.target = o;
//3.3、通过Proxy的方法创建代理对象
//第一个参数是要代理对象的classLoader
//第二个参数是要代理对象实现的所有接口
//第三个参数是实现类InvocationHandler的对象
//此时的result就是一个代理对象,代理的是o 【result代理o】
Object result = Proxy.newProxyInstance(o.getClass().getClassLoader(),
o.getClass().getInterfaces(),
proxy);
return result;
}
/**
* 当有了代理对象之后,不管这个代理对象执行什么方法,都会调用以下的invoke方法
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// if(method.getName().equals("add")||method.getName().equals("delete")) {
// Logger.info("进行了相应的操作");
// }
Object obj = method.invoke(target, args);
if(method.isAnnotationPresent(LogInfo.class)) {
LogInfo li = method.getAnnotation(LogInfo.class);
Logger.info(li.value());
}
return obj;
}
AOP.jpg
基于XML配置的Spring AOP:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
<bean id="helloWorldImpl1" class="com.xrq.aop.HelloWorldImpl1" />
<bean id="helloWorldImpl2" class="com.xrq.aop.HelloWorldImpl2" />
<bean id="timeHandler" class="com.xrq.aop.TimeHandler" />
<bean id="logHandler" class="com.xrq.aop.LogHandler" />
<aop:config>
<aop:aspect id="time" ref="timeHandler" order="1">
<aop:pointcut id="addTime" expression="execution(* com.xrq.aop.HelloWorld.*(..))" />
<aop:before method="printTime" pointcut-ref="addTime" />
<aop:after method="printTime" pointcut-ref="addTime" />
</aop:aspect>
<aop:aspect id="log" ref="logHandler" order="2">
<aop:pointcut id="printLog" expression="execution(* com.xrq.aop.HelloWorld.*(..))" />
<aop:before method="LogBefore" pointcut-ref="printLog" />
<aop:after method="LogAfter" pointcut-ref="printLog" />
</aop:aspect>
</aop:config>
</beans>
使用注解配置AOP:
@Service("math")
public class Math{
//加
public int add(int n1,int n2){
int result=n1+n2;
System.out.println(n1+"+"+n2+"="+result);
return result;
}
//减
public int sub(int n1,int n2){
int result=n1-n2;
System.out.println(n1+"-"+n2+"="+result);
return result;
}
}
/**
* @Component表示该类的实例会被Spring IOC容器管理;
* @Aspect表示声明一个切面;
* @Before表示before为前置通知,通过参数execution声明一个切点
**/
@Component
@Aspect
public class Advices {
@Before("execution(* com.zhangguo.Spring052.aop02.Math.*(..))")
public void before(JoinPoint jp){
System.out.println("----------前置通知----------");
System.out.println(jp.getSignature().getName());
}
@After("execution(* com.zhangguo.Spring052.aop02.Math.*(..))")
public void after(JoinPoint jp){
System.out.println("----------最终通知----------");
}
} // 在xml中配置为<aop:aspectj-autoproxy proxy-target-class="true">
AspectJ切点函数:
@AspectJ使用AspectJ专门的切点表达式描述切面。
AspectJ表达式可分为四类:
- 方法切点函数:通过描述目标类方法信息定义连接点。
- 方法参数切点函数:通过描述目标类方法入参信息定义连接点。
- 目标类切点函数:通过描述目标类类型信息定义连接点。
- 代理类切点函数:通过描述代理类信息定义连接点。
常见的AspectJ表达式函数:
execution():满足匹配模式字符串的所有目标类方法的连接点
@annotation():任何标注了指定注解的目标方法链接点
args():目标类方法运行时参数的类型指定连接点
@args():目标类方法参数中是否有指定特定注解的连接点
within():匹配指定的包的所有连接点
target():匹配指定目标类的所有方法
@within():匹配目标对象拥有指定注解的类的所有方法
@target():匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解
this():匹配当前AOP代理对象类型的所有执行方法
最常用的是:execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)切点函数,可以满足多数需求。
AspectJ通知注解:
/**
* 通知类,横切逻辑
*/
@Component
@Aspect
public class Advices {
//切点
@Pointcut("execution(* com.zhangguo.Spring052.aop04.Math.a*(..))")
public void pointcut(){
}
//前置通知
@Before("pointcut()")
public void before(JoinPoint jp){
System.out.println(jp.getSignature().getName());
System.out.println("----------前置通知----------");
}
//最终通知
@After("pointcut()")
public void after(JoinPoint jp){
System.out.println("----------最终通知----------");
}
//环绕通知
@Around("execution(* com.zhangguo.Spring052.aop04.Math.s*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println(pjp.getSignature().getName());
System.out.println("----------环绕前置----------");
Object result=pjp.proceed();
System.out.println("----------环绕后置----------");
return result;
}
//返回结果通知
@AfterReturning(pointcut="execution(* com.zhangguo.Spring052.aop04.Math.m*(..))",returning="result")
public void afterReturning(JoinPoint jp,Object result){
System.out.println(jp.getSignature().getName());
System.out.println("结果是:"+result);
System.out.println("----------返回结果----------");
}
//异常后通知
@AfterThrowing(pointcut="execution(* com.zhangguo.Spring052.aop04.Math.d*(..))",throwing="exp")
public void afterThrowing(JoinPoint jp,Exception exp){
System.out.println(jp.getSignature().getName());
System.out.println("异常消息:"+exp.getMessage());
System.out.println("----------异常通知----------");
}
}
结果
add
----------前置通知----------
100+0=100
----------最终通知----------
sub
----------环绕前置----------
100-0=100
----------环绕后置----------
100*0=0
mut
结果是:0
----------返回结果----------
div
异常消息:/by zero
----------异常通知----------
网友评论