一场雨

spring的aop部分

aop中的几个基本概念:

1.连接点:由两个信息确定,一个是方法表示的程序的执行点,另一个是用相对点表示的方位。spring使用切点对执行点进行定位,而方位则是在增强的类型中定义的。JoinPoint

2.切点:每一个程序类都有多个连接点,如一个拥有两个方法的类,这两个方法都是连接点,aop通过切点来定位到特定的连接点,准确的说是定位到特定的方法上。

3.增强:除了提供一段程序代码之外,还提供了执行点的方位信息。结合执行点的方位信息和执行点信息,就可以找到特定的连接点。

所以,只有结合切点和增强两者,才能确定特定的连接点并实施增强逻辑。织入是将增强添加到目标类具体的连接点上的过程。

Spring AOP的核心就是动态代理技术:jdk动态代理,和CGLib动态代理。

没有使用@AspectJ的spring aop的配置是比较麻烦的。

//当使用jdk的动态代理时:(只能为接口创建代理实例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class PerformaceHandler implements InvocationHandler {
private Object target;
public PerformaceHandler(Object target){
​​ this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
PerformanceMonitor.begin(target.getClass().getName()+"."+ method.getName());
Object obj = method.invoke(target, args);
PerformanceMonitor.end();
return obj;
}
}

test:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
​ForumService target = new ForumServiceImpl();
PerformaceHandler handler = new PerformaceHandler(target);
ForumService proxy = (ForumService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler);
​​proxy.removeForum(10);
​​proxy.removeTopic(1012);

xml中也需要配置。

切面:一般切面(仅包含Advice),切点的切面(Advice,pointCut),引介切面

PointcutAdvisor有6个实现。

Advisor需要配合一个增强才会起作用,这边将一个前置增强装配到一个切面中去了,之后,配置切面就可以了。

​<!-- 普通方法名匹配切面 -->
<bean id="waiterTarget" class="com.baobaotao.advisor.Waiter" />

​<bean id="sellerTarget" class="com.baobaotao.advisor.Seller" />

<bean id="greetingAdvice" class="com.baobaotao.advisor.GreetingBeforeAdvice" />

<bean id="greetingAdvisor" class="com.baobaotao.advisor.GreetingAdvisor"
​​p:advice-ref="greetingAdvice" />

<bean id="parent" abstract="true"
​​class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="greetingAdvisor" p:proxyTargetClass="true" />

​<bean id="waiter" parent="parent" p:target-ref="waiterTarget" />

​<bean id="seller" parent="parent" p:target-ref="sellerTarget" />

以上是静态的切面配置

当然还有动态切面

在Spring中,不管是静态切面还是动态切面,使用的都是动态代理技术,所谓的静态切面是指生成代理对象时就确定好了,增强是否需要织入到目标类的连接点上,而动态切面是指在运行期间根据方法的入参来决定是否需要织入到目标类的连接点上。

注解的方式进行处理@AspcetJ。

有了注解之后,使用就方便很多了,

@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.METHOD)
@Inherited
public @interface Cacheable {

Class<? extends Entity> group();

CacheOperation cacheOperation() default CacheOperation.NOP;

Class<? extends Entity>[] flushGroup() default {};

}

@Around("@annotation(cacheable)")
public Object cacheable(final ProceedingJoinPoint pjp,
final Cacheable cacheable)throws Throwable {
    if(cacheable.cacheOperation() == CacheOperation.NOP) {
        return pjp.proceed();
    }
    String group = cacheable.group().getName(),
            cacheKey = getCacheKey(pjp);
    Cache cache = getCache(group);
    Object result = null;
    if(cacheable.cacheOperation() == CacheOperation.CACHING) {
        Element element = cache.get(cacheKey);
        if(element == null) {
            result = pjp.proceed();
            if(result == null || result instanceof Serializable) {
                element = new Element(cacheKey, (Serializable)result);
                LOG.info("正在缓存{0}对象。", cacheKey);
                cache.put(element);
            }else {
                LOG.warn("{0}指定方法返回对象{1}不能被序列化,不能缓存。", cacheKey, result);
            }
        }else {
            LOG.info("从缓存中获取{0}对象。", cacheKey);
            result = element.getObjectValue();
        }
    }else if(cacheable.cacheOperation() == CacheOperation.FLUSH_CACHE) {
        result = pjp.proceed();
        for(Class<? extends Entity> clazz : cacheable.flushGroup()) {
            String groupName = clazz.getName();
            if(!groupName.equals(group)) {
                LOG.info("删除缓存组{0}所有的缓存对象。", groupName);
                getCache(group).removeAll();
            }

        }
        LOG.info("删除缓存组{0}所有的缓存对象。", group);
        cache.removeAll();
    }
    return result;
}

以上,主要是aop的部分应用。