spring中的两种aop实现方式jdk动态代理和cglib代理
aop的概念
aop (Aspect Oriented Programming), 直译: 面向侧面编程, 通过 预编译方式 或者 动态代理 实现程序功能的统一维护的一种技术
带来的好处是: 可以对通用的业务逻辑独立, 降低通用逻辑和业务逻辑的耦合, 提高通用逻辑的重用性, 提高开发效率.
主要应用
统一日志记录, 性能统计, 统一授权, 事务处理, 统一异常处理.
jdk动态代理
如何使用
实现利用java.lang.reflect.InvocationHandler接口, 重写其invoke方法为方法批量添加统一业务.
interface UserService {
void insert(String userName);
}
class UserServiceImpl implements UserService {
public void insert(String userName) {
System.out.println("insert " + userName);
}
}
class UserServiceProxy implements InvocationHandler {
private Object target;
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before insert");
Object result = method.invoke(target, args);
System.out.println("after insert");
return result;
}
public Object getProxy(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
}
public class CglibAopDemo {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
userService.insert("zhaoxi");// 只输出 insert zhaoxi
UserServiceProxy proxy = new UserServiceProxy();
UserService userServiceProxy = (UserService) proxy.getProxy(userService);
userServiceProxy.insert("zhaoxi");
//输出: before insert
//insert zhaoxi
//after insert
}
}
动态代理的代理类生成过程
- 根据传入的ClassLoader和接口生成接口代理类的字节码
- 将自定义InvocationHandler对象作为代理类构造函数的参数, 传入代理类获取代理类的构造器
- 根据构造器来生成代理类的对象实例
jdk动态代理的注意事项
- 由于代理类继承了Proxy类, java不支持多继承, 所有只能代理接口, 不能代理类.
cglib代理
如何使用
使用CGLIB库, 实现库中MethodInterceptor 接口为方法添加统一业务
使用时需单独引入CGLIB库
class UserServiceCglib implements MethodInterceptor {
private Object target;
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before insert cglib");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("after insert cglib");
return result;
}
public Object getTarget(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
}
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
UserService userServiceProxy = (UserService) new UserServiceCglib().getTarget(userService);
userServiceProxy.insert("zhaoxi");
//输出 before insert cglib
//insert zhaoxi
//after insert cglib
}
cglib代理的注意实现
- 需要单独引入CGLIB库
- 不需要类继承接口, 但是不能将类设置为final, 因为使用继承生成代理.
Spring中的aop概念
定义
- Joinpoint : 连接点(在哪里), 在什么地方执行 增强操作, spring只支持方法级的连接点.
- Pointcut : 切点是一组连接点的集合(在哪里干的集合).
- 切点表达式介绍 execution(* fun.zhaoxi.aop….(…))
- execution() 是表达式的主体
- 括号中第一个 * 是方法的返回值, * 代表所有
- fun.zhaoxi.aop… 前面是包名的前缀, … 代表子包和孙包中的所有类
- . 代表类名和方法名, * 代表所有
- (…) 代表方法中的参数, … 代表所有
- Advice : 增强(干什么), 一般是我们的公共业务.
- Aspect : 切面(在哪儿干和干什么的集合), SpringAop就是负责操作切面并执行.
切面类型:- @Before 前置通知
- @After 后置通知
- @AfterReturning 目标方法执行结束后执行
- @AfterThrowing 目标方法异常执行
- @Around 环绕通知, 目标方法执行前后都执行
目标方法正确的执行顺序是
5 → 1 → target() → 5 → 2 → 3.
切面的示例
下面示例需要引入 spring-core, spring-context, spring-beans, spring-aop, spring-aspects 包
默认使用SDK动态代理, 如果使用CGLIB代理, 需要单独引入CGLIB库.
interface UserService {
void insert(String userName);
}
@Component
class UserServiceImpl implements UserService {
public void insert(String userName) {
System.out.println("insert " + userName);
}
}
@Component
@Aspect // 切面
class UserServiceAspect {
@Pointcut("execution(* fun.zhaoxi.aop.*.*(..))") //切点 参数为切点表达式
public void pointcut() {
}
@Before("pointcut()")
public void insertBefore() { //增强
System.out.println("spring aop insert before");
}
@After("pointcut()")
public void insertAfter() { //增强
System.out.println("spring aop insert after");
}
@Around("pointcut()")
public void insertAround(ProceedingJoinPoint point) throws Throwable { //增强
System.out.println("spring aop insert around before");
point.proceed();
System.out.println("spring aop insert around after");
}
@AfterReturning("pointcut()")
public void insertAfterReturning() { //增强
System.out.println("spring aop insert afterReturning");
}
@AfterThrowing("pointcut()")
public void insertAfterThrow() { //增强
System.out.println("spring aop insert afterThrow");
}
}
//spring-aop.xml: 放入resources文件夹
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<aop:aspectj-autoproxy/> <!-- 如果要使用cglib代理, 添加 proxy-target-class="true", 需引入 cglib 包 -->
<context:component-scan base-package="fun.zhaoxi.aop" /><!-- 包名是 service的包名 -->
</beans>
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aop.xml");
UserService userService = applicationContext.getBean(UserService.class);
userService.insert("zhaoxi");
}
//输出:
// spring aop insert around before
// spring aop insert before
// insert zhaoxi
// spring aop insert around after
// spring aop insert after
// spring aop insert afterReturning
spring的aop总结
aop是spring的核心功能之一, 要玩转spring, 肯定要深入了解.
在spring中 缓存, 错误处理, 事务, 等等功能都有aop的身影, 但是真正能发挥强大功能的是 ioc + aop.