一直再用aop,一直都没时间整理一下,得空整理一下,备忘!
Pointcut可以有下列方式来定义或者通过&& || 和!的方式进行组合.
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)
任意公共方法的执行:
execution(public * *(..))
任何一个以“set”开始的方法的执行:
execution(* set*(..))
AccountService 接口的任意方法的执行:
execution(* com.xyz.service.AccountService.*(..))
定义在service包里的任意方法的执行:
execution(* com.xyz.service.*.*(..))
定义在service包和所有子包里的任意类的任意方法的执行:
execution(* com.xyz.service..*.*(..))
定义在pointcutexp包和所有子包里的JoinPointObjP2类的任意方法的执行:
execution(* com.test.spring.aop.pointcutexp..JoinPointObjP2.*(..))")
带有@Transactional标注的所有类的任意方法.
@within(org.springframework.transaction.annotation.Transactional)
@target(org.springframework.transaction.annotation.Transactional)(注解类型必须是全限定类型名)
带有@Transactional标注的任意方法.
@annotation(org.springframework.transaction.annotation.Transactional)
参数带有@Transactional标注的方法.
@args(org.springframework.transaction.annotation.Transactional)
1. 当前 AOP 对象实现了IPointcutService 接口的任何方法(this 中
使用的表达式必须是类型全限定名,不支持通配符)
this(cn.javass.spring.chapter6.service.IIntroductionService)
2. cn.javass 包及子包下的任何方法执行
within(cn.javass..*)
3. target :使用 “target( 类型全限定名 )” 匹配当前目标对象类型的执行方法;注意
是目标对象的类型匹配,这样就不包括引入接口也类型匹配;注意 target 中使用的表
达式必须是类型全限定名,不支持通配符;
args表达式 有如下两个作用:
① 提供了一种简单的方式来访问目标方法的参数。
② 可用于对切入表达式增加额外的限制。
@AfterReturning(returning="retVal", pointcut="execution(* com.chapa.annotation.service.*.*(..)) && args(var,word)")
public void access(String var, String word, Object retVal){
//todo
}
@AfterReturning(returning="retVal", pointcut="execution(* com.chapa.annotation.service.*.*(..)) && args(String,String)")
public void access( Object retVal){
//todo
}
aop 通过spring容器,基于代理类运行时实现aop功能,注意所有的相关类必须是基于spring容器初始化,否则无法实现aop功能
cglib 能基于具体实现类实现动态代理,默认的jdk动态代理的不能对具体类实现代理,只能基于接口.
<aop:config proxy-target-class="false">
<aop:aspect id="TestAspect" ref="aspectAdvice" order="1">
<aop:pointcut id="businessService" expression="execution(* com.chapa.service.*.*(..))" />
<aop:before pointcut-ref="businessService" method="doBefore"/>
<aop:after pointcut-ref="businessService" method="doAfter"/>
<aop:around pointcut-ref="businessService" method="doAround"/>
<aop:after-throwing pointcut-ref="businessService" method="doThrowing" throwing="ex"/>
aop:aspect>
<aop:aspect id="secondAspect" ref="secondAdvice" order="2">
<aop:pointcut id="businessService" expression="execution(* com.chapa.service.*.*(..))" />
<aop:before pointcut-ref="businessService" method="doBefore"/>
<aop:after pointcut-ref="businessService" method="doAfter"/>
<aop:around pointcut-ref="businessService" method="doAround"/>
<aop:after-throwing pointcut-ref="businessService" method="doThrowing" throwing="ex"/>
aop:aspect>
aop:config>
<context:component-scan base-package="com.chapa.annotation.*"/>
<context:annotation-config/>
<aop:aspectj-autoproxy proxy-target-class="true"/>
aspectj实现,在编译期已经完成了相关aop功能的织入.基于 aspectj 实现时,需要用ajc 自己的编译器,
同时因为aspectj实现,实际上是已经在编译期把对应aop代码织入到目标对象,所以不需要通过spring容器初始化对象,实际上aspectj实现已经和spring没有关系了.
<plugin>
<groupId>org.codehaus.mojogroupId>
<artifactId>aspectj-maven-pluginartifactId>
<configuration>
<complianceLevel>1.8complianceLevel>
configuration>
<executions>
<execution>
<goals>
<goal>compilegoal>
<goal>test-compilegoal>
goals>
execution>
executions>
plugin>
编译器设置ajc编译,idea参见下文,eclipse需要安装插件,自行百度
coped-proxy 指定由ScopedProxyFactoryBean创建bean,ScopedProxyFactory为这个Bean增加了getTargetObject的方法(使用Introduction),因此所有带上了这个标签的Bean,也就默认实现了ScopedObject的接口,可以调用getTargetObject方法。这个方法的意义在于,因为代理Bean的scope是默认singleton的,这也就意味着,我们每次调用applicationContext.getBean方法,总是返回同一个代理bean,如果我们想要获得scope下真正的bean的话,就实际上调用getTargetObject方法了。
不加scoped-proxy,引用该对象时,只有在最开始注入的时候,获得该对象的代理,也就是说实际上这个引用指向spring初始话的唯一代理对象,无法实现真正意义上的这个对象对因的scope周期.(该注解 合适使用在httpsession 场景,session Scope是web环境下的,有兴趣的可以自行验证)
<aop:aspect>
<aop:declare-parents
types-matching="com.chapa.service.TestServiceImpl"
implement-interface="com.chapa.service.IIntroduce"
default-impl="com.chapa.service.IntroduceImpl"/>
aop:aspect>
<bean id="testService" class="com.chapa.service.TestServiceImpl" scope="prototype">
<aop:scoped-proxy/>
bean>
<bean class="com.chapa.service.TestServiceWithoutInterface">bean>
<bean id="singletonBean" class="com.chapa.service.SingletonBean" >
<property name="testService">
<ref bean="testService" />
property>
bean>
注:coped-proxy 和 Introduction (aop:declare-parents) 共同使用的时候不能设置对像scope为prototype,会导致注入的类强制转换失败(参见demo,具体原因,有兴趣的可以研读一下代码,坐等大神解惑)
这两个都是用来 指定织入顺序,有些业务逻辑必须指定织入顺序否则会导致结果不一致.aop的织入顺序类似 serverlet的 filter ,先进后出,环形嵌套织入的规则. @Order(1) 值越小优先级越大, 只对spring aop 有效,对aspectj 编译实现的aop无效.@DeclarePrecedence ,aspectj 专用的指定顺序的注解,使用改注解必须使用 ajc 编译,使用javac编译会报错.
允许我们向现有的类添加新的方法和属性,类似Adatper模式. (@DeclareParents 注解方式,config方式参见上文). value表示目标对象,defaultImpl 待引用对象的实现类.
@DeclareParents(value = "com.chapa.annotation.service.TestServiceImpl",defaultImpl =com.chapa.annotation.service.IntroduceImpl.class)
IIntroduce testService;
aop 默认无法对 对象调用自己的方法实现增强,必须基于expose-proxy,实现AOP代理对象的ThreadLocal支持.
<aop:aspectj-autoproxy expose-proxy="true"/><!—注解风格支持-->
<aop:config expose-proxy="true"><!—xml风格支持-->
设置expose-proxy后,按下面的方式调用当前类的方法,就能实现aop
public void hhh() {
System.out.println("TestServiceImpl with interface");
((ITestService) AopContext.currentProxy()).hprintTime();
}
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// isProxyTargetClass() 通过spring 设置 是否强制 使用 cglib 默认 false
if(!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
return new JdkDynamicAopProxy(config);
} else {
Class targetClass = config.getTargetClass();
if(targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
} else if(targetClass.isInterface()) {
return new JdkDynamicAopProxy(config);
} else if(!cglibAvailable) {
throw new AopConfigException("Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces.");
} else {
return DefaultAopProxyFactory.CglibProxyFactory.createCglibProxy(config);
}
}
}
//通过类加载器 判断 当前环境是否加载了 cglib 相关类
static {
cglibAvailable = ClassUtils.isPresent("net.sf.cglib.proxy.Enhancer", DefaultAopProxyFactory.class.getClassLoader());
}
注:早期版本项目必须依赖cglib 否则 对无接口类的 代理实现 会报错
<dependency>
<groupId>cglibgroupId>
<artifactId>cglibartifactId>
<version>2.2.2version>
dependency>
注:从 spring 3.2.2 版本开始 spring 提供了 cglib 的实现 org.springframework.cglib.proxy.Enhancer,不用在强制依赖cglib
@Aspect
@Component // @Repository,@Service,@Controller 表识 需要spring 加载到容器中 ,本质上目前没发现 他们四个有任何区别,不过语义上最好用在对应的层级上
@Scope(value=ConfigurableBeanFactory.SCOPE_SINGLETON,proxyMode= ScopedProxyMode.DEFAULT) //指定 spring容器生成改对像的方式(单列.原型) spring 生成代理的方式(基于interface的jdk dynamic proxy,基于cglib)
@Order(3) // 指定3spring aop 的植入顺序 ,必须基于代理的模式 才有效
@DeclarePrecedence("TestAdvice,SecondAdvice") //指定aop植入顺序 (ajc注解 需要指定使用ajc编译代码)
public class TestAdvice {
@After("execution(* com.chapa.annotation.service.*.*(..))")
public void doAfter(JoinPoint jp) {
System.out.println("TestAdvice annotation log Ending method: " + jp.getTarget().getClass().getName() + "." + jp.getSignature().getName());
}
@Around("execution(* com.chapa.annotation.service.*.*(..))")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
long time = System.currentTimeMillis();
Object retVal = pjp.proceed();
time = System.currentTimeMillis() - time;
System.out.println("TestAdvice annotation process time: " + time + " ms");
return retVal;
}
@Before("execution(* com.chapa.annotation.service.*.*(..))")
public void doBefore(JoinPoint jp) {
System.out.println("TestAdvice annotation log Begining method: " + jp.getTarget().getClass().getName() + "." + jp.getSignature().getName());
}
@AfterThrowing(throwing="ex",pointcut="execution(* com.chapa.annotation.service.*.*(..))")
public void doThrowing(JoinPoint jp, Throwable ex) {
System.out.println("TestAdvice annotation method " + jp.getTarget().getClass().getName() + "." + jp.getSignature().getName() + " throw exception");
System.out.println(ex.getMessage());
}
}
https://github.com/chen8238065/aop-study