动态代理与Spring AOP
JDK 动态代理
目标类
JDK动态代理基于接口创建代理,所以目标类必须至少实现一个接口。
public interface SmsService {
String send(String message);
void print();
}
public class SmsServiceImpl implements SmsService{
@Override
public String send(String message) {
// TODO Auto-generated method stub
System.out.println("send message:" + message);
return message;
}
@Override
public void print() {
// TODO Auto-generated method stub
System.out.println("execute print method!");
}
}
代理类
JDK动态代理类必须实现
InvocationHandler
接口,重写自己的invoke()
方法
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class JDKProxy implements InvocationHandler{
private final Object target; // 被代理对象
public JDKProxy(Object target) {
this.target = target;
}
/**
* 当调用被代理对象的方法时, 会自动跳转到代理对象的invoke()方法
* 可以通过 method.getName() 指定被代理的方法
* 默认会代理所有方法
*
* @param proxy 动态生成的代理对象
* @param method 实际调用的方法
* @param args 实际调用方法的入参
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
Object result = null;
if (method.getName().equals("send")) {
System.out.println("Before: " + method.getName());
result = method.invoke(target, args);
System.out.println("After: " + method.getName());
}
else {
result = method.invoke(target, args);
}
return result;
}
}
Proxy动态创建代理类
利用反射机制创建实现被代理对象接口的代理类,在调用具体方法时,调用
InvocationHandler
处理。
import java.lang.reflect.Proxy;
public class JDKProxyFactory {
public static Object getJDKProxy(Object target) {
// 动态创建代理对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new JDKProxy(target));
}
}
测试JDK动态代理
public class JDKProxyTest {
public static void main(String[] args) {
SmsService smsService = (SmsService) JDKProxyFactory.getJDKProxy(new SmsServiceImpl());
smsService.send("message");
smsService.print();
}
}
Before: send
send message:message
After: send
execute print method!
sned()
方法被代理增强print()
方法未被代理
Cglib 动态代理
目标类
Cglib动态代理基于子类创建代理,所以对于未实现接口的类可以考虑使用Cglib实现代理。但是如果目标类被
final
关键字修饰则代理无法创建。
public class SmsServiceClass {
public String send(String message) {
// TODO Auto-generated method stub
System.out.println("send message:" + message);
return message;
}
public void print() {
// TODO Auto-generated method stub
System.out.println("execute print method!");
}
}
代理类
Cglib动态代理类则必须实现
MethodInterceptor
接口,并重写自己的intercept()
方法。
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxy implements MethodInterceptor {
/**
* @param obj 动态生成的代理对象
* @param method 实际调用的方法
* @param args 实际调用方法的入参
* @param methodProxy Method代理方法
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object result = null;
// 增强 print 方法
if (method.getName().equals("print")) {
System.out.println("Before: " + method.getName());
// 执行被代理类的逻辑
result = methodProxy.invokeSuper(obj, args);
System.out.println("After: " + method.getName());
}
else {
result = methodProxy.invokeSuper(obj, args);
}
return result;
}
}
Enhancer动态创建代理类
利用
org.objectweb.asm
软件包,加载代理对象的class
文件,通过修改字节码生成子类实现代理。
import net.sf.cglib.proxy.Enhancer;
public class CglibProxyFactory {
public static Object getCglibProxy(Object target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(new CglibProxy());
return enhancer.create();
}
}
setSupperClass()方法源码
public void setSuperclass(Class superclass) {
if (superclass != null && superclass.isInterface()) {
this.setInterfaces(new Class[]{superclass});
} else if (superclass != null && superclass.equals(Object.class)) {
this.superclass = null;
} else {
this.superclass = superclass;
}
}
所以 Cglib 即可以代理未实现接口的类(supperClass),也可以代理实现了接口的类(setInterfaces)。
测试Cglib动态代理
public class CglibTest {
public static void main(String[] args) {
// 获取代理类
SmsServiceClass smsServiceClass = (SmsServiceClass) CglibProxyFactory.getCglibProxy(new SmsServiceClass());
smsServiceClass.send("message");
smsServiceClass.print();
}
}
send message:message
Before: print
execute print method!
After: print
send()
方法未被代理print()
被代理增强
小结
代理类型 | 实现机制 | 创建方式 | 应用场景 |
---|---|---|---|
JDK动态代理 | 代理类和目标类都实现了同样的接口,代理类委托 InvocationHandler 去调用目标类的原始方法 |
反射 | 目标类实现了接口 |
Cglib动态代理 | 代理类继承并重写了目标类的方法,通过回调函数 MethodInterceptor 调用父类方法执行原始方法 |
ASM | 非final 类、非final 方法 |
Spring AOP
AOP(Aspect Orient Programming): 面向切面编程,即可以在不修改原有代码的情况下给系统添加额外的功能。AOP可以拦截指定的业务方法,并对其进行增强,而且无需入侵到具体业务代码中,使得业务代码与增强处理逻辑分离。
AOP主要应用体现在:
- 事务处理
- 日志管理
- 权限控制
- 异常处理
Spring AOP 简单概念理解
Advice
: 通知,描述切面何时执行以及如何增强处理join point
: 连接点,描述可以被动态代理拦截的目标类方法PointCut
: 切点,真正被拦截的连接点Aspect
: 切面,即通知和切点的结合Weaving
: 织入,描述增强逻辑应用到目标类上,生成代理对象的过程
AspectJ基于注解实现切面
创建接口及其实现类
public interface SmsService {
String send(String message);
}
@Component
public class SmsServiceImpl implements SmsService{
@Override
public String send(String message) {
// TODO Auto-generated method stub
System.out.println("send message:" + message);
return message;
}
}
定义切面类
@Aspect
@Component
public class SmsServiceAspectJ {
// 声明切点表达式
@Pointcut("execution(String com.lzy.aopdemo.test.SmsService.send(String))")
public void point(){}
@Before("point()")
public void beforeMethod() {
System.out.println("Execute before!");
}
@After("point()")
public void afterMethod() {
System.out.println("Execute after!");
}
@AfterReturning("point()")
public void afterReturningMethod() {
System.out.println("Execute after returning!");
}
@Around("point()")
public void aroundMethod(ProceedingJoinPoint pjp) {
try {
System.out.println("Around before");
pjp.proceed();
System.out.println("Around after");
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}
切点表达式 execution(* com.lzy.aopdemo.test.SmsService.send(..))
- execution: 标识方法执行时触发
- *: 返回任意类型(不关心返回值类型)
- com.lzy.aopdemo.test.SmsService: 方法所属类或接口
- send: 特定方法
- ..: 使用任意参数(不关心入参)
Spring AOP通知类型
通知类型 | 描述 |
---|---|
@Before | 在目标方法调用之前执行 |
@After | 在目标方法调用之后执行 |
@AfterReturning | 在目标方法返回后调用 |
@AfterThrowing | 在目标方法抛出异常后调用 |
@Around | 将目标方法封装起来 |
注意@Around的执行顺序
@Around \(\rightarrow\) @Before \(\rightarrow\) @Around \(\rightarrow\) @After \(\rightarrow\) @AfterReturning
配置启用AOP
proxyTargetClass
- true: 使用Cglib动态代理
- false: 使用JDK动态代理
@Configuration
@ComponentScan(basePackageClasses = {com.lzy.aopdemo.test.SmsService.class})
@EnableAspectJAutoProxy(proxyTargetClass = false)
public class AOPConfiguration {}
参考文章