动态代理与Spring AOP
Posted ylyzty
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态代理与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
参考文章
Spring AOP
面向切面编程,在我们的应用中,经常需要做一些事情,但是这些事情与核心业务无关,比如,要记录所有 update 方法的执行时间时间,操作人等等信息,记录到日志, 通过 Spring 的 AOP 技术,就可以在不修改 update 的代码的情况下完成该需求。
pring AOP 中的动态代理主要有两种方式,JDK 动态代理 和 CGLIB 动态代理。JDK 动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK 动态代理的核心是 InvocationHandler
接口和 Proxy
类。
如果目标类没有实现接口,那么 Spring AOP 会选择使用 CGLIB 来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB 是通过继承的方式做的动态代理,因此如果某个类被标记为 final,那么它是无法使用 CGLIB 做动态代理的。
以上是关于动态代理与Spring AOP的主要内容,如果未能解决你的问题,请参考以下文章