前言
面试中经常会被问到Spring AOP的原理,相信大家都是条件反射的想到JDK动态代理和CGLib动态代理,本文将介绍这两个代理的区别及实现一个Spring AOP实例。
1、为什么会有AOP
AOP的全称是Aspect Oriented Programming,翻译成中文是面向切面编程。当我们在开发日志、权限验证、事务等功能时,如果不使用AOP,只能在每个对象里引用公共行为,这样做不便于维护,而且有大量重复代码,AOP的出现就是解决这些问题。
2、Spring AOP的原理
Spring Aop中使用的代理有两种方式,一种是Jdk的动态代理,另一种是基于CGLIB实现的代理,当我们的bean对象实现了某一接口后,Spring默认将采用Jdk动态代理,当我们的bean对象没有实现接口时,默认将采用CGLIB代理,Spring也支持我们在bean对象实现了接口时也强制的使用CGLIB代理。Spring的Bean容器在初始化bean对象的时候就会判断对应的bean是否需要进行切面编程,即是否需要对其进行代理,如果需要,则初始化的时候就会把它初始化为一个代理对象。
3、Jdk动态代理
- JDK动态代理基于拦截器和反射来实现。
- JDK动态代理是不需要第三方库支持的,只需要JDK环境就可以进行代理。
使用的条件:
- 必须实现InvocationHandler接口;
- 使用Proxy.newProxyInstance产生代理对象;
- 被代理的对象必须要实现接口;
4、CGLib动态代理
- CGLib包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。
- CGLib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,并覆盖其中方法实现增强。
使用条件
- CGLib是针对类实现代理,采用的是继承,类或方法不要声明成final;
- 必须实现MethodInterceptor接口;
- 需要使用CGLib包;
5、spring aop 实践
- 编写测试类
//user接口
public interface UserDao {
//登录
void login();
}
//user实现类,测试jdk代理
public class MyUserDao implements UserDao {
@Override
public void login() {
System.out.println("接口用户正常登录");
}
}
//无接口实现,测试cglib
public class CglibDao {
public void cglibLogin(){
System.out.println("cglib用户正常登录");
}
}
//自定义切面类
public class MyAspect {
public void begin() {
System.out.println("[前置通知] 开启事务..");
}
public void commit() {
System.out.println("[后置通知] 提交事务..");
}
public void after() {
System.out.println("[返回后通知]");
}
public void afterThrowing() {
System.out.println("[异常通知]");
}
public void arroud(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("[环绕前:]");
pjp.proceed(); // 执行目标方法
System.out.println("[环绕后:]");
}
}
//测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = {"classpath:applicationContext.xml"})
public class AopTest {
@Resource
UserDao userDao;
@Resource
CglibDao cglibDao;
@Test
public void testUser(){
userDao.login();
}
@Test
public void testCglib(){
cglibDao.cglibLogin();
}
}
配置文件applicationContext.xml
<?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"
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">
<!-- dao实例加入容器 -->
<bean id="myUserDao" class="aop.MyUserDao"></bean>
<!-- dao实例加入容器 -->
<bean id="cglibDao" class="aop.CglibDao"></bean>
<!-- 实例化切面类 -->
<bean id="myAspect" class="aop.MyAspect"></bean>
<!-- Aop相关配置 -->
<aop:config>
<!-- 切入点表达式定义 -->
<aop:pointcut expression="execution(* aop.*Dao.*(..))" id="transactionPointcut"/>
<!-- 切面配置 -->
<aop:aspect ref="myAspect">
<!-- 【环绕通知】 -->
<aop:around method="arroud" pointcut-ref="transactionPointcut"/>
<!-- 【前置通知】 在目标方法之前执行 -->
<aop:before method="begin" pointcut-ref="transactionPointcut" />
<!-- 【后置通知】 -->
<aop:after method="commit" pointcut-ref="transactionPointcut"/>
<!-- 【返回后通知】 -->
<aop:after-returning method="after" pointcut-ref="transactionPointcut"/>
<!-- 异常通知 -->
<aop:after-throwing method="afterThrowing" pointcut-ref="transactionPointcut"/>
</aop:aspect>
</aop:config>
</beans>
输出结果:
结束语
本篇详细的介绍了Jdk动态代理和Cglib代理,以及手写了一个spring aop的实例,熟悉spring aop的配置。