`

spring日记(四):AOP基础

阅读更多

AOP的实现者:AspectJ、JBoss AOP、Spring AOP,其中AspectJ提供语言级别的AOP实现,有一个专门的编译器生成遵守Java字节编码规范的Class文件。在Spring中,可以无缝集成Spring AOP、IoC、AspectJ。

Spring AOP使用了两种代理机制:一种是基于JDK的动态代理,另一种是基于CGLib的动态代理,之所以需要两种代理机制,很大程度上是因为JDK本身只提供接口的代理,而不支持类的代理。

》JDK动态代理实例:

package com.springzoo.proxy;
 
public interface ForumService {
    void removeTopic(int topicId);
    void removeForum(int forumId);
}

 

package com.springzoo.proxy;
 
public class ForumServiceImpl implements ForumService {
 
    public void removeTopic(int topicId) {
        System.out.println("模拟删除Topic记录:"+topicId);
        try {
            Thread.currentThread().sleep(20);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
 
    public void removeForum(int forumId) {
        System.out.println("模拟删除Forum记录:"+forumId);
        try {
            Thread.currentThread().sleep(40);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }       
    }
}

 

package com.springzoo.proxy;
 
public class PerformanceMonitor {
    private static ThreadLocal<MethodPerformace> performaceRecord = new ThreadLocal<MethodPerformace>();
    public static void begin(String method) {
        System.out.println("begin monitor...");
        MethodPerformace mp = performaceRecord.get();
        if(mp == null){
            mp = new MethodPerformace(method);
            performaceRecord.set(mp);
        }else{
            mp.reset(method);   
        }
    }
    public static void end() {
        System.out.println("end monitor...");
        MethodPerformace mp = performaceRecord.get();
        mp.printPerformace();
    }
}

 

package com.springzoo.proxy;
 
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
 
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;
    }
}

 

 

 

最后测试下:

package com.springzoo.proxy;
 
public class TestForumService {
    public static void main(String[] args) {
        // 使用JDK动态代理
//      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);
         
        //使用CGLib动态代理
        CglibProxy proxy = new CglibProxy();
        ForumService forumService = (ForumService)proxy.getProxy(ForumServiceImpl.class);
        forumService.removeForum(10);
        forumService.removeTopic(1023);
         
    }
}

 

以上的手动编写AOP逻辑和植入非常的不方便,而spring AOP就是要解决这个问题。spring AOP通过Pointcut指定在哪些类的哪些方法上织入横切逻辑,通过Advice描述横切逻辑和方法的织入方位(方法前、方法后、方法前后,跑出异常时)。此外,spring通过Advisor切面将Pointcut和Advice两者组装起来。有了Advisor的信息,spring就可以利用JDK或者CGLib的动态代理技术采用统一的方式为目标Bean创建织入切面的代理对象了。

动态代理性能:CGLib的代理对象性能是JDK的10倍,但是创建时间是JDK的8倍,因此对于singleton的对象适合用CGLib,而其他的情况适合用JDK动态代理。注意,CGLib由于采用动态创建子类方式生产代理对象,所以不能对目标类中的final方法进行代理。

》Introduction,引介增强

引介增强可以为目标类创建新的方法和属性,所以其连接点是类级别,通过引介增强我们可以为目标类添加一个接口的实现,通过引介增强我们可以为目标类创建实现某接口的代理。继承DelegatingIntroductionInterceptor即可,覆盖其中的invoke方法。

先定义一个接口类:

package com.springzoo.introduce;
 
public interface Monitorable {
   void setMonitorActive(boolean active);
}

然后自定义Introduction:

package com.springzoo.introduce;
 
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
 
public class ControllablePerformaceMonitor extends DelegatingIntroductionInterceptor
        implements Monitorable{
    private ThreadLocal<Boolean> monitorStatusMap = new ThreadLocal<Boolean>();
 
    public void setMonitorActive(boolean active) {
        monitorStatusMap.set(active);
    }
 
    public Object invoke(MethodInvocation mi) throws Throwable {
        Object obj = null;
        if (monitorStatusMap.get() != null && monitorStatusMap.get()) {
            PerformanceMonitor.begin(mi.getClass().getName() + "."
                    + mi.getMethod().getName());
            obj = super.invoke(mi);
            PerformanceMonitor.end();
        } else {
            obj = super.invoke(mi);
        }
        return obj;
    }
}
 

接下来在spring中配置Introduction:

<?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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
 
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
 
    <bean id="pmonitor" class="com.springzoo.introduce.ControllablePerformaceMonitor" />
    <bean id="forumServiceTarget" class="com.springzoo.introduce.ForumService" />
    <bean id="forumService" class="org.springframework.aop.framework.ProxyFactoryBean"
        p:interfaces="com.springzoo.introduce.Monitorable"
        p:target-ref="forumServiceTarget"
        p:interceptorNames="pmonitor"
        p:proxyTargetClass="true" />
</beans>

注意:必须指定引介增强所实现的接口,p:inaterfaces,其次由于只能通过为目标类创建子类方式生产引介增强代理,所以必须将proxyTargetClass设为true。还有就是我们没有对ControllerPerformanceMonitor进行线程安全的特殊处理,就必须将singleton属性设为true让ProxyFactoryBean产生prototype的代理,这样会带来严重的性能问题,因为CGLib创建动态代理的性能很低,所以加了个TreadLocal,那么就可以了。

》创建切面Advisor

我们再次给出spring AOP如何定位连接点:

增强Advice提供了连接点方位信息:如织入方法前、后等,而切点进一步描述织入到哪些类的哪些方法上。

spring通过org.springframework.aop.Pointcut接口描述切点,Pointcut由ClassFilter和MethodMatcher构成。

 

本人博客已搬家,新地址为:http://yidao620c.github.io/

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics