-
AOP基本概念及特点
一、什么是AOP及实现方式
AOP:Aspect Oriented Programming,面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
主要功能:日志记录,性能统计,安全控制,事物处理,异常处理等等。(事务处理:任何操作数据库的方法,尤其是增删改,需要事物的处理,使用AOP时,执行到某种类型的方法,或者某一层的类,自动开启事物和获取连接、提交事物、关闭事物的步骤,简化了操作)
AOP实现方式:1——预编译方式。(AspectJ)
2——运行期动态代理。(JDK动态代理、CGLib动态代理)
(SpringAOP、JbossAOP)
切面的理解:例如系统中有产品管理、订单管理、服务管理等等模块(子功能),任一模块(子功能),都需要记录它的日志,控制它的事物,以及做一些安全验证的功能。但是如果在每一个模块(子功能)中去设计日志、事物、安全验证,会带来大量工作量,尤其当项目达到一定规模时,比如修改日志记录的方式,那么则需要修改每一个模块的日志功能,通过切面方式对开发人员是不可见的,当执行任一模块时,通过预编译或者运行期动态代理方式,都会去记录它的日志,实现了一处写功能,处处可实现的方式,对于开发人员允许不知道有这样功能的存在,这样就简化了操作(修改日志、编写事物等),业务功能横向走,切面垂直业务功能。
二、AOP基本概念(切面—>织入—>目标对象—>切入点—>连接点—>通知—>引入—>AOP代理:“切面”要执行的动作,通过“织入”与所有功能模块进行联系,又通过“目标对象”找到具体功能模块,通过“切入点”将要查找某个功能的某个方法,通过“连接点”找到该功能的特定方法的开始,通过“通知”将要执行何种切面的动作,通过“引入”引入该动作用到的对象和方法,通过“AOP代理”实现该方法。)
AOP相关概念:
1、切面(Aspect):一个关注点(事物),这个关注点可能会横切多个对象(产品管理、订单管理)。
2、连接点(Joinpoint):程序执行过程中的某个特定的点。(一个类中执行的某个方法的开始)。
3、通知(Advice):在切面的某个特定的连接点上执行的动作(方法执行的时候,切面额外执行的动作,比如说方法执行时,开启事物提交功能)。
4、切入点(Pointcut):匹配连接点的断言,在AOP中通知和一个切入点表达式关联(在切面中匹配一个具体的连接点(某个功能的方法的开始))。
5、引入(Introduction):在不修改类代码的前提下,修改的是字节码文件,为类添加新的方法和属性。(类似于编译期动态的修改class文件去增加新的属性和方法,源代码中并没有这样的属性和方法),取决于使用AspectJ和SpringAOP的实现方式,使用方式不同,为类添加属性和方法的方式是有区别的。
6、目标对象(Target Object):被一个或者多个切面所通知的对象。(例如存在一个订单的service(对象)和一个商品的service(对象),执行该模块的功能时,切面会通知相对应的service,在执行数据库操作时,去加上事物的控制,这两个service就是目标对象)。
7、AOP代理(AOP Proxy):AOP框架创建的对象,用来实现切面契约(aspect contract),包括通知方法执行等功能(这里执行的方法指的是切面里的执行相应操作所用到的方法)开发时不知道这个对象存在的,也不清楚会创建成什么样子。
8、织入(Weaving):把切面连接到其他的应用程序类型或者对象上,并创建一个被通知的对象,分为:编译时织入、类加载时织入、执行时织入。(使切面和对象(模块功能)牵连起来)
Spring官方文档给出的解释:
Advice(通知)的类型(在切面某个特定连接点执行的动作)
前置通知(Before advice):在某连接点(join point)(某个功能方法开始执行前)之前执行的通知,但不能阻止连接点前的执行(除该方法外的其他方法的执行)(除非它抛出一个异常)。
返回后通知(After returning advice):在某连接点(方法)正常完成后执行的通知(将要执行的切面功能)。
抛出异常后通知(After throwing advice):在方法抛出异常退出时执行的通知(将要执行切面的功能)。
后通知(After(finally)advice):当某连接点(方法)退出的时候执行的通知(切面将要执行的功能)(不论是正常返回还是异常退出都会执行的通知)。
环绕通知(Around Advice):包围一个连接点(join point)的通知(在整个方法的内部都有切面要执行的功能存在,不分前后)。
Spring框架中AOP的用途:
用途1:提供了声明式的企业服务(也可以是其他服务,例如互联网服务),特别是EJB(重量级框架)的替代服务的声明。
用途2:允许用户定制自己的方面(切面),以完成OOP(面向对象编程,模拟世界中行为和方式,可以理解为实现一个功能的顺序)与AOP(横切的方式,可以理解为各个功能之间横切的一种功能)的互补使用,可以实现自己横切的功能。
三、Spring中的AOP(Spring中AOP的实现方式以及特点)
特点1:纯java实现,无需特殊的编译过程(普通代码需要加载才能执行),不需要控制类加载器层次。可以更好的进行控制(使用类加载器应当谨慎,容易方法生ClassNotFound异常这种情况)。
特点2:目前只支持方法执行连接点(通知Spring Bean的方法执行),(在执行某方法时,才去执行切面的功能)。
特点3:SpringAOP不是为了提供最完整的AOP实现(尽管它非常强大);而是侧重于提供一种AOP实现和Spring IoC容器之间的整合,用于帮助解决企业应用中的常见问题。
特点4:SpringAOP不会与AspectJ(完整的、全面的AOP解决方案)竞争,也不会提供综合全面的AOP解决方案。
有接口和无接口的SpringAOP实现区别:(一个类是否实现了某个接口,例如一个ProductService接口,一个ProductServiceImpl实现了该接口,在这类上切入的切面就是有接口的AOP)
SpringAOP对于有接口的使用标准的JavaSE动态代理作为AOP代理,这使得任何接口(或者接口集)都可以被代理。
SpringAOP对于没有接口的实现类中使用CGLIB代理(如果一个业务对象并没有实现一个接口)
四、Schema-based 实现AOP(基于配置文件实现AOP)
五、API实现AOP(基于API实现AOP)
六、AspectJ实现AOP
查看全部 -
基于Schema-based配置的AOP实现
Spring所有的切面和通知器都必须放在<aop:config>内(可以配置多个<aop:config>元素),每一个<aop:config>可以包含aspect,pointcout和advisor元素(它们必须按照这个顺序进行声明)。
<aop:config>的配置大量使用了Spring的自动代理机制。
该配置的含义:一个类作为切面来声明,切面Id是myAspect,也就是说程序执行某个功能模块时,通过pointcut和Joinpoint执行该切面的功能。
案例:两个类,一个类作为切面类,另一个作为业务类。(注意:<beans>中添加约束,xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
)
代码:
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<bean id="aspectImooc" class="aspect.AspectImooc"></bean>
<bean id="ServiceImooc" class="aspect.ServiceImooc"></bean>
<aop:config>
<aop:aspect id="aspectImoocAop" ref="aspectImooc"></aop:aspect>
</aop:config>
查看全部 -
配置切入点Pointcut(找到某个功能模块的具体方法)
execution用于匹配某个功能模块的具体方法。
以下SpringAOP和Spring支持的AspectJ都支持。
图一的1:执行所有的public方法时,进行切入(执行相应切面的相应功能)。
图一的2:执行所有的set方法时,进行切入。
图一的3:执行com.xy.service.AccountService类下的所有方法时,进行切入。
图一的4:执行com.xyz.service包下的所有方法时,进行切入。
图一的5:执行com.xyz.service包下以及子包下的所有方法时,进行切入。
以下为SpringAOP自己支持的。
SpringAOP其他的匹配。
图一的1:匹配目标对象。
图一的2:匹配参数的。
图一的3:按注解匹配的。
图一的4:按注解匹配的。
图一的5:按注解匹配的。
图二的1:参数里采用了何种注解。
图二的2:采用了何种bean。
图二的3:bean是以何种形式进行结尾的。(这些可以去Spring官方文档和搜索引擎搜索,会有很全面的解释,这些切入点会有很多类型,在具体使用时,可以查找相应文档)
完整的AOP配置(根据不同业务需求,查找相关文档,进行不同的expression表达式配置)
案例1:表示执行service包下的所有类的任何类型的方法时,进行切入。
案例2:表示执行SystemArchitecture的businessService()方法时,才进行切入。
案例3:执行AspectBiz中的所有方法,都会进行切入。
配置以某个单词结尾的类:例如上面Aspect编写成*。
查看全部 -
Advice应用(advice的配置)
Before advice的两种配置方式
<aop:before pointcut-ref="切入点的Id" method="切面的方法"/>。直接设置切入点(就不用单独设置<aop:point-cut/>标签了)
<aop:before pointcut="execution(* com.xyz.myapp.dao..(..))" method="doAccessCheck"/>
案例:(前置)
步骤1:编写逻辑功能类、切面功能类、并编写各自的方法。
代码:
步骤2:编写XML配置,实例化逻辑功能类的Bean和切面功能类的Bean,配置切面、切入点、通知。
案例:(After return advice返回后通知)
<aop:after-returning>returning属性,限制方法的返回值,例如retVal。(arg-names为参数名称,它是严格匹配的。)
步骤1:编写逻辑功能类、切面功能类、并编写各自的方法(和以上相似,只不过切面类多了一个方法)。
步骤2:编写XML配置,实例化逻辑功能类的Bean和切面功能类的Bean,配置切面、切入点、通知。
案例:(抛出异常后通知After throwing advice,<aop:after-throwing>标签多了一个throwing属性,它是限制可被传递的异常参数名称)
步骤1:编写逻辑功能类、切面功能类、并编写各自的方法(编写发生异常后切面要执行的方法)
步骤2:编写XML配置,实例化逻辑功能类的Bean和切面功能类的Bean,配置切面、切入点、抛出异常通知。
案例:(后置通知After (finally) advice)
步骤1:编写逻辑功能类、切面功能类、并编写各自的方法(编写逻辑方法执行后要执行的切面方法)
步骤2:编写XML配置,实例化逻辑功能类的Bean和切面功能类的Bean,配置切面、切入点、抛出异常通知。
案例:(环绕通知After (finally) advice)
步骤1:编写逻辑功能类、切面功能类、并编写各自的方法(编写逻辑方法执行后要执行的切面方法)
步骤2:编写XML配置,实例化逻辑功能类的Bean和切面功能类的Bean,配置切面、切入点、抛出异常通知。
查看全部 -
Introductions应用(简介的advice,作用:在不改变对象和类的代码的前提下,为对象和类添加新的方法。)
Introduction定义:简介允许一个切面声明一个实现指定接口的通知对象,并且提供一个接口实现类(接口的实现类,来代表实例化好的Bean)来代表这些对象。
Introductions的实现:由<aop:aspect>中的<aop:declare-parents>元素声明,该元素用于声明所有匹配的类型,拥有一个新的parent(接口声明的对象可以指向getBean获得的对象)。
<aop:declare-parents>属性
type-matching="expression表达式(表示匹配什么样的类型)。
implement-interface="接口的路径"(切面类的接口)。
default-impl=“切面类的路径”。
案例:
步骤1:编写逻辑功能接口(限制逻辑功能),并提供它的实现类。
public interface ServiceInterfaceFunction {
public void print();
}
public class ServiceFunction implements ServiceInterfaceFunction {
@Override
public void print() {
System.out.println("逻辑功能接口实现类的方法执行了");
}
}
public class ServiceGongNeng {
}
步骤2:配置<aspect:config>(有接口的SpringAOP实现,使用JavaSE动态代理,也就是JDK代理,JDK动态代理只能对实现了接口的类生成代理,而不能针对类,所以要配置<aop:declare-parents>)。
步骤3:测试
@Test
public void testaop(){
ApplicationContext ac=new ClassPathXmlApplicationContext("spring-aop-schema-advice.xml");
ServiceInterfaceFunction sif=(ServiceInterfaceFunction)ac.getBean("serviceGongNeng",ServiceGongNeng.class);
sif.print();
}
结果:正常可以执行接口实现类里的方法。(注意:types-matching="springAOP.ServiceGongNeng"没有execution)
重点扩展:所有基于配置文件的aspects,只支持单例模式(Single Model)。
查看全部 -
Advisors
1、advisor就像一个小的自包含的方面,只有一个advice。
2、切面自身通过一个bean表示,并且必须实现某个advice接口,同时,advisor也可以很好的利用AspectJ的切入点表达式(后面介绍AspectJ实现AOP时会介绍到AspectJ的切入点表达式)。
3、Spring通过配置文件中<aop:advisor>元素支持advisor,实际使用中,大多数情况下它会和transactional advice(事物相关的advice)配合使用(也可以有其他使用方式,根据项目或者工程的实际情况来决定)。
4、为了定义一个advisor的优先级以便让advice可以有序,可以使用order属性来定义advisor的顺序。
advisor的配置(<tx:advice>是对于事物的相关声明,通过这种方式使用advisor,这种方式是在使用Spring事物的控制时,经常使用的方式)
案例:(没有讲解advisor的案例,讲解了环绕通知的一个案例,适用场景:统计逻辑功能方法的调用次数,例如统计某方法调用3次,就不让调用了)
步骤1:
public class ServiceFunction {
public void print1(){
System.out.println("逻辑功能方法1执行了");
}
public void print2(){
System.out.println("逻辑功能方法2执行了");
throw new RuntimeException();
}
}
步骤2:
public class AspectFunction {
private int max_times;
public int getMax_times() {
return max_times;
}
public void setMax_times(int max_times) {
this.max_times = max_times;
}
public Object aspectFunction(ProceedingJoinPoint pjp) throws Throwable{
int num_times=0;
do{
num_times++;
try{
System.out.println(num_times+"次");
return pjp.proceed();
}catch(RuntimeException e){
}
}while(num_times<=max_times);
System.out.println("方法总共执行了"+num_times+"次");
return null;
}
}
步骤3:
查看全部 -
Spring的API实现AOP功能
1、这是Spring1.2的历史用法,现在Spring4.0仍然支持。
2、这是SpringAOP的基础
使用Spring的API实现AOP的原因:Schema-based(配置文件)和AspectJ实现AOP完全可以满足我们使用AOP的功能,使用API是因为这是SpringAOP的基础,可以加深我们应用和了解SpringAOP的理解(无论是注解方式、还是XML配置文件的方式,最终实现的基础都是和API密切关系的)。
3、现在SpringAOP的用法也是基于历史的,只是更简便了(在Spring1.2时配置文件很多,还需要了解各种配置)。
Spring的API
一、Pointcut(作为一个接口,有几个实现类)
1、Pointcut接口的实现类之一:NameMatchMethodPointcut,根据方法名字进行匹配。
2、该类有一个成员变量:mappedNames(它是一个集合,存放用于匹配的方法的名称),匹配方法的集合。
配置文件的例子:(sa开头的所有方法进行切入)
二、Before advice(原理:单独写一个前置通知的类,并实现MethodBeforeAdvice接口,并实现该接口的方法,)
1、一个简单的通知类型。
2、执行逻辑方法前被调用,不需要MethodInvocation对象。
3、前置通知可以在连接点执行之前插入自定义行为,但不能改变返回值。
案例:(会使用到后面的知识,暂时编写这些)
public class MoocBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] object, Object target) throws Throwable {
System.out.println("前置通知的方法:" + method.getName() + " " + target.getClass().getName());
}
}
三、Throws advice
1、如果连接点(执行的业务方法)出现异常,throws advice在方法返回后被调用。
2、如果throws-advice的方法抛出异常,那么它将覆盖原有异常。
3、接口org.springframework.aop.ThrowAdvice不包含任何方法,仅仅是一个声明,其实现类一定要实现类似这样的方法,void afterThrowing([Method, args, target],ThrowableSubclass);
Throws advice发生异常的说明
public void afterThrowing(Exception ex);//只有一个异常。
public void afterThrowing(RemoteException ex);//另外一种类型的异常。
public void afterThrowing(Method method,Object[] args(目标参数),Object target(对象),Exception ex);
public void afterThrowing(Method method,Object[] args(目标参数),Object target(对象),ServletException ex(其他类型的异常));
结论:异常通知(如上)方法参数里必须有异常,其他参数可有可无的。
抛出异常通知案例:
public class MoocThrowsAdvice implements ThrowsAdvice{
public void afterThrows(Exception ex){
System.out.println("抛出异常通知执行了");
}
public void afterThrows(Method method,Object[] object,Object target,Exception ex){
System.out.println("抛出异常通知执行了");
}
}
四、After Returning advice
1、返回后通知必须实现org.springframework.aop.AfterReturningAdvice接口(它和基于配置文件的配置方式是一样的)
2、可以访问返回值(但不能修改返回值)、被调用的方法、方法的参数和目标。
3、如果抛出异常,将会抛出拦截器链,替代返回值。
返回后通知案例
public class MoocAfterReturningAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("返回后通知方法执行了"+method.getName()+" "+target.getClass().getName()+" "+returnValue);
}
}
五、Interception around advice(环绕通知)
1、Spring的切入点模型使得切入点可以独立与advice重用,以针对不同的advice可以使用相同的切入点(和之前基于XML配置文件的AOP实现方式是一样的,切入点可以放在外面单独地去定义,通过Point reference,在每一个业务逻辑方法中都可以引用相同的切入点,当然,每个Advice也可以定义自己的Point cut)。
案例:
public class MoocMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object obj = null;
System.out.println("环绕前通知实现了");
obj = invocation.proceed();
System.out.println("环绕后通知实现了");
return null;
}
}
六、Introduction advice(和XML配置中的Introduction advice一样的功能)
1、Spring把引入通知作为一种特殊的拦截通知。
2、如果使用API实现AOP,则需要IntroductionAdvisor和IntroductionInterceptor这两个接口。
3、Introduction advice仅适用于类,不能和任何其它切入点一起使用。
如下为基于XML配置的Introduction advice内容
一个Spring test suite的例子
1、如果调用lock()方法,希望所有的setter方法抛出LockedException异常(使用场景:如使物体不可变,AOP典型例子)
2、需要完成繁重任务的IntroductionInterceptor,不是去实现该接口,而是使用org.springframework.aop.support.DelegatingIntroductionInterceptor
查看全部 -
ProxyFactoryBean及相关内容(上)
1、通过Spring API创建Spring AOP代理的基本方法是使用org.springframework.aop.framework.ProxyFactoryBean
2、ProxyFactoryBean可以完全控制切入点和通知(advice)以及他们的顺序。(和其它工厂Bean实现方式是一样的,都引入了一个中间层)
假如有这样一种情况:定义了一个Bean的Id为foo的ProxyFactoryBean,那么引用foo这个对象,看到的将不是ProxyFactoryBean本身,而是ProxyFactoryBean这个类通过getObject()方法创建的对象。
getObject():将创建一个AOP代理包装一个目标对象。(ProxyFactoryBean通过这种方式达到代理的目的)
3、使用ProxyFactoryBean或者其它IoC相关类来创建AOP代理的最重要好处是因为通知和切入点也可以由IoC来管理。
4、当被代理类没有实现任何接口,使用CGLIB代理,否则使用JDK代理。
5、通过设置proxyTargetClass为true,可强制使用CGLIB代理(无论代理类是否实现接口)
6、如果目标类实现了一个(或者多个)接口,那么创建代理的类型将依赖ProxyFactoryBean的配置。(因为ProxyFactoryBean里有一个ProxyInterfaces属性,该属性可以查看该类实现了哪些接口)
7、如果ProxyFactoryBean的proxyInterfaces属性被设置为一个或者多个全限定接口名(包括包名、类名、完整的名称),基于JDK的代理将被创建。
8、如果ProxyFactoryBean的proxyInterfaces属性没有被设置,但是目标类实现了一个(或者更多)接口,那么ProxyFactoryBean将自动检测到这个目标类已经实现了至少一个接口,创建一个基于JDK的代理。
例子:创建基于接口的代理
ProxyFactoryBean工作原理:首先,定义了一个Id为personTarget的Bean,又定义了一个Id为myAdvisor的Bean,定义了一个Id为debugInterceptor的Bean,重点在接下来的定义,定义了一个Id为person的Bean,但是对应的并不是Person类,而是Spring提供的ProxyFactoryBean这个类,并且这个Bean配置里有一个属性名为proxyInterfaces的<property>配置,并且它的value值为Person的路径,另外一个<property>标签属性名称为targer并且指向personTarget(这个类是Person的具体实现类),当我们get()Id为person这个Bean时,返回的并不是ProxyFactoryBean的对象,而是ProxyFactoryBean里的getObject()返回的对象,它返回的是属性名为target指向的personTarget的对象,通过ProxyFactoryBean创建代理的时候,可以指定它的interceptor,interceptorNames属性,是一个集合,可以通过List、Value指定它的具体内容。
案例:(通过Spring API实现通知)
各种通知的类前面已经写好了,所以主要进行XML配置。
public interface BizLogic {
String save();
}
public class BizLogicImpl implements BizLogic {
@Override
public String save() {
System.out.println("BizLogicImpl: BizLogicImpl save");
return "BizLogicImpl save";
}
}
XML配置:
<?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"
default-autowire="constructor">
<bean id="afterReturningAdvice" class="springAPI.MoocAfterReturningAdvice" ></bean>
<bean id="beforeAdvice" class="springAPI.MoocBeforeAdvice" ></bean>
<bean id="methodInterceptor" class="springAPI.MoocMethodInterceptor" ></bean>
<bean id="throwsAdvice" class="springAPI.MoocThrowsAdvice" ></bean>
<bean id="pointcutBean" class="org.springframework.aop.support.NameMatchMethodPointcut">
<property name="mappedNames">
<list>
<value>sa*</value>
</list>
</property>
</bean>
<bean id="defaultAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="beforeAdvice"></property>
<property name="pointcut" ref="pointcutBean"></property>
</bean>
<bean id="bizLogicImplTarget" class="springAPI.BizLogicImpl"></bean>
<bean id="bizLogicImpl" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target"><ref bean="bizLogicImplTarget"/></property>
<property name="interceptorNames">
<list>
<value>afterReturningAdvice</value>
<value>beforeAdvice</value>
<value>methodInterceptor</value>
<!-- <value>throwsAdvice</value>
-->
</list>
</property>
</bean>
</beans>
测试:
@Test
public void testApi(){
ApplicationContext ac=new ClassPathXmlApplicationContext("spring-ioc.xml");
BizLogic bl=(BizLogic)ac.getBean("bizLogicImpl");
bl.save();
}
结果:
前置通知的方法:save springAPI.BizLogicImpl
环绕前通知实现了
BizLogicImpl: BizLogicImpl save
环绕后通知实现了
返回后通知方法执行了save springAPI.BizLogicImpl null
查看全部 -
AspectJ介绍及Pointcut注解应用
1、@AspectJ的风格类似纯java注解的普通java类。
2、Spring可以使用AspectJ来做切入点解析。
3、AOP的运行时仍旧是纯的SpringAOP,对AspectJ的编译器或者织入无依赖性。
Spring中配置@AspectJ
1、对@AspectJ支持可以使用XML或者Java注解类型的配置(@EnableAspectJAutoProxy使用AspectJ进行自动代理)
2、前提:确保AspectJ的aspectjweaver.jar库包含在应用程序(版本1.6.8或更高版本)的classpath中,以Spring4.0.5为基准的,其他版本可能不一样(可以根据具体情况查找Spring具体文档)。
aspect注解的具体使用
1、@AspectJ切面使用@Aspect注解配置,拥有@Aspect的任何bean将被Spring自动识别并应用。
2、@Aspect注解的类可以有方法和字段,他们也可能包括切入点(pointcut),通知(Advice)和引入(introduction)声明。
3、@Aspect注解是不能够通过类路径自动检测发现的,所以需要配合使用@Component注释或者在xml配置bean(@Aspect注解的类配置在xml中)。
4、一个类中的@Aspect注解标识它为一个切面,并且将自己从自动代理中排出。
如何定义一个切入点
1、一个切入点通过一个普通的方法定义来提供,并且切入点表达式使用@Pointcut注解,方法返回类型必须为void
2、定义一个名为'anyOldTransfer',这个切入点将匹配任何名为“transfer”的方法的执行。
切入点支持哪些定义方式
组合Pointcut
1、切入点表达式可以通过&&、||和!进行组合,也可以通过名字引用切入点表达式。
2、通过组合,可以建立更加复杂的切入点表达式。
如何定义良好的pointcuts
1、AspectJ是编译期的AOP
2、检查代码并匹配连接点与切入点的代价是昂贵的。
3、一个好的切入点应该包括以下几点
【a】选择特定类型的连接点,如:execution,get,set,call,handler。
【b】确定连接点范围,如:within,withincode。
【c】匹配上下文信息,如:this,target,@annotation。
查看全部 -
Advice定义及实例(AspectJ中advice的定义)
一、Before advice:对于切面类使用@Aspect修饰类,相当于<aop:aspect>配置,在方法上添加@Before(“execution(* com.imooc.aop.aspectj.biz.*Biz.*(..))”),相当于<aop:before>标签,@Component修饰类,省略了配置<bean>标签。
案例(前置通知):
步骤1:业务方法
public class MoocBiz {
public void print(){
System.out.println("业务方法执行了");
}
}
步骤2:切面方法
@Aspect
public class MoocAspect {
@Before("execution(* *Biz.*(..))")
public void beforeAdvice(){
System.out.println("前置通知方法执行了");
}
}
XML配置:
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="src"></context:component-scan>
<bean id="moocBiz" class="MoocBiz"></bean>
<bean id="moocAspect" class="MoocAspect"></bean>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
测试:
@Test
public void testMooc(){
ApplicationContext ac=new ClassPathXmlApplicationContext("spring-ioc2.xml");
MoocBiz mb=(MoocBiz) ac.getBean("moocBiz");
mb.print();
}
结果:
前置通知方法执行了
业务方法执行了
可以定义共享的pointcut(一个方法上使用@Pointcut("execution(* com.imooc.aop.aspectj.biz.*Biz.*(..))"),然后@Before("pointcut()")修饰另一个方法上
@Aspect
public class MoocAspect {
@Pointcut("execution(* *Biz.*(..))")
public void pointcut(){
}
@Before("pointcut()")
public void beforeAdvice(){
System.out.println("前置通知方法执行了");
}
}
二、After returning advice
和前置通知相似。
有时候需要在通知体内得到返回的实际值,可以使用@AfterReturning绑定返回值的形式,如果知道返回值类型可以写类型,如果不知道,可以写Object。
后置通知案例:
public class MoocBiz {
public void print(){
System.out.println("业务方法执行了");
}
}
@Aspect
public class MoocAspect {
@Pointcut("execution(* *Biz.*(..))")
public void pointcut(){
}
@Before("pointcut()")
public void beforeAdvice(){
System.out.println("前置通知方法执行了");
}
@AfterReturning(pointcut="pointcut()",returning="o")
public void afterAdvice(Object o){
System.out.println("AfterReturning:"+o);
}
}
<context:component-scan base-package="src"></context:component-scan>
<bean id="moocBiz" class="MoocBiz"></bean>
<bean id="moocAspect" class="MoocAspect"></bean>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
结果:
前置通知方法执行了
业务方法执行了
AfterReturning:null
三、After throwing advice
有时候需要在通知体内得到异常的实际值,可以使用@AfterReturning绑定返回值的形式
案例:
@AfterThrowing(pointcut="pointcut()",throwing="t")
public void afterThrowAdvice(RuntimeException t){
System.out.println("异常:"+t.getMessage());
}
public class MoocBiz {
public void print(){
System.out.println("业务方法执行了");
throw new RuntimeException("运行时异常");
}
}
结果:
前置通知方法执行了
业务方法执行了
异常:运行时异常
四、After(finally)advice
最终通知必须准备处理正常和异常两种返回情况,它通常用于释放资源。(类似于try-catch中finally的功能)
@After("pointcut()")
public void afterAdvice(){
System.out.println("后置通知执行了");
}
前置通知方法执行了
业务方法执行了
后置通知执行了
异常:运行时异常
五、环绕通知(Around Advice)
1、环绕通知使用@Around注解来声明,通知方法的第一个参数必须是ProceedingJoinPoint类型
2、在通知方法内部会调用ProceedingJoinPoint的proceed()方法会导致执行真正的方法,同时可以传入一个Object[]对象,数组中的值将被作为参数传递给方法。
案例:
@Around("pointcut()")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("环绕前置通知方法执行了========");
Object o=pjp.proceed();
System.out.println("环绕后置通知方法执行了========");
return o;
}
结果:
环绕前置通知方法执行了========
前置通知方法执行了
业务方法执行了
环绕后置通知方法执行了========
后置通知执行了
AfterReturning:null
查看全部 -
Advice扩展
一、给advice传递参数
【a】方式一:在通知注解里写参数,通过&& args(参数名,..)传入参数,该参数可以是自己定义的类型,也可以为String等类型。(通常获取参数,进行一些判断或者日志的记录)
【a】方式二:切入点和通知分开写的方式,在切入点注解里写参数,在通知注解里引用切入点注解的方法名。
【b】运行时,生效的一个注解,这两个注解修饰在方法上,在通知注解里通过&& @annotation(运行时注解修饰的方法名,首字母小写),如果某个方法使用了运行时注解,则一定会给value()附一个值,在通知方法里可以通过方法参数,调用该方法得到该值(一般记录该注解用了哪些方法,或者用在判断该方法上是否加了某注解,后者判断获取到的值是哪值)
查看全部 -
Bean的生命周期:
分为4个阶段
定义
初始化
使用
销毁
查看全部 -
Bean的作用域
singleton:单例,指一个Bean容器中只存在一份。
prototype:每次请求(每次使用)创建新的实例,destroy方式不生效。
request:每次http请求创建一个实例且仅在当前request内有效。
session:同上,每次http请求擦黄健,当前session内有效。
global session:基于portlet的web中有效(portlet定义了global session),如果是在web中,同session
查看全部 -
Bean配置项
Id:在整个IoC容器中的唯一标识。
Class:具体要实例化的哪个类。
Scope:作用域。
Constructor arguments:构造器的参数。
Properties:属性。
Autowiring mode:自动装配的模式。
lazy-initialization mode:懒加载模式。
Initialzation/destruction method:初始化/销毁的方法。
理论上,处理class之外其他都可以不配置。但想要从Bean容器中得到某一个实例,有两种方式,一种是通过Id,另一种是通过Bean 的类型。
查看全部 -
设值注入例子
调用set方法
查看全部
举报