1.AOP概念
AOP采用代理的方式实现
1.1 连接点
程序执行的某个特定位置:如类的开始初始化前、类初始化后、类某个方法调用前、调用后、方法抛出异常后。
1.2 切点
每个程序类有多个连接点,如拥有两个方法的类,如何定位到感兴趣的连接点,AOP通过切点定位特定连接点。
1.3 增强
增强是织入目标连接点上的一段程序代码。
2. 代理及spring AOP
2.1 JDK 动态代理实例
a.接口
package com.baobaotao.proxy;
public interface ForumService {
public void removeTopic(int topicId);
public void removeForum(int forumId);
}
b.实现接口
package com.baobaotao.proxy;
public class ForumServiceImpl implements ForumService {
public void removeTopic(int topicId) {
System.out.println("模拟删除Topic记录:"+topicId);
try {
Thread.currentThread().sleep(20);
} catch(Exception exp) {
throw new RuntimeException(exp);
}
}
public void removeForum(int forumId) {
System.out.println("模拟删除Forum记录:"+forumId);
try {
Thread.currentThread().sleep(40);
} catch(Exception exp) {
throw new RuntimeException(exp);
}
}
}
c1. 记录性能监视信息类
package com.baobaotao.proxy;
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() - begin;
long elapse = end - begin;
System.out.println(serviceMethod+"花费"+elapse+"毫秒");
}
}
c2.性能监视实现类
package com.baobaotao.proxy;
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();
}
}
d. 将性能监视代码与逻辑代码结合
package com.baobaotao.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class PerformanceHandler implements InvocationHandler {
//target是目标业务类
private Object target;
public PerformanceHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy,Method method,Object[] args) {
PerformanceMonitor.begin(target.getClass().getName()+"."+method.getName());
//通过反射方式调用业务类的目标方法
Object obj = method.invoke(target,args);
PerformanceMonitor.end();
return obj;
}
}
e.创建代理实例
package com.baobaotao.proxy;
import java.lang.reflect.Proxy;
public class TestForumService {
//希望被代理的目标业务类
ForumService target = new ForumServiceImpl();
//将目标业务类和横切代码编织在一起
PerformanceHandler handler = new PerformanceHandler(target);
//创建代理实例
ForumService proxy = (ForumService)Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),handler);
//调用代理实例
proxy.removeForum(10);
proxy.removeTopic(11);
}
2.2 CGLib动态代理
JDK创建代理有个限制:它创建的代理实例一定要基于接口,CGLib没有这方面的限制
a. CGLib将性能监视代码与逻辑代码结合
package com.baobaotao.proxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglig.proxy.MethodProxy;
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;
}
}
b.测试代理对象的方法
package com.baobaotao.proxy;
import java.lang.reflect.Proxy;
public class TestForumService {
public static void main(String args[]) {
CglibProxy proxy = new CglibProxy();
//通过动态生成子类的方式生成代理类
ForumServiceImpl forumService = (ForumServiceImpl)proxy.getProxy(ForumServiceImpl.class);
forumService.removeForum(10);
forumService.removeTopic(11);
}
}
2.3 Spring中的AOP
Spring AOP通过Pointcut(切点)指定在哪些类的哪些方法上织入横切逻辑,通过Advice(增强)描述横切逻辑和方法的具体织入点(方法前、方法后、方法的两端等),Spring通过切面Advisor将Advice和Pointcut两者组装起来。Spring有各种增强、切点、切面,本文只介绍个小例子。
a1.业务类Waiter
package com.baobaotao.advisor;
public class Waiter {
public void greetTo(String name) {
System.out.println("greet to "+name+"...");
}
public void serveTo(String name) {
System.out.println("serving "+name+"...");
}
}
a2.业务类Seller
package com.baobaotao.advisor;
public class Seller {
public void greetTo(String name) {
System.out.println("greet to "+name+"...");
}
}
b.方法的前置增强类
package com.baobaotao.advisor;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class GreetingBeforeAdvice implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object obj)
throws Throwable {
String clientName = (String) args[0];
System.out.println("How are you! Mr."+clientName+".");
}
}
c.切点
package com.baobaotao.advisor;
import java.lang.reflect.Method;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
public class GreetingAdvisor extends StaticMethodMatcherPointcutAdvisor {
public boolean matches(Method method, Class clazz) {
//切点方法匹配规则,方法名为greetTo
return "greetTo".equals(method.getName());
}
public ClassFilter getClassFilter() {
//切点类型匹配规则,为Waiter类或者子类
return new ClassFilter() {
public boolean matches(Class clazz) {
return Waiter.class.isAssignableFrom(clazz);
}
};
}
}
d.在spring核心配置文件中进行配置
<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定义公共的代理信息--->
<bean id="parent" abstract="true" class="org.springframework.aop.framework.ProxyFactoryBean" p:interceptorNames="greetingAdvisor" p:proxyTargetClass="true"/>
<!---waiter代理--->
<bean id="waiter" parent="parent" p:target-ref="waiterTarget"/>
<!---seller代理--->
<bean id="seller" parent="parent" p:target-ref="sellerTarget"/>