spring之AOP(转)
Spring之AOP篇:
AOP框架是Spring的一个重要组成部分.但是Spring IOC 并不依赖于AOP,这就意味着你有权力选择是否使用AOP,AOP作为Spring IOC容器的一个补充,使它成为一个强大的中间件解决方案。
一、AOP(Aspect-Oriented Programming)
AOP的目标:横切关注点 这种问题用面向对象很难解决
AOP是用来解决什么问题的呢?
横越多个模块的业务关注点,比如记录日志,事务管理
1、AOP的解决方案——拦截
1)由谁来拦截?--代理类Proxy 和本类实现同一个接口
就像是这样:action发出命令--代理类(处理一些操作)--本类
2)拦截的过程:
I.将Proxy(如UserServiceProxy)交给调用者(如UserAction),调用者调用需要的方法
II.Proxy先拦截该方法,执行拦截逻辑
III.然后执行调用者真正的实现类(如UserServiceImp)
3)核心概念:
a、代理对象Proxy:是核心,负责拦截,由工具自动创建
b、拦截器(Interceptor):实现拦截逻辑的对象
c、目标对象(Target):是Proxy所代理的对象,是已有的类
以上三个类实现了一个“金三角”
Proxy--拦截-->Interceptor--实现拦截逻辑--处理目标对象-->Target
2、代理如何实现?
有两种方式:
1)JDK动态代理(使用这个比较多)
由JDK自带的动态代码生成技术,可以对实现了接口的类进行处理
2)CGLib
对没有实现任何接口的类进行处理
这两种方式的共同点:(都是在后台自动生成的)
在程序运行期间,动态的生成代码并进行动态编译和加载
问题:Spring的AOP是如何实现代理的?是自己实现的吗?
注:Spring借用了JDK动态代理和CGLib来实现代理对象。
Spring进行如下判断来实现代理:
i、如果目标类实现了接口,Spring则调用JDK动态代理;
ii、反之,调用CGLib
Proxy是核心:
创建Proxy的方法:ProxyFactoryBean
<bean id="arithProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="arith"/>
<property name="interceptorNames">
<list>
<!--<value>logBefore</value>
<value>logAfter</value>
<value>logThrows</value> -->
<value>logAround</value>
</list>
</property>
JDK动态代理的实现方式:(工厂模式)
UserDao userDao = new UserDaoImp();
UserDao proxy = (UserDao)
Proxy.newProxyInstance(userDao.getClass().getClassLoader(),
userDao.getClass().getInterfaces(),
new LogHandler(userDao));
二、Spring中的AOP方式(在Spring中将拦截称为通知)
拦截逻辑放在Advice之中,Advice按时间将拦截器分为四类:
1)Before Advice:方法执行之前进行拦截
public class LogBeforeAdvice implements MethodBeforeAdvice{
//Method method拦截的方法
//Object[] args 方法中的参数
//target 目标对象
@Override
public void before
(Method method, Object[] args, Object target)
throws Throwable {
MyLogger logger = new MyLogger();
System.out.println("Methods:"+method.getName()+
" begins, args:"+Arrays.toString(args));
}
}
2)After Advice:方法执行之后进行拦截
public class LogAfterAdvice implements AfterReturningAdvice{
@Override
public void afterReturning(Object resultValue, Method method, Object[] args,
Object target) throws Throwable {
MyLogger logger = new MyLogger();
System.out.println("Methods:"+method.getName()+
" ends, result:"+resultValue);
}
}
3)AfterThrowing Advice:方法异常结束之后进行拦截
public class LogThrowingAdvice implements ThrowsAdvice {
//ThrowsAdvice 没有方法,但方法名必须是afterThrowing
public void afterThrowing(IllegalArgumentException e)
throws Throwable{
MyLogger logger = new MyLogger();
logger.log("IllegalArgumentException!");
}
}
4)Around Advice:环绕通知
public class LogAroundAdvice implements MethodInterceptor {
//MethodInvocation相当于Method的包装
@Override
public Object invoke(MethodInvocation methodInvocation)
throws Throwable {
MyLogger logger = new MyLogger();
try {
//methodInvocation.getMethod()获得方法
//methodInvocation.getArguments()获得方法的参数
logger.log("before:"+
methodInvocation.getMethod().getName()+
", args:"+methodInvocation.getArguments());
//在环绕通知里面,必须执行目标方法!!!!!!!!!!!!!!
Object result = methodInvocation.proceed();
logger.log("ends:"+
methodInvocation.getMethod().getName());
return result;
} catch (Exception e) {
logger.log("Exception:"+e.getMessage());
throw e;
}
}
注意:虽然环绕通知包含了另外三种,但还是要依据业务逻辑来选择,这样有利于代码的编程量
并且环绕通知最好不要和另外三种混用,并且并许执行目标方法proceed().
也可以大致分成两组——
i、BeforeAdvice, AfterReturning, Throwing
ii、AroundAdvice:需要在内部负责调用目标类的方法。
注意:AroundAdvice应单独使用
<!-- Target Object -->
<bean id="arith" class="myspring.calculator.ArithmeticCalculatorImp"></bean>
<!-- Advice -->
<bean id="logBefore" class="myspring.aop.LogBeforeAdvice"/>
<bean id="logAfter" class="myspring.aop.LogAfterAdvice"/>
<bean id="logThrows" class="myspring.aop.LogThrowingAdvice"/>
<bean id="logAround" class="myspring.aop.LogAroundAdvice"/>
<!-- Proxy -->
<bean id="arithProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="arith"/>
<property name="interceptorNames">
<list>
<!-- <value>logBefore</value>
<value>logAfter</value>
<value>logThrows</value> -->
<value>logAround</value>
</list>
</property>
</bean>
在test类中这样用:ApplicationContext ac =
new ClassPathXmlApplicationContext("aop-base.xml");
IArithmeticCalculator proxy =
(IArithmeticCalculator)ac.getBean("arithProxy");
面试中可能会问到:
proxyTargetClass属性
将其加入到代理bean中去,则Spring将调用CGLib来实现创建Proxy对象。
(无论Target有无实现接口)
形如
<bean id="" class="....ProxyFactoryBean">
.....
<property name="proxyTargetClass" value="true" />
</bean>
三、Spring AOP基本方式 AspectJ Spring2.5之后用的
Spring对AspectJ的支持
应用于有多个target,不想一个一个的配置时,用AspectJ
1)POJO类(Aspect类),使用@Aspect
基本信息:Advice和Pointcut
2)定义Bean:在Bean定义文件中定义这个Aspect类
3)启用Spring的自动代理
<aop:aspectj-autoproxy />
1、JoinPoint(连接点):被拦截的方法
程序执行过程中的点,就好像方法中的一个调用,
或者一个特别的被抛出的异常
在Spring AOP中,一个连接点通常是方法调用.
Spring并不明显地使用"连接点"作为术语,
连接点信息可以通过MathodInvocation的参数穿过拦截器访问,
相当于实现org.springframework.aop.Pointcut接口.
2、JoinPoint对象
获取连接点信息(获取目标方法以及目标对象的信息)
1)目标方法:
i、getSignature():方法签名
joinPoint.getSignature().getName()
ii、getArgs():方法的实参(方法的实际参数)
joinPoint.getArgs()
打印输出时:Arrays.toString(joinPoint.getArgs())
2)目标对象: getTarget()
目标对象实际类型:getTarget().getClass()
3、PointCut(切入点):一组连接点。
作用:定义了一组被拦截的方法
PointCut(切入点):当一个通知被激活的时候,
会指定一些连接点.一个AOP框架必须允许开发者指定切入点。
一个AOP框架必须允许开发者指定切入点:例如使用正则表达式.
只有引入了切入点,才能很好的进行拦截
PointCut的意义:可以一次性定义多个类的
多个方法作为拦截目标(即JoinPoint)
PointCut代表了Target 一组Target组成了PointCut
一组Target可以理解为一个类的多个方法,或者多个类的多个方法
代理、拦截器、目标
PointCut
在AspectJ中,切入点是用PointCut Expression来
定义的。(切入点表达式可以理解为过滤条件)
PointCut表达式
格式 execution(表达式)
表达式 一个表达式就是一个匹配规则
* myspring.calculator.IArithmeticCalculator.add(..)
*理解为public void myspring.calculator.IArithmeticCalculator.add(int a,int b)
* 取代public void 通配一切字符
Spring自动代理(即Spring自动生成Proxy对象)
用<aop:aspectj-autoproxy />来启用
Pointcut表达式语法
1)方法级别 —— execution
execution(* *.*(..)) :所有类的方法
execution(public * somepackage.SomeClass.someMethod(..))
总结:execution是对方法签名进行匹配
2)对象级别(类级别)—— within
within(somepackage.*):somepackage包下所有的类
within(com.dang.service.*Service)
即 com.dang.service包下所有以Service结尾的类
within(somepackage..*):somepackage以及所有子包中的类
within(somepackage.SomeInterface+):SomeInterface接口的所有实现类
总结:within是对类名(带包名)进行匹配。 拦截这个类的所有方法
4、Aspect类的组成 一般都用注解 重点
1)Advice(AspectJ中的通知类型)
i、Before
//@Before("LogPointcuts.logAdd()") 方法1
@Before("execution(* *.*(..))") 方法2
public void logBefore(JoinPoint joinPoint){
logger.log("{before} the method: "
+joinPoint.getSignature().getName()+
" args:"+Arrays.toString(joinPoint.getArgs()));
}
注:方法1和方法2都是可以的,方法1的话需要在定义一个方法.
在同一个类或不同的类都是可以的 直接调用这个方法
@Pointcut("execution(* *.*(..))")
public void logOperation(){}
ii、AfterReturning
@AfterReturning(pointcut="execution(* *.*(..))",
returning="result")
public void afterReturning(JoinPoint joinPoint,Object result){
logger.log("{AfterReturning} the method: "
+joinPoint.getSignature().getName()
+" result:"+result);
}
注意:方法有两个参数,在注解中的returning的值必须和Object的值完全一样
iii、AfterThrowing
@AfterThrowing(pointcut="execution(* *.*(..))",
throwing="e")
public void afterThrowing(JoinPoint joinPoint,IllegalArgumentException e){
logger.log("{AfterThrowing} the method: "
+joinPoint.getSignature().getName()
+" e:"+e.getMessage());
}
iv、After:相当于finally,它在AfterReturning
和AfterThrowing之后执行。
它的作用——一般为释放资源(如关闭Connection)
@After("execution(* *.*(..))")
public void after(JoinPoint joinPoint){
logger.log("{After} method:"+joinPoint.getSignature().getName());
}
注意:After是肯定是要在最后执行的,
v、Around
@Around("execution(* *.*(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable{
logger.log("{Arount}Before method:"
+joinPoint.getSignature().getName());
try{
//一定不要忘记调用目标方法
Object result = joinPoint.proceed();
logger.log("{Arount}After method:"
+joinPoint.getSignature().getName()
+" result:"+result);
//返回目标方法的返回值
return result;
}
catch(IllegalArgumentException e){
logger.log("{Around}Exception:Illegal Argument"
+Arrays.toString(joinPoint.getArgs()));
throw e;
}
注意:Around最好不要和其他四种同时使用
把处理日志的代码放到一起就是切面,模块化(Aspect)
5.Introduction(引入)
1)Introduction的定义
给正在运行的程序中的对象添加方法。
其实是一种动态技术
2)问题:如何给ArithmeticCalculatorImp类加入
求最大值和最小值的方法
假设:最大值和最小值的方法已经存在,且分别属于不同的类。
extends MaxCalculatorImp, MinCalculatorImp 不行,java不允许多继承
3)要点:
a、实现一个POJO类
定义@DeclareParents来"继承"的父类
@DeclareParents(
value="myspring.calculator.ArithmeticCalculatorImp",
defaultImpl=MaxCalculatorImp.class
)
private MaxCalculator maxCalculator;
@DeclareParents(
value="myspring.calculator.ArithmeticCalculatorImp",
defaultImpl=MinCalculatorImp.class
)
private MinCalculator minCalculator;
b、定义这个Bean:在Bean定义文件中声明一下。
<bean class="myspring.calculator.CalculatorIntroduction"></bean>
在test类中测试时:ApplicationContext ac =
new ClassPathXmlApplicationContext("aop.xml");
IArithmeticCalculator cal = (IArithmeticCalculator)
ac.getBean("arith");
cal.add(2, 3);
cal.div(4, 3);
MaxCalculator max = (MaxCalculator)cal;
max.max(3, 6);
MinCalculator min = (MinCalculator)cal;
min.min(3, 6);
spring之AOP
spring之AOP(转)相关推荐
- Spring.Net Aop
Spring.Net 有四种通知: IMethodBeforeAdvice,IAfterReturningAdvice,IMethodInterceptor,IThrowsAdvice BeforeA ...
- [Spring 深度解析]第4章 Spring之AOP
第4章 ◄Spring之AOP► 在上一章节中,我们大致了解了Spring核心容器,了解了IOC思想在Spring中的具体应用Bean容器以及Bean的配置与使用,这一章我们将开始学习AOP在Spri ...
- Spring对AOP的支持
Spring对AOP的支持<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" ...
- Spring 容器AOP的实现原理——动态代理
本文来自极客学院 Spring 容器AOP的实现原理--动态代理 之前写了一篇关于IOC的博客--<Spring容器IOC解析及简单实现>,今天再来聊聊AOP.大家都知道Spring的两大 ...
- Spring的AOP使用xml配置
需要使用spring的包,大家自己全部导入进去即可.省4........ 用户管理接口 package com.rx.spring; public interface UserManager { pu ...
- 【SSM框架系列】Spring 的 AOP(面向切面编程)
什么是 AOP AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. AOP 是 OOP ...
- Spring实现AOP的4种方式
Spring实现AOP的4种方式 先了解AOP的相关术语: 1.通知(Advice): 通知定义了切面是什么以及何时使用.描述了切面要完成的工作和何时需要执行这个工作. 2.连接点(Joinpoint ...
- Spring的AOP原理
AOP是什么? 软件工程有一个基本原则叫做"关注点分离"(Concern Separation),通俗的理解就是不同的问题交给不同的部分去解决,每部分专注于解决自己的问题.这年头互 ...
- Spring中AOP相关的API及源码解析,原来AOP是这样子的
前言 之所以写这么一篇文章主要是因为下篇文章将结束Spring启动整个流程的分析,从解析配置到创建对象再到属性注入最后再将创建好的对象初始化成为一个真正意义上的Bean.因为下篇文章会涉及到AOP,所 ...
- spring教程--AOP详解
1 Spring中的AOP 1.1 Spring的传统AOP : AOP:不是由Spring定义.AOP联盟的组织定义. Spring中的通知:(增强代码) 前置通知 org.springframew ...
最新文章
- BZOJ 1503 treap
- Android CookieSyncManager同步cookie
- Django Rest Framework 视图和路由
- linux 挂载exfat u盘 yum,centos挂载exfat u盘
- c语言的class,Objective-C中Class的本质
- linux查看网卡的驱动命令行,linux查看网卡驱动模块信息
- RED HAT 7 性能监控工具
- linux awk拼接字符串,shell awk命令字符串拼接
- 学习笔记︱深度学习以及R中并行算法的应用(GPU)
- cocos2dx 在win32打开黑色命令行
- Altium Designer放置数码管字体
- 基于asp.net的在线问卷调查系统
- STL(标准模板库)
- 目标追踪:FCNT、GOTURN、SiamFC、SiamRPN、SiamRPN++
- #bfs#[luogu 2873] [USACO07DEC]泥水坑Mud Puddles
- “生死看淡”的雷军要造车,这对中国的汽车产业意味着什么?
- ffmpeg AV转换常用命令
- CORBA协议相关的概念
- html 表格 选择,html表格选择与colspan或rowspan
- 可怕的大学,到底有多少人在校园里浪费青春