SpringAOP
2017年9月22日大约 4 分钟
1、SpringAOP
> SpringAOP的本质就是动态代理,底层使用JDK动态代理或者CGlib动态代理,通过代理框架生成代理类,实现对目标类的增强,Spring代理是方法级别的代理,是对方法增强,
>
> 代理有四个要素:
> 1. 目标类
> 2. 额外功能(增强)
> 3. 切入点(被增强的方法)
> 4. 组装(把切入点和额外功能进行整合,就是确认哪些方法需要增强)
2、SpringAOP - 非注解实现
2.1 演示1(有接口实现)
//接口
public interface UserService {
void login(String username,String password);
}
//实现
public class UserServiceImpl implements UserService{
private String username;
@Override
public String toString() {
return "UserServiceImpl{" +
"name='" + username + '\'' +
'}';
}
public void setUsername(String username) {
this.username = username;
}
@Override
public void login(String username, String password) {
System.out.println("login: username " + username);
}
}
//增强
public class LogInterceptor implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("before advice");
//注意此处不用调用 原方法,原方法会自行调用,调用会导致原方法重复调用
// method.invoke(target,args);
}
}
//测试
public class Main {
@Test
public void test1() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) applicationContext.getBean("userService");
userService.login("zhangsan", "123456");
}
}
<bean id="userService" class="org.example.UserServiceImpl" >
<property name="username">
<value>吴彦祖</value>
</property>
</bean>
<bean id="logInterceptor" class="org.example.LogInterceptor" />
<aop:config>
<aop:pointcut id="pc" expression="execution(* *(..))"/>
<aop:advisor advice-ref="logInterceptor" pointcut-ref="pc"></aop:advisor>
</aop:config>
<!--proxy-target-class默认为false,代表默认使用jdk动态代理,true代表使用cglib代理-->
<aop:aspectj-autoproxy proxy-target-class="false"></aop:aspectj-autoproxy>
当proxy-target-class="false",可以看到使用的是jdk动态代理
当proxy-target-class="true",可以看到使用的是CGlib动态代理
2.2 演示2(无接口实现)
//原目标类
public class UserServiceImpl {
private String username;
@Override
public String toString() {
return "UserServiceImpl{" +
"name='" + username + '\'' +
'}';
}
public void setUsername(String username) {
this.username = username;
}
public void login(String username, String password) {
System.out.println("login: username " + username);
}
}
//增强类不变
//测试类
@Test
public void test1() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserServiceImpl userService = (UserServiceImpl) applicationContext.getBean("userService");
userService.login("zhangsan", "123456");
}
结果proxy-target-class的值无论是true还是false,都使用的是CGlib代理,其实也好理解,因为JDK动态代理只支持带接口实现的类增强,而CGlib还可以增强 普通的类。
3、SpringAOP注解实现
SpringAOP注解实现主要是引入了AspectJ包,AspectJ本身是一个强大的代理框架,使用acj编译器在编译器间生成代理类,是使用asm对字节码进行操作,使用SpringAOP用 注解开发时,要引入AspectJ包,其实Spring仅仅是想使用它的注解类而已,并没有真正使用它的代理功能,SpringAOP底层只用了JDK动态代理或者CGlib动态代理, 这一点很多人都不知道。
注解的本质和xml一样,不过是把解析xml变成解析注解而已。
4、 SpringAOP实现原理
4.1 SpringAOP是如何产生代理类的
结论
SpringAOP底层实现的原理是使用的BeanPostProcessor,BeanPostProcessor的核心功能就是可以对一个bean进行再加工,参考上面bean的生命周期可知BeanPostProcessor执行实际是在bean初始化前后,而SpringAOP的原理就是在BeanPostProcessor的后置方法中对bean进行修改,同时把修改后的代理对象替换原对象。
当配置了AOP后,spring容器会自动配置一个叫做AnnotationAwareAspectJAutoProxyCreator的类,它也是一个BeanPostProcessor,最终会调用后置方法
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//返回代理对象
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
//用代理对象替换原生对象
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
4.2 源码层面查看SpringAOP是使用JDK动态代理还是CGlib
//org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (!NativeDetector.inNativeImage() &&
(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}