相信经过上一篇博客,大家都对Spring的bean有所了解,今天就继续带大家学习Spring的AOP。
AOP定义
AOP:Aspect Oriented Programming 的缩写,意为:面向切面的编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
主要功能:日志记录,性能统计,安全控制,事务处理,异常处理。
AOP实现方式:
1. 预编译方式-AspectJ2. 运行期动态代理(JDK动态代理,CGLib动态代理)-SpringAOP、JbossAOP
Spring框架中AOP的用途
提供了声明式的企业服务,特别是EJB的替代服务的申明。允许用户定制自己的方面,以完成oop与aop的互补使用。
Spring的AOP实现
- 纯java实现,无需特殊的编译过程,不需要控制类加载器层次。
- 目前只支持方法执行连接点(通过Spring Bean 的方法执行)
- 不是为了提供最完整的AOP实现,而是侧重于提供一种AOP实现和Spring Ioc容器之间的整合,用于帮助解决企业应用中的常见问题
有接口和无接口的spring aop实现
Spring AOP默认使用标准的JavaSE动态代理作为AOP代理,这使得任何接口(或者接口集)都可以被代理Spring AOP 中也可以使用CGLIB代理(如果一个业务对象并没有实现一个接口)
基于配置的AOP实现
Spring所有的切面和通知器都必须放在一个<aop:config>内(可以配置包含多个<aop:config>元素),每一个<aop:config>可以包含pointcut,advisor和aspect元素(他们必须按照这个顺序进行申明)。<aop:config>风格的配置大量使用Spring的自动代理机制。
下面是配置代码:
<?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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.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="com.example.test"/>
<context:property-placeholder location="classpath:com/example/test/config.properties"/>
<bean id="beanlife" name="BeanLife"/>
<aop:config>
<aop:aspect id="beanlifeaspect" ref="beanlife"></aop:aspect>//这句代码的意思就是将id为beanlife作为bean。
</aop:config>
</beans>
要使用aop标签,必须导入上面相应的aop信息,在导入时候,如果顺序不对会报一个错,可以见我博客spring配置异常查看。
pointcut
execution用于匹配方法执行的连接点。
<aop:config>
<aop:aspect id="beanlifeaspect" ref="beanlife">
<aop:pointcut id="beanlifepointcut" expression="*com.example.test...(..)"/>//切入点匹配该包即包下的子类所有方法
</aop:aspect>
</aop:config>
Advice 配置
<bean id="testaspect" class="com.example.test.TestAspect"/> <bean id="testbean" class="com.example.test.TestBean"/> <aop:config> <aop:aspect id="beanlifeaspect" ref="testaspect"> <aop:pointcut expression="execution(* com.example.test.TestBean.*(..))" id="beanlifepointcut"/> <aop:before method="before" pointcut-ref="beanlifepointcut"/> <aop:after-returning method="return1" pointcut-ref="beanlifepointcut"/> <aop:after-throwing method="exception1" pointcut-ref="beanlifepointcut"/> <aop:after method="after" pointcut-ref="beanlifepointcut"/>
</aop:aspect></aop:config><aop:around method="round" pointcut-ref="beanlifepointcut"/>
public class TestAspect { public void before(){ System.out.println("这是before"); } public void return1(){ System.out.println("这是return"); } public void exception1(){ System.out.println("这是exception"); } public void after(){ System.out.println("这是after"); }
public Object round(ProceedingJoinPoint pjp){ Object o = null; try { System.out.println("这是round环绕之前"); o = pjp.proceed(); System.out.println("这是round环绕之后"); } catch (Throwable e) { // TODO Auto-generated catch block e.printStackTrace(); } return o; }
} 这是自己的逻辑代码
public class TestBean {
public void test(){
System.out.println("这是执行的方法");
throw new RuntimeException();
}
}
上述将advice基本配置用法列出来,需要注意几点:
- 如果after-throwing 遇到异常,方法将不能正确返回,即使有after-returning方法也不会执行。
- after 遇到异常,是可以执行方法。
- round方法参数类型第一个必须是ProceedingJoinPoint,否则会出错。
- 在上述expression="execution(* com.example.test.TestBean.*(..))" ,在第一个*后面有个空格,一定要注意,否则会报错。
advice参数传递
配置信息:
<aop:around method="round1" pointcut="execution(* com.example.test.TestBean.testarg(String)) and args(name)"/>
public void testarg(String name){
System.out.println("我是传过来的参数:"+name);
}
public Object round1(ProceedingJoinPoint pjp,String name){
Object o = null;
try {
System.out.println("这是round1"+name);
o = pjp.proceed();
System.out.println("这是round2");
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return o;
}
Introductions
简介(Introduce)允许一个切面声明一个实现指定接口的通知对象,并且提供了一个接口实现类来代表这些对象。
由<aop:aepect>中的<aop:declare-parents> 元素声明该元素用于申明所匹配的类型拥有一个新的parent。
下面具体例子代码:
先申明一个接口
public interface Filter {
void filterMehtod();
}
接口实现类
public class FilterImpl implements Filter{
@Override
public void filterMehtod() {
// TODO Auto-generated method stub
System.out.println("这是父类接口方法");
}
}
<aop:declare-parents types-matching="com.example.test.TestBean" implement-interface="com.example.test.Filter" default-impl="com.example.test.FilterImpl"/>
测试
Filter fit= (Filter) context.getBean("testbean");
fit.filterMehtod();
该实现就是将先定义好的接口,实现类,然后根据匹配的类添加接口父类,然后在该bean,就可以使用接口实现类的方法。我们常常称这个,认干爸,花他的钱。哈哈!!
切面的模式
注意在配置文件中配置的Aspect,模式是单例模式(singleton)。
Advisors
- advisor就像一个小的自包含的方面,只有一个advice。
- 切面自身通过一个bean表示,并且必须实现某个advice接口,同时,advisor也可以很好的利用AspectJ的切入点表达式。
- Spring通过配置文件中<aop:advisor>元素支持advisor实际使用中,大多数情况它会和transactional advice配合使用。
- 为了定义一个advisor的优先级以便让advice可以有序,可以使用order属性来定义advisor的顺序。