Spring基础:AOP编程(1)

[size=x-large][b]Java编程中的代理[/b][/size]

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

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

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

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

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

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

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

输出以下信息:
[color=blue]
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毫秒。
[/color]

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

使用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);


输出结果:
[color=blue]
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毫秒。
[/color]

使用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);


输出结果:
[color=blue]
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毫秒。
[/color]

可以看出CGLib创建出来的类的名字还是比较奇怪的,另外还有一点[color=red]CGLib在创建对象的时候没有Invocation快,不适合需要反复创建对象的场合,在IoC容器启动的时候创建较为合适[/color]。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值