`

Spring基础:AOP编程(1)

阅读更多
Java编程中的代理

Spring以IoC为基础,发展了另外一个底层组件,就是AOP。
AOP的含义是“面向切面的编程”,将业务无关的代码但是又和业务缠在一起的代码剥离出去。AOP是一个很复杂的概念,这里只是拿出冰山一角说明一下。

AOP的术语
1.连接点(Joinpoint):程序执行的某一个特定的位置:比如类开始初始化前,初始化后,某个方法调用的前后,方法抛出异常的时候,等等具有某一种边界性质的特定点都可以称之为“连接点”。

2.切点(Pointcut):感兴趣的点的集合,如果说连接点是数据库中存储的数据的话,那切点就是where子句查询出来的目标点了。

3.增强(Advice):有的地方按照字面的意思翻译成通知,这里使用增强更加形象,即对目标对象的功能进行增强。

4.目标对象(Target):即待增强的对象。

5.引介(Introduction):特殊的增强,可以为类添加方法和属性。

6.织入(Weaver):织入就是以某种技术,将增强和目标对象融合起来,构建新类的过程,总结起来有3种织入技术。
  • 编译期织入:需要特殊的Java编译器
  • 类装载期织入:需要特殊的类装载器
  • 动态代理织入:在运行期为目标类添加增强

Spring采用第三种代理织入,所以不需要额外的编译器。

7.代理(Proxy);织入增强之后的目标类生成的新的类。

8.切面(Advisor):由增强和切点组合形成。

现在来看一个带有横切逻辑的实例:
public class ForumServiceImpl implements ForumService {

    public void removeTopic(int topicId) {
        PerformanceMonitor.begin("com.firethewhole.maventest07.proxy.ForumService.Impl.removeTopic");
        System.out.println("模拟删除Topic记录:" + topicId);
        try {
            Thread.currentThread().sleep(20);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        PerformanceMonitor.end();
    }

    public void removeForum(int forumId) {
        PerformanceMonitor.begin("com.firethewhole.maventest07.proxy.ForumService.Impl.removeForum");
        System.out.println("模拟删除Forum记录:" + forumId);
        try {
            Thread.currentThread().sleep(20);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        PerformanceMonitor.end();
    }
}

public class PerformanceMonitor {
    private static ThreadLocal<MethodPerformance> performanceRecord = new ThreadLocal<MethodPerformance>();
    
    public static void begin(String method) {
        System.out.println("begin monitor...");
        MethodPerformance mp = new MethodPerformance(method);
        performanceRecord.set(mp);
    }
    
    public static void end() {
        System.out.println("end monitor...");
        MethodPerformance mp = performanceRecord.get();
        mp.printPerformance();
    }
}

public class MethodPerformance {
    private long begin;
    private long end;
    private String serviceMethod;

    public MethodPerformance(String serviceMethod) {
        this.serviceMethod = serviceMethod;
        this.begin = System.currentTimeMillis();
    }

    public void printPerformance() {
        end = System.currentTimeMillis();
        long elapse = end - begin;
        System.out.println(serviceMethod + "花费" + elapse + "毫秒。");
    }
}

ForumService forumService = new ForumServiceImpl();
forumService.removeForum(10);
forumService.removeTopic(1012);

输出以下信息:

begin monitor...
模拟删除Forum记录:10
end monitor...
com.firethewhole.maventest07.proxy.ForumService.Impl.removeForum花费20毫秒。
begin monitor...
模拟删除Topic记录:1012
end monitor...
com.firethewhole.maventest07.proxy.ForumService.Impl.removeTopic花费20毫秒。


这里性能代码和业务代码都在一起,将来修改业务代码的时候,性能监控代码可能也需要同时修改,显的很不清晰。

使用JDK动态代理技术
首先将性能横切代码都删除掉
public class ForumServiceAopStyleImpl implements ForumService {

    public void removeTopic(int topicId) {
        System.out.println("模拟删除Topic记录:" + topicId);
        try {
            Thread.currentThread().sleep(20);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    public void removeForum(int forumId) {
        System.out.println("模拟删除Forum记录:" + forumId);
        try {
            Thread.currentThread().sleep(20);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

public class PerformanceHandler implements InvocationHandler {
    private Object target;
    
    public PerformanceHandler(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;
    }
}

在InvocationHandler中创建代理类,在调用ForumServiceAopStyleImpl的方法的时候,可以只能增强逻辑,先执行性能监控代码。接下来看下如何使用
// 加入动态代理的方法
ForumService target = new ForumServiceAopStyleImpl();
PerformanceHandler handler = new PerformanceHandler(target);
ForumService proxy = (ForumService) Proxy.newProxyInstance(
        target.getClass().getClassLoader()
        , target.getClass().getInterfaces()
        , handler);

proxy.removeForum(10);
proxy.removeTopic(1012);


输出结果:

begin monitor...
模拟删除Forum记录:10
end monitor...
com.firethewhole.maventest07.proxy.ForumServiceAopStyleImpl.removeForum花费47毫秒。
begin monitor...
模拟删除Topic记录:1012
end monitor...
com.firethewhole.maventest07.proxy.ForumServiceAopStyleImpl.removeTopic花费31毫秒。


使用CGLib创建代理
使用JDK自带的InvocationHandler有一个局限性,就是只能针对接口进行代理,如果想直接针对类进行代理的话,就只能使用CGLib了。首先需要加入maven依赖。
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>


public class CglibProxy implements MethodInterceptor {
    private Enhancer enhancer = new Enhancer();
    
    public Object getProxy(Class clazz) {
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }
    public Object intercept(Object obj, Method method, Object[] args,
            MethodProxy proxy) throws Throwable {
        PerformanceMonitor.begin(obj.getClass().getName() + "." + method.getName());
        Object result = proxy.invokeSuper(obj, args);
        PerformanceMonitor.end();
        return result;
    }
}


基于CGLib创建代理类。
// 使用Cglib代理
CglibProxy proxyCglib = new CglibProxy();
ForumServiceAopStyleImpl forumServiceImpl = (ForumServiceAopStyleImpl) proxyCglib.getProxy(ForumServiceAopStyleImpl.class);
forumServiceImpl.removeForum(10);
forumServiceImpl.removeTopic(1023);


输出结果:

begin monitor...
模拟删除Forum记录:10
end monitor...
com.firethewhole.maventest07.proxy.ForumServiceAopStyleImpl$$EnhancerByCGLIB$$58fbcf09.removeForum花费125毫秒。
begin monitor...
模拟删除Topic记录:1023
end monitor...
com.firethewhole.maventest07.proxy.ForumServiceAopStyleImpl$$EnhancerByCGLIB$$58fbcf09.removeTopic花费32毫秒。


可以看出CGLib创建出来的类的名字还是比较奇怪的,另外还有一点CGLib在创建对象的时候没有Invocation快,不适合需要反复创建对象的场合,在IoC容器启动的时候创建较为合适
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics