- 测试一下没有任何Aop情况下的bean
- 定义 UserService
public interface UserService {
void saveUser(String user);
}
2.定义一个UserService的实现,并加入IOC容器
@Service
public class UserServiceImpl implements UserService{
@Override
public void saveUser(String user) {
System.out.println("保存用户:" + user);
}
}
- 启动容器并尝试获取bean
private ApplicationContext getContext(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.aop");
return context;
}
@Test
public void test1() throws Exception {
ApplicationContext context = getContext();
UserService bean = context.getBean(UserService.class);
System.out.println(bean);//com.aop.service.UserServiceImpl@76c3e77a
}
此时的bean就是一个普通的UserServiceImpl类实例
- 尝试加入切面后获取
- 定义一个切面类
package com.aop.aspect;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class MyAspect {
@Pointcut("within(com.aop.service.UserServiceImpl)")
public void pointcut(){
}
@Before("pointcut()")
public void before(){
System.out.println("方法前置...");
}
@After(value = "pointcut()")
public void after(){
System.out.println("方法最终通知...");
}
@AfterThrowing(value = "pointcut()")
public void afterThrow(){
System.out.println("方法异常通知...");
}
@AfterReturning("pointcut()")
public void afterReturn(){
System.out.println("方法后置通知");
}
}
2.尝试调用UserServiceImpl的方法
此时切面类生效了。我们再看一下UserServiceImpl的类型
很明显,此时的bean是一个代理对象,而且是Proxy和UserService类的实例,而不是UserServiceImpl的实例,因为此时Spring是用JDK动态代理去创建代理对象的,只能基于接口创建,相当于为UserService创建了一个实现类,而这个实现类中注入了被代理的对象,也就是UserServiceImpl。
那么既然这个bean也是Proxy的实例,那么我们从容器获取一个Proxy类也是可以的。
- 去掉UserServiceImpl实现的接口
@Service
public class UserServiceImpl {
public void saveUser(String user) {
System.out.println("保存用户:" + user);
}
}
此时该类没有实现任何接口,我们看看aop是否还能生效。
此时的切面还是生效了。我们看一下这个bean的类型
此时UserServiceImpl没有实现任何接口,Spring用了CGLIB生成了代理对象,那么此时的bean类型就是UserServiceImpl类型。
- 新创建一个Service,看看能不能代理
@Service
public class OrderServiceImpl {
public void saveOrder(){
System.out.println("保存了订单...");
}
}
发现此时OrderServiceImpl并没有被代理。原因是在写切入点表达式时只代理了UserServiceImpl。尝试修改一下切入点表达式
@Pointcut("execution(* com.aop.service..*(..))")
此时这两个bean都被代理了。
Spring在创建完bean以后会利用bean的后置处理器判断该bean是否
需要被代理(根据配置的切面的切入点表达式),如果需要,则用jdk获取cglib创建代理对象并注入被代理的对象,然后通过反射获取切面类中的各种增强方法,最终将这个代理对象重新放入容器中,在其他需要注入的地方使用。这就是使用容器带来的好处,只需要简单的配置,容器就可以生成代理对象并自动注入好。
- 多个切面的配置
- 给容器中再注入一个切面类
2.尝试获取bean,并执行代码
此时发现,先切入了MyAspect,然年再切入了MyAspect1,这个是可以通过@Order注解来配置的,比如现在想先切入MyAspect1
@Component
@Aspect
@Order(value = 1)
public class MyAspect1 {
@Component
@Aspect
@Order(value = 2)
public class MyAspect {
此时就先切入了MyAspect1.切面的执行顺序类似SpringMVC的拦截器顺序。