学习交流群:
✅✅1:这是孙哥suns给大家的福利!
✨✨2:我们免费分享Netty、Dubbo、k8s、Mybatis、Spring...应用和源码级别的视频资料
3:QQ群:583783824 工作微信:BigTreeJava 拉你进微信群,免费领取!
4:本文章内容出自上述:Spring应用课程!
5:以上内容,进群免费领取呦~
编程人员在常规过程中不常遇到的问题,但是一旦遇到这个问题,就一定会出错
public class TestASpectProxy {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext3.xml");
UserService userService = (UserService) ctx.getBean("userService");
userService.register(new User());
//--------------aspect log-----------
//--------------aspect tx-----------
//UserServiceImpl.register
//UserServiceImpl.login
}
}
@Aspect
public class MyAspect {
//这是一个类切入点,给所有的都加上额外功能
@Pointcut("execution(* *..UserServiceImpl.*(..))")
public void myPointcut(){}
/**
* ProceedingJoinPoint这个等效于MethodInvocation
* 这里写的是额外功能的部分,现在的额外功能不需要实现接口了,只需要加入注解。
* */
@Around(value="myPointcut()")//在这里调用切入点表达式,就完成了整合。
public Object arround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("--------------aspect log-----------");
Object proceed = joinPoint.proceed();
return proceed;
}
@Around(value="myPointcut()")
public Object arround1(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("--------------aspect tx-----------");
Object proceed = joinPoint.proceed();
return proceed;
}
}
public class UserServiceImpl implements UserService {
@Override
public void login(String username, String password) {
System.out.println("UserServiceImpl.login");
}
@Override
public void register(User user) {
System.out.println("UserServiceImpl.register");
this.login("suns","123456");
}
}
测试结果并不符合预期,login方法调用的时候额外功能并没有展示,这个就是那个坑,为什么login方法没有执行额外功能呢?当我们的调用者调用register方法的时候,我们调用的是Proxy代理对象的Register方法,加入了日志加入了事务,最终执行了这两个额外功能,没有问题,但是调用login方法的时候,是在原有功能当中调用的login方法,调用的并不是代理对象中的login方法而是调用的login原生的login方法所以没有额外功能的加入。
这个告诉我们的道理就是:方法中调用类中原始对象的方法的的时候,那么久不会有额外功能的引入,要想使调用方法的额外功能也展示需要调用代理对象的对应的方法,而不是原始对象的对应的方法。也就是说我们需要在我们的register方法中获取到Spring的工厂对象,进而拿到我们的代理对象,进而调用我们的代理对象的login方法,然而初步的想法是在register方法中创建Spring的工厂对象,然而这是不可取的,这是重量级资源,占用大量内存,可以采取的途径是让我们的UserServiceImpl 实现ApplicationContextAware接口,Aware的含义是知道的意思,这个接口中有一个方法是setApplicationContext,通过这个方法入参是工厂对象,Spring容器启动时会回调这个方法,将引用赋值给这个这个方法,所以我们实现方法之后,存一份就好了。
public class UserServiceImpl implements UserService, ApplicationContextAware {
private ApplicationContext ctx;
@Override
public void login(String username, String password) {
System.out.println("UserServiceImpl.login");
}
@Override
public void register(User user) {
System.out.println("UserServiceImpl.register");
UserService userService = (UserService) ctx.getBean("userService");//userService是代理对象
userService.login("suns","123456");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ctx = applicationContext;
}
}
public class TestASpectProxy {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext3.xml");
UserService userService = (UserService) ctx.getBean("userService");
userService.register(new User());
//--------------aspect log-----------
//--------------aspect tx-----------
//UserServiceImpl.register
// --------------aspect log-----------
//--------------aspect tx-----------
//UserServiceImpl.login
}
}
//实现了被调用的时候也有这个额外功能的实现。
在同一个业务类中,进行业务方法间的相互调用的时候,只有最外层的方法,加入了额外功能了。
内部的方法通过普通的方式进行调用,并没有额外功能,都是原始方法,那么如果想让内层的方法也调用代理对象的方法,就需要通过ApplicationContextAware获得工厂,进而获得代理对象