1.什么是AOP(面向切面编程)
不扯那些概念的东西,简单说来AOP是OOP的一个补充,AOP可以在程序运行期追加一些公用的功能,比如权限判断,日志记录,这些功能都是项目需要的,但是又不能每个地方都调用,这样无疑增加了代码的复杂度和工作量,我们可以将这些分散在系统中的公用代码集中于一个地方并通过aop技术应用于系统各个地方。
2. AOP术语
- 切点:我们需要插入这些公用功能的点?比如哪些类?哪些方法?等等(何处调用)
- 切面:这些公用功能代码(调用什么)
- 通知:在什么时候调用?
3.通知分类
- 前置通知(Before):在目标方法调用前调用
- 后置通知(After):在目标方法调用后调用
- 返回通知(AfterReturning):在目标方法成功执行后调用
- 异常通知(AfterThrowing):在目标方法执行失败抛异常后调用
- 环绕通知(Around):将目标方法包裹,由你决定什么时候调用(最强大,最常用)
3.Spring AOP
Spring 通过生成代理类将目标对象包裹从而实现AOP,调用者调用目标方法时,其实是在调用代理类,再由代理类
调用目标方法,从而控制对目标方法的调用,Spring只支持最细方法级别的连接点,如果需要实现更细粒度的控制
则需要借助专业的AOP框架AspectJ,可以控制字段,构造方法等更细的东西,不过需要学习新的语法。
4.Spring 切点表达式
可以通过这些表达式配置切点
上面是Spring支持的AspectJ 切点表达式。除execution是增加切点范围的外,其他都是用于缩小范围
5.编写切点
6.编写切面
用注解@Aspect表示一个切面,注解用于类上
@Aspect //表示是切面
@Component //Bean注入Spring
@Order(1)//优先执行该切面
public class TicketLoginAspect {
@Autowired
private HttpServletRequest request;
/**
* 切点
*/
//表示作用于TicketLogin注解
@Pointcut("@annotation(com.cnct.webchat.common.annotation.TicketLogin)")
public void loginPointCut() {
//扩展作用范围,不用在每个@Around后写很长的表达式了,只需写方法名即可,像下面
}
@Around("loginPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
}
}
写完切面后要开启Spring aop自动代理才能生效,如果使用JavaConfig的话,在配置类上使用
@EnableAspectJ-AutoProxy即可,如果用XML配置则加入
7.通过注解为目标类增加新功能(方法)
既然AOP通过代理实现,那么就可以为目标对象增加新的代理方法。
@Aspect //定义切面
@Component
public class TestAop{
@DeclareParents(value = "com.lyq3.NewFunctionInterface+",
defaultImpl = NewFunctionImpl.class
)
public static Abc abc;
}
- value 指定那种类型的Bean要引入,(后面的“+”号表示所有的子类型而不是本身)
- defaultImpl 指定引入功能的实现类
- @DeclareParents 所标注的静态属性指明要引入的接口,也就是要为Abc接口增加新功能
8.注意:踩坑记
@Aspect
@Component
@Order(2)//登录注解执行后执行该注解
public class HandleRecordAspect {
@Autowired
private HandleRecordMapper handleRecordMapper;
@Autowired
private HttpSession session;
/**
* 切点
*/
@Pointcut("@annotation(com.cnct.webchat.common.annotation.HandleRecord)")
public void handlePointCut() {
}
@Around("handlePointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
//先执行方法
Object result = point.proceed();
//访问目标方法的参数:
Object[] args = point.getArgs();
Signature sig = point.getSignature();
MethodSignature msig = null;
msig = (MethodSignature) sig;
Method method = msig.getMethod();
// String methodName = msig.getName();//方法名
HandleRecord handleRecord = method.getAnnotation(HandleRecord.class);//获取注解
//操作标识(增?删?改?)
int handle_status = handleRecord.handle_status();
String remark = handleRecord.value();
}
}
上面的代码是我在项目中实现一个存操作纪录的部分代码,咋一看没毛病。@HandleRecord这个自定义注解在Controller层能用,我放到Service层的时候(service有接口和接口实现,注解放实现类方法上)
HandleRecord handleRecord = method.getAnnotation(HandleRecord.class);
这句代码是获取不到注解的,也就是说 handleRecord = null;
原因是AOP采用的jdk代理,MethodSignature 转型之后,会丢失子类的方法注解,所以得先获取实现类的方法
修改下代码,就能用了:
//先执行方法
Object result = point.proceed();
//访问目标方法的参数:
Object[] args = point.getArgs();
Signature sig = point.getSignature();
MethodSignature msig = null;
msig = (MethodSignature) sig;
Method method = msig.getMethod();
//MethodSignature 转型之后,会丢失子类的方法注解,所以得先获取实现类的方法
//============================================
Method soruceMethod = point.getTarget().getClass().getMethod(method.getName(), method.getParameterTypes());
//=============================================
HandleRecord handleRecord = soruceMethod.getAnnotation(HandleRecord.class);//获取注解
//操作标识(增?删?改?)
int handle_status = handleRecord.handle_status();
String remark = handleRecord.value();
详情查看Spring in Action 第四版 第四章
本文作者: 小小牛
本文链接: http://blog.lyq3.com/2017/08/22/Spring_in_Action/Spring-AOP/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议。转载请注明出处!