前言:所有基于配置文件的aspects只支持singleton model。
一、什么是AOP及实现方式
1.AOP:Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
2.主要功能是:日志记录,性能统计,安全控制,事务处理,异常处理等。
3.AOP实现方式
——预编译:AspectJ
——运行期动态代理(JDK动态代理,CGLIB动态代理):SpringAOP、JbossAOP
4.AOP的用途
——提供了声明式的企业服务,特别是EJB的替代服务的声明。
——允许用户定制自己的方面,以完成OOP(面向对象)与AOP的互补使用。
二、AOP基本概念
1.相关概念(Schema-based 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: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/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<bean id="csdnAspect" class="com.csdn.aop.schema.advice.CsdnAspect"></bean>
<bean id="aspectBiz" class="com.csdn.aop.schema.advice.biz.AspectBiz"></bean>
<aop:config>
<aop:aspect id="csdnAspectAOP" ref="csdnAspect">
</aop:aspect>
</aop:config>
</beans>
切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象。
连接点(Joinpoint):程序执行过程中的某个特定的点。
通知(Advice):在切面的某个特定的连接点上执行的动作。
切入点(Pointcut):匹配连接点的断言,在AOP中通知和一个切入点表达式关联。
引入(Introduction):在不修改代码的前提下,为类添加新的方法和属性。
目标对象(Target Object):被一个或者多个切面所通知的对象。
AOP代理(AOP Proxy):AOP框架创建的对象,用来实现切面契约(aspect contract)(包括通知方法执行等功能)。
织入(Weaving):把切面连接到其他的应用程序类型或者对象上,并创建一个被通知的对象,分为:编译时织入、类加载时织入、执行时织入。
——Advice的类型:
*Around Advice通知方法的第一个参数必须是ProceedingJoinPoint类型。
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<bean id="csdnAspect" class="com.csdn.aop.schema.advice.CsdnAspect"></bean>
<bean id="aspectBiz" class="com.csdn.aop.schema.advice.biz.AspectBiz"></bean>
<aop:config>
<aop:aspect id="csdnAspectAOP" ref="csdnAspect">
<aop:pointcut expression="execution(* com.csdn.aop.schema.advice.biz.*Biz.*(..))" id="csdnPiontcut"/>
<aop:before method="before" pointcut-ref="csdnPiontcut"/>
<aop:after-returning method="afterReturning" pointcut-ref="csdnPiontcut"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="csdnPiontcut"/>
<aop:after method="after" pointcut-ref="csdnPiontcut"/>
<aop:around method="around" pointcut-ref="csdnPiontcut"/>
<aop:around method="aroundInit" pointcut="execution(* com.csdn.aop.schema.advice.biz.AspectBiz.init(String, int))
and args(bizName, times)"/>
</aop:aspect>
</aop:config>
</beans>
public class AspectBiz {
public void biz() {
System.out.println("AspectBiz biz.");
// throw new RuntimeException();
}
public void init(String bizName, int times) {
System.out.println("AspectBiz init : " + bizName + " " + times);
}
}
public class CsdnAspect {
public void before() {
System.out.println("CsdnAspect before.");
}
public void afterReturning() {
System.out.println("CsdnAspect afterReturning.");
}
public void afterThrowing() {
System.out.println("CsdnAspect afterThrowing.");
}
public void after() {
System.out.println("CsdnAspect after.");
}
public Object around(ProceedingJoinPoint pjp) {
Object obj = null;
try {
System.out.println("CsdnAspect around 1.");
obj = pjp.proceed();
System.out.println("CsdnAspect around 2.");
} catch (Throwable e) {
e.printStackTrace();
}
return obj;
}
public Object aroundInit(ProceedingJoinPoint pjp, String bizName, int times) {
System.out.println(bizName + " " + times);
Object obj = null;
try {
System.out.println("CsdnAspect aroundInit 1.");
obj = pjp.proceed();
System.out.println("CsdnAspect aroundInit 2.");
} catch (Throwable e) {
e.printStackTrace();
}
return obj;
}
}
@RunWith(BlockJUnit4ClassRunner.class)
public class TestAOPSchemaAdvice extends UnitTestBase {
public TestAOPSchemaAdvice() {
super("classpath:spring-aop-schema-advice.xml");
}
@Test
public void testBiz() {
AspectBiz biz = super.getBean("aspectBiz");
biz.biz();
}
@Test
public void testInit() {
AspectBiz biz = super.getBean("aspectBiz");
biz.init("csdnService", 3);
}
}
——pointcut:
execution(public * *(..))切入点为执行所有public方法时
execution(* set*(..))切入点为执行所有set开始的方法时
execution(* com.xyz.service.AccountService.*(..))切入点为执行AccountService类中的所有方法时
execution(* com.xyz.service..(..))切入点为执行com.xyz.service包下的所有方法时
execution(* com.xyz.service...(..))切入点为执行com.xyz.service包及其子包下的所有方法
within(com.xyz.service.*)(only in Spring AOP) within(com.xyz.service..*)(only in Spring AOP) within用于匹配指定类型内的方法执行
this(com.xyz.service.AccountService)(only in Spring AOP) this用于匹配当前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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<bean id="csdnAspect" class="com.csdn.aop.schema.advice.CsdnAspect"></bean>
<bean id="aspectBiz" class="com.csdn.aop.schema.advice.biz.AspectBiz"></bean>
<aop:config>
<aop:aspect id="csdnAspectAOP" ref="csdnAspect">
<aop:pointcut expression="execution(* com.csdn.aop.schema.advice.biz.AspectBiz.*(..))" id="csdnPointcut">
</aop:aspect>
</aop:config>
</beans>
——Introductions
简介允许一个切面声明一个实现指定接口的通知对象,并且提供了一个接口实现类来代表这些对象。
由<aop:aspect>中的<aop:declare-parents>元素声明该元素用于声明所匹配的类型拥有一个新的parent(因此得名)。
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<bean id="csdnAspect" class="com.csdn.aop.schema.advice.MoocAspect"></bean>
<bean id="aspectBiz" class="com.csdn.aop.schema.advice.biz.AspectBiz"></bean>
<aop:config>
<aop:aspect id="csdnAspectAOP" ref="csdnAspect">
<aop:declare-parents types-matching="com.csdn.aop.schema.advice.biz.*(+)"
implement-interface="com.csdn.aop.schema.advice.Fit"
default-impl="com.csdn.aop.schema.advice.FitImpl"/>
</aop:aspect>
</aop:config>
</beans>
public interface Fit {
void filter();
}
public class FitImpl implements Fit {
@Override
public void filter() {
System.out.println("FitImpl filter.");
}
}
@RunWith(BlockJUnit4ClassRunner.class)
public class TestAOPSchemaAdvice extends UnitTestBase {
public TestAOPSchemaAdvice() {
super("classpath:spring-aop-schema-advice.xml");
}
@Test
public void testFit() {
Fit fit = (Fit)super.getBean("aspectBiz");
fit.filter();
}
}
2.Advisors
——advisor就像一个小的自包含的方面,只有一个advice。
——切面自身通过一个bean表示,并且必须实现某个advice接口,同时,advisors也可以很好的利用AspectJ的切入点表达式。
——Spring通过配置文件中<aop:advisor>元素支持advisor实际使用中,大多数情况下它会和transactional advice配合使用。
——为了定义一个advisor的优先级以便让advice可以有序,可以使用order属性来定义advisor的顺序。
<?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="com.csdn.aop.schema"></context:component-scan>
<aop:config>
<aop:aspect id="concurrentOperationRetry" ref="concurrentOperationExecutor">
<aop:pointcut id="idempotentOperation"
expression="execution(* com.csdn.aop.schema.advisors.service.*.*(..)) " />
<!-- expression="execution(* com.csdn.aop.schema.service.*.*(..)) and -->
<!-- @annotation(com.csdn.aop.schema.Idempotent)" /> -->
<aop:around pointcut-ref="idempotentOperation" method="doConcurrentOperation" />
</aop:aspect>
</aop:config>
<bean id="concurrentOperationExecutor" class="com.csdn.aop.schema.advisors.ConcurrentOperationExecutor">
<property name="maxRetries" value="3" />
<property name="order" value="100" />
</bean>
</beans>
@Service
public class InvokeService {
public void invoke() {
System.out.println("InvokeService ......");
}
public void invokeException() {
throw new PessimisticLockingFailureException("");
}
}
public class ConcurrentOperationExecutor implements Ordered {
private static final int DEFAULT_MAX_RETRIES = 2;
private int maxRetries = DEFAULT_MAX_RETRIES;
private int order = 1;
public void setMaxRetries(int maxRetries) {
this.maxRetries = maxRetries;
}
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
int numAttempts = 0;
PessimisticLockingFailureException lockFailureException;
do {
numAttempts++;
System.out.println("Try times : " + numAttempts);
try {
return pjp.proceed();
} catch (PessimisticLockingFailureException ex) {
lockFailureException = ex;
}
} while (numAttempts <= this.maxRetries);
System.out.println("Try error : " + numAttempts);
throw lockFailureException;
}
}
@RunWith(BlockJUnit4ClassRunner.class)
public class TestAOPSchemaAdvisors extends UnitTestBase {
public TestAOPSchemaAdvisors() {
super("classpath:spring-aop-schema-advisors.xml");
}
@Test
public void testSave() {
InvokeService service = super.getBean("invokeService");
service.invoke();
System.out.println();
service.invokeException();
}
}
三、Spring中的AOP
1.Spring中的AOP实现
——纯Java实现,无需特殊的编译过程,不需要控制类加载器层次。
——目前只支持方法执行连接点(通知Spring Bean的方法执行)。
——不是为了提供最完整的AOP实现(尽管它非常强大);而是侧重于提供一种AOP实现和Spring IoC容器之间的整合,用于帮助解决企业应用中的常见问题。
——Spring AOP不会与AspectJ竞争,从而提供综合全面的AOP解决方案。
2.有接口和无接口的Spring AOP实现区别
——Spring AOP默认使用标准的JavaSE动态代理作为AOP代理,这使得任何接口(或者接口集)都可以被代理。
——Spring AOP中也可以使用CGLIB代理(如果一个业务对象并没有实现一个接口)
四、ProxyFactoryBean
1.创建Spring AOP代理的基本方法是使用org.springframework.aop.framework.ProxyFactoryBean。
2.这可以完全控制切入点和通知以及他们的顺序。
3.使用ProxyFactoryBean或者其他IoC相关类来创建AOP代理的最重要好处是通知和切入点也可以由IoC来管理。
4.被代理类没有实现任何接口,使用CGLIB代理,否则JDK代理。
5.通过设置proxyTargetClass为true,可强制使用CGLIB。
6.如果目标类实现了一个(或多个)接口,那么创建代理的类型依赖ProxyFactoryBean的配置。
7.如果ProxyFactoryBean的proxyInterFaces属性被设置为一个或者多个全限定接口名,基于JDK的代理将被创建。
8.如果ProxyFactoryBean的proxyInterfaces属性没有被设置,但是目标类实现了一个(或者更多)接口,那么ProxyFactoryBean将自动检测到这个目标类已经实现了至少一个接口,创建一个基于JDK的代理。
9.如果想,可以强制在任何情况下使用CGLIB,即使有接口。
10.CGLIB代理的工作原理是在运行时生成目标类的子类,Spring配置这个生成的子类委托方法调用到原来的目标。
11.子类是用来实现Deorator模式,织入通知。
12.CGLIB的代理对用户是透明的,需要注意:
——final方法不能被通知,因为他们不能被覆盖。
——不用把CGLIB添加到classpath中,在Spring3.2中,CGLIB被重新包装并包含在Spring核心的JAR(即基于CGLIB的AOP就像JDK动态代理一样“开箱即用”)。
13.使用global advisors:用*做通配,匹配所有拦截器加入通知链。
14.简化的proxy定义:使用父子bean定义,以及内部bean定义,可能会带来更清洁和更简洁的代理定义(抽象属性标记父bean定义为抽象的这样它不能被实例化)。
15.使用ProxyFactory:
——使用Spring AOP而不必依赖于Spring IoC
——大多数情况下最佳实践是用IoC容器创建AOP代理
——虽然可以硬编码方式实现,但是Spring推荐使用配置或注解方式实现
16.使用“auto-proxy”:
——Spring也允许使用“自动代理”的bean定义,它可以自动代理选定的bean,这是建立在Spring的“bean post processor”功能基础上的(在加载bean的时候就可以修改)。
——BeanNameAutoProxyCreator
<?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">
<bean id="csdnBeforeAdvice" class="com.csdn.aop.api.CsdnBeforeAdvice"></bean>
<bean id="csdnAfterReturningAdvice" class="com.csdn.aop.api.CsdnAfterReturningAdvice"></bean>
<bean id="csdnMethodInterceptor" class="com.csdn.aop.api.CsdnMethodInterceptor"></bean>
<bean id="csdnThrowsAdvice" class="com.csdn.aop.api.CsdnThrowsAdvice"></bean>
<!-- <bean id="bizLogicImplTarget" class="com.csdn.aop.api.BizLogicImpl"></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="csdnBeforeAdvice" /> -->
<!-- <property name="pointcut" ref="pointcutBean" /> -->
<!-- </bean> -->
<!-- <bean id="bizLogicImpl" class="org.springframework.aop.framework.ProxyFactoryBean"> -->
<!-- <property name="target"> -->
<!-- <ref bean="bizLogicImplTarget"/> -->
<!-- </property> -->
<!-- <property name="interceptorNames"> -->
<!-- <list> -->
<!-- <value>defaultAdvisor</value> -->
<!-- <value>csdnAfterReturningAdvice</value> -->
<!-- <value>csdnMethodInterceptor</value> -->
<!-- <value>csdnThrowsAdvice</value> -->
<!-- </list> -->
<!-- </property> -->
<!-- </bean> -->
<!-- <bean id="bizLogicImplTarget" class="com.csdn.aop.api.BizLogicImpl"></bean> -->
<!-- <bean id="bizLogicImpl" class="org.springframework.aop.framework.ProxyFactoryBean"> -->
<!-- <property name="proxyInterfaces"> -->
<!-- <value>com.csdn.aop.api.BizLogic</value> -->
<!-- </property> -->
<!-- <property name="target"> -->
<!-- <bean class="com.csdn.aop.api.BizLogicImpl"></bean> -->
<!-- <ref bean="bizLogicImplTarget"/> -->
<!-- </property> -->
<!-- <property name="interceptorNames"> -->
<!-- <list> -->
<!-- <value>csdnBeforeAdvice</value> -->
<!-- <value>csdnAfterReturningAdvice</value> -->
<!-- <value>csdnMethodInterceptor</value> -->
<!-- <value>csdnThrowsAdvice</value> -->
<!-- <value>csdn*</value> -->
<!-- </list> -->
<!-- </property> -->
<!-- </bean> -->
<bean id="baseProxyBean" class="org.springframework.aop.framework.ProxyFactoryBean"
lazy-init="true" abstract="true"></bean>
<bean id="bizLogicImpl" parent="baseProxyBean">
<property name="target">
<bean class="com.csdn.aop.api.BizLogicImpl"></bean>
</property>
<property name="proxyInterfaces">
<value>com.csdn.aop.api.BizLogic</value>
</property>
<property name="interceptorNames">
<list>
<value>csdnBeforeAdvice</value>
<value>csdnAfterReturningAdvice</value>
<value>csdnMethodInterceptor</value>
<value>csdnThrowsAdvice</value>
</list>
</property>
</bean>
</beans>
public class CsdnAfterReturningAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable {
System.out.println("CsdnAfterReturningAdvice : " + method.getName() + " " +
target.getClass().getName() + " " + returnValue);
}
}
public class CsdnBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("CsdnBeforeAdvice : " + method.getName() + " " +
target.getClass().getName());
}
}
public class CsdnMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("CsdnMethodInterceptor 1 : " + invocation.getMethod().getName() + " " +
invocation.getStaticPart().getClass().getName());
Object obj = invocation.proceed();
System.out.println("CsdnMethodInterceptor 2 : " + obj);
return obj;
}
}
public interface BizLogic {
String save();
}
public class BizLogicImpl implements BizLogic {
public String save() {
System.out.println("BizLogicImpl : BizLogicImpl save.");
return "BizLogicImpl save.";
// throw new RuntimeException();
}
}
@RunWith(BlockJUnit4ClassRunner.class)
public class TestAOPAPI extends UnitTestBase {
public TestAOPAPI() {
super("classpath:spring-aop-api.xml");
}
@Test
public void testSave() {
BizLogic logic = (BizLogic)super.getBean("bizLogicImpl");
logic.save();
}
}