之前介绍了动态代理的两种方式
jdk动态代理:https://blog.csdn.net/qq_24516549/article/details/89085881
cglib动态代理:https://blog.csdn.net/qq_24516549/article/details/89167591
现在开始研究在spring中的实现有什么不同
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ ElementType.METHOD,ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyTest {
}
import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
@Aspect
@Configuration
public class MyTestAspect {
Logger log = LoggerFactory.getLogger(MyTestAspect.class);
@Pointcut("@annotation(cn.test.modules.aop.aoptest.annotation.MyTest)")
public void pointCut() {
}
@Before("pointCut()")
public void doBefore(JoinPoint point) throws ClassNotFoundException, NoSuchMethodException, SecurityException {
log.info("before:" + getMethod(point).getName());
}
private Method getMethod(JoinPoint point) throws NoSuchMethodException, SecurityException {
MethodSignature methodSignature = (MethodSignature) point.getSignature();
Class<?> targetClass = point.getTarget().getClass();
return targetClass.getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
}
}
import org.springframework.stereotype.Service;
import cn.test.modules.aop.aoptest.annotation.MyTest;
@Service
public class AService {
@MyTest
public void test() {
}
@MyTest
public void hello() {
test();
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import cn.test.modules.aop.aoptest.classService.AService;
import io.swagger.annotations.ApiOperation;
import cn.test.common.utils.R;
/**
*
*
* @author zhanchong
* @email [email protected]
* @date 2019-04-10 14:24:36
*/
@RestController
@RequestMapping("aop/test")
public class TestController {
@Autowired
private AService a;
/**
* 列表
*/
@RequestMapping("/test")
@ApiOperation(value = "测试aop", httpMethod = "GET")
public R list(int type) {
switch (type) {
case 1:
a.hello();
break;
case 2:
a.test();
break;
case 3:
AService A = new AService();
A.hello();
break;
default:
break;
}
return R.ok();
}
}
以上注解和切面类期望达到调用注上MyTest注解的方法之前打印一行log,依次调用123三种情况查看结果
输出:
2019-04-11 15:18:52.660 INFO 13244 --- [io-8080-exec-25] c.t.m.aop.aoptest.aspect.MyTestAspect : before:hello
只输出了hello()方法上的日志,可见自调用的test()方法未被增强
输出:
2019-04-11 15:20:10.338 INFO 13244 --- [io-8080-exec-24] c.t.m.aop.aoptest.aspect.MyTestAspect : before:test
正常输出了test()方法上的日志
无输出,未发生任何增强
修改service代码如下:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopContext;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Service;
import cn.test.modules.aop.aoptest.annotation.MyTest;
@Service
@EnableAspectJAutoProxy(exposeProxy = true)
public class AService {
Logger log = LoggerFactory.getLogger(AService.class);
@MyTest
public void test() {
Object o = AopContext.currentProxy();
log.info("test start");
log.info(AopUtils.isAopProxy(o) + "");
log.info(AopUtils.isJdkDynamicProxy(o) + "");
log.info(AopUtils.isCglibProxy(o) + "");
log.info("test end");
}
@MyTest
public void hello() {
Object o = AopContext.currentProxy();
log.info("hello start");
log.info(AopUtils.isAopProxy(o) + "");
log.info(AopUtils.isJdkDynamicProxy(o) + "");
log.info(AopUtils.isCglibProxy(o) + "");
log.info("hello end");
test();
}
}
依次调用123三种情况查看结果
输出:
2019-04-11 15:26:46.620 INFO 13244 --- [nio-8080-exec-3] c.t.m.aop.aoptest.aspect.MyTestAspect : before:hello
2019-04-11 15:26:46.622 INFO 13244 --- [nio-8080-exec-3] c.t.m.aop.aoptest.classService.AService : hello start
2019-04-11 15:26:46.622 INFO 13244 --- [nio-8080-exec-3] c.t.m.aop.aoptest.classService.AService : true
2019-04-11 15:26:46.622 INFO 13244 --- [nio-8080-exec-3] c.t.m.aop.aoptest.classService.AService : false
2019-04-11 15:26:46.622 INFO 13244 --- [nio-8080-exec-3] c.t.m.aop.aoptest.classService.AService : true
2019-04-11 15:26:46.622 INFO 13244 --- [nio-8080-exec-3] c.t.m.aop.aoptest.classService.AService : hello end
2019-04-11 15:26:46.622 INFO 13244 --- [nio-8080-exec-3] c.t.m.aop.aoptest.classService.AService : test start
2019-04-11 15:26:46.624 INFO 13244 --- [nio-8080-exec-3] c.t.m.aop.aoptest.classService.AService : true
2019-04-11 15:26:46.624 INFO 13244 --- [nio-8080-exec-3] c.t.m.aop.aoptest.classService.AService : false
2019-04-11 15:26:46.624 INFO 13244 --- [nio-8080-exec-3] c.t.m.aop.aoptest.classService.AService : true
2019-04-11 15:26:46.624 INFO 13244 --- [nio-8080-exec-3] c.t.m.aop.aoptest.classService.AService : test end
可见hello被增强,test未被增强;
当前调用的对象是被spring管理的动态代理对象,动态代理方式是cglib
输出:
2019-04-11 15:20:10.338 INFO 13244 --- [io-8080-exec-24] c.t.m.aop.aoptest.aspect.MyTestAspect : before:test2019-04-11 15:27:06.379 INFO 13244 --- [nio-8080-exec-6] c.t.m.aop.aoptest.aspect.MyTestAspect : before:test
2019-04-11 15:27:06.380 INFO 13244 --- [nio-8080-exec-6] c.t.m.aop.aoptest.classService.AService : test start
2019-04-11 15:27:06.380 INFO 13244 --- [nio-8080-exec-6] c.t.m.aop.aoptest.classService.AService : true
2019-04-11 15:27:06.380 INFO 13244 --- [nio-8080-exec-6] c.t.m.aop.aoptest.classService.AService : false
2019-04-11 15:27:06.380 INFO 13244 --- [nio-8080-exec-6] c.t.m.aop.aoptest.classService.AService : true
2019-04-11 15:27:06.380 INFO 13244 --- [nio-8080-exec-6] c.t.m.aop.aoptest.classService.AService : test end
可见test被增强
当前调用的对象是被spring管理的动态代理对象,动态代理方式是cglib
2019-04-11 15:27:33.504 ERROR 13244 --- [nio-8080-exec-7] c.t.common.exception.RRExceptionHandler : Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.
报错,调用自定义对象并非被spring管理的对象
修改service代码如下:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopContext;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Service;
import cn.test.modules.aop.aoptest.annotation.MyTest;
@Service
@EnableAspectJAutoProxy(exposeProxy = true)
public class AService {
Logger log = LoggerFactory.getLogger(AService.class);
@MyTest
public void test() {
log.info("test start");
log.info(AopContext.currentProxy().getClass().getName());
log.info("test end");
}
@MyTest
public void hello() {
log.info("hello start");
log.info(AopContext.currentProxy().getClass().getName());
log.info("hello end");
((AService) AopContext.currentProxy()).test();
}
}
查看1的结果
输出:
2019-04-11 15:33:31.397 INFO 13244 --- [nio-8080-exec-1] c.t.m.aop.aoptest.aspect.MyTestAspect : before:hello
2019-04-11 15:33:31.400 INFO 13244 --- [nio-8080-exec-1] c.t.m.aop.aoptest.classService.AService : hello start
2019-04-11 15:33:31.400 INFO 13244 --- [nio-8080-exec-1] c.t.m.aop.aoptest.classService.AService : cn.test.modules.aop.aoptest.classService.AService$$EnhancerBySpringCGLIB$$2641a8af
2019-04-11 15:33:31.400 INFO 13244 --- [nio-8080-exec-1] c.t.m.aop.aoptest.classService.AService : hello end
2019-04-11 15:33:31.400 INFO 13244 --- [nio-8080-exec-1] c.t.m.aop.aoptest.aspect.MyTestAspect : before:test
2019-04-11 15:33:31.400 INFO 13244 --- [nio-8080-exec-1] c.t.m.aop.aoptest.classService.AService : test start
2019-04-11 15:33:31.400 INFO 13244 --- [nio-8080-exec-1] c.t.m.aop.aoptest.classService.AService : cn.test.modules.aop.aoptest.classService.AService$$EnhancerBySpringCGLIB$$2641a8af
2019-04-11 15:33:31.401 INFO 13244 --- [nio-8080-exec-1] c.t.m.aop.aoptest.classService.AService : test end
可见hello和test均被增强;
当前调用的对象属于cn.test.modules.aop.aoptest.classService.AService$$EnhancerBySpringCGLIB$$2641a8af
类
修改service代码如下:
@MyTest
public void test() {
log.info("test start");
log.info("test end");
}
@MyTest
public void hello() {
log.info("hello start");
log.info(AopContext.currentProxy().getClass().getName());
log.info("hello end");
new AService().test();
}
查看1的结果
输出:
2019-04-11 15:37:08.917 INFO 13244 --- [nio-8080-exec-3] c.t.m.aop.aoptest.aspect.MyTestAspect : before:hello
2019-04-11 15:37:08.917 INFO 13244 --- [nio-8080-exec-3] c.t.m.aop.aoptest.classService.AService : hello start
2019-04-11 15:37:08.917 INFO 13244 --- [nio-8080-exec-3] c.t.m.aop.aoptest.classService.AService : cn.test.modules.aop.aoptest.classService.AService$$EnhancerBySpringCGLIB$$2641a8af
2019-04-11 15:37:08.917 INFO 13244 --- [nio-8080-exec-3] c.t.m.aop.aoptest.classService.AService : hello end
2019-04-11 15:37:08.917 INFO 13244 --- [nio-8080-exec-3] c.t.m.aop.aoptest.classService.AService : test start
2019-04-11 15:37:08.917 INFO 13244 --- [nio-8080-exec-3] c.t.m.aop.aoptest.classService.AService : test end
可见hello被增强,test未被增强;
当前调用的对象属于cn.test.modules.aop.aoptest.classService.AService$$EnhancerBySpringCGLIB$$2641a8af
类
由上可见,spring在这里采用cglib来管理被动态代理的对象,那么我们之前有看到cglib生成的代理类自调用是会被增强的,为什么spring的cglib代理对象自调用增强不起作用呢?
这是因为spring实现的cglib是cglib风格的代理,实际上还是用类似jdk动态代理中委托的形式来实现的…
关于此我看到如下解释:
The behavior of the Cglib-proxies has nothing to do with the way of how cglib works but with how cglib is used by Spring. Cglib is capable of either delegating or subclassing a call. Have a look at Spring’s DynamicAdvisedInterceptor which implements this delegation. Using the MethodProxy, it could instead perform a super method call.
Spring defines delegation rather than subclassing in order to minimize the difference between using Cglib or Java proxies.
根据以上内容及分析,我们应该可以看出来,spring容器中ioc管理的对象默认是原生对象,但只要该类通过aop增强,该对象就是代理对象,我们用代理对象来调用方法才会触发各种代理增强
关于spring如何选择代理方式,之前可能有看到过优先使用jdk动态代理,没有接口再使用cglib动态代理;
实际上现在任何情况下都会优先选择cglib动态代理,选择jdk动态代理必须手动指定才行了
从上面的示例中我们可以看到,直接用autowired注入的对象是被aop增强的我们期望得到的对象,而自己new出来的则只是原始对象
如果我们想要获得被代理的对象,不通过spring的话,需要手动调用代理控制类去增强原始对象,AOP在spring里用的不要太多,可想而知会有多麻烦
而spring就改变了这一情况,他把控制反转了,之前是程序主动去创建依赖对象,而spring则是有一个容器来创建这些对象;所谓控制反转就是ioc容器控制了对这些对象的依赖获取
那么为什么叫反转呢,之前的情况是我们自己在程序中直接获取依赖的对象,这是正转;而现在是容器来创建和注入依赖对象,那么需要依赖对象的实例对依赖对象的依赖也就被反转了
下面参考开涛大神的讲解:
假设A类中有个方法的调用需要B类中的某个方法,而B类又依赖C类
我们此时需要调用A类中的方法,之前需要先创建B对象,再创建C对象;再把C对象注入到B对象里,再调用B的方法
现在,使用spring,我们只需在A类中注入B对象,关于B的依赖Spring会自动帮我们管理,这就是spring的依赖注入
所以说spring实现了依赖注入和控制反转,也实现了aop;
如果你不用依赖注入,那么也就不能方便的使用spring中的aop,而是需要很麻烦的手动设置原始对象的动态代理
下面参考bromon大神的讲解,讲的非常清晰明了:
https://blog.csdn.net/bromon/article/details/326250#comments
Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。