AOP
AOP作为Spring中核心的部分,重点研究一下。其实AOP的概念不难,主要就是为了降低解耦度然后减少代码的冗余。
日志一般使用的是AOP计数,类似下面这张图
1.1 AOP的底层原理
AOP的底层原理,动态代理
- 动态代理的代理类是动态生成的,不是直接写好的!
- 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口——JDK动态代理
- 基于类:cglib类
- java字节码实现:javasist
需要了解两个类:Proxy: 代理类,InvocationHandler : 调用处理程序
//Proxy是生成动态代理类,提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。
//InvocationHandler-- invoke 调用处理程序并返回接口, 是由代理实例的调用处理程序实现的接口 。
1.2 动态代理的实现
1.2.1 cglib技术
比如,现在存在一个UserService类:
public class UserService {
public void test() {
System.out.println("test...");
}
}
此时,我们new一个UserService对象,然后执行test()方法,结果是显而易见的。
如果我们现在想在不修改UserService类的源码前提下,给test()增加额外逻辑,那么就可以使用动态代理机制来创建UserService对象了,比如:
使用cglib技术
UserService target = new UserService();
// 通过cglib技术
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
// 定义额外逻辑,也就是代理逻辑,这个是数组
enhancer.setCallbacks(new Callback[]{new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before...");
Object result = methodProxy.invoke(target, objects);
System.out.println("after...");
return result;
}
}});
//里面accept,返回的是new Callback[]数组的下标
enhancer.setCallbackFilter(new CallbackFilter() {
@Override
public int accept(Method method) {//返回的数据是上面Callback的下标
if(method.getName().equals("test")){
return 0;
}else{
return 1;
}
}
});
// 动态代理所创建出来的UserService对象
UserService userService = (UserService) enhancer.create();
// 执行这个userService的test方法时,就会额外会执行一些其他逻辑
userService.test();
得到的都是UserService对象,但是执行test()方法时的效果却不一样了,这就是代理所带来的效果。
上面是通过cglib来实现的代理对象的创建,是基于父子类的,被代理类(UserService)是父类,代理类是子类,代理对象就是代理类的实例对象,代理类是由cglib创建的,对于程序员来说不用关心。
1.2.2 jdk动态代理
jdk本身也提供了一种创建代理对象的动态代理机制,但是它只能代理接口,也就是UserService得先有一个接口才能利用jdk动态代理机制来生成一个代理对象,比如:
public interface UserInterface {
public void test();
}
public class UserService implements UserInterface {
public void test() {
System.out.println("test...");
}
}
利用JDK动态代理来生成一个代理对象:
UserService target = new UserService();
// UserInterface接口的代理对象
Object proxy = Proxy.newProxyInstance(UserService.class.getClassLoader(), new Class[]{UserInterface.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before...");
Object result = method.invoke(target, args);
System.out.println("after...");
return result;
}
});
UserInterface userService = (UserInterface) proxy;
userService.test();
如果你把new Class[]{UserInterface.class},替换成new Class[]{UserService.class},允许代码会直接报错:
Exception in thread "main" java.lang.IllegalArgumentException: com.zhouyu.service.UserService is not an interface
表示一定要是个接口。
由于这个限制,所以产生的代理对象的类型是UserInterface,而不是UserService,这是需要注意的。
1.3 AOP的实现
AOP的实现有四种方法
- 1.经典的基于代理的AOP
- 2.@AspectJ注解驱动的切面
- 3.纯POJO切面
- 4.注入式AspectJ切面
下面我只简单介绍一下自己当前使用的,基于注解的AOP实现
首先先自己定义一个UserService,作为bean
@Component
public class UserService{
@Value("wbw")
private String user;
public void test() {
//applicationContext.publishEvent("123");
System.out.println("wbw");
}
然后添加@Aspect切面注解,其中@Before是指在指定方法前运行。切面类
@Aspect
@Component
public class ZhouyuAspect {
@Before("execution(public void com.zhouyu.service.UserService.test())")
public void zhouyuBefore(JoinPoint joinPoint) {
System.out.println("zhouyuBefore");
System.out.println("目标方法名为:" + joinPoint.getSignature().getName());
System.out.println("目标方法所属类的简单类名:" + joinPoint.getSignature().getDeclaringType().getSimpleName());
System.out.println("目标方法所属类的类名:" + joinPoint.getSignature().getDeclaringTypeName());
System.out.println("目标方法声明类型:" + Modifier.toString(joinPoint.getSignature().getModifiers()));
//获取传入目标方法的参数
Object[] args = joinPoint.getArgs();
for (int i = 0; i < args.length; i++) {
System.out.println("第" + (i+1) + "个参数为:" + args[i]);
}
System.out.println("被代理的对象:" + joinPoint.getTarget());
System.out.println("代理对象自己:" + joinPoint.getThis());
}
}
主main函数
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) context.getBean("userService");
userService.test();
AOP代理输出,展示join point的输出消息
zhouyuBefore
目标方法名为:test
目标方法所属类的简单类名:UserService
目标方法所属类的类名:com.zhouyu.service.UserService
目标方法声明类型:public
被代理的对象:com.zhouyu.service.UserService@480bdb19
代理对象自己:com.zhouyu.service.UserService@480bdb19
wbw
上面是AOP的简单实现,其实还有很多种就没一一写出。
1.3 AOP的概念和专属名词
意思是,AOP中的这些概念不是Spring特有的,不幸的是,AOP中的概念不是特别直观的,但是,如果Spring重新定义自己的那可能会导致更加混乱
-
Aspect:表示切面,比如被@Aspect注解的类就是切面,可以在切面中去定义Pointcut、Advice等等
-
Join point:表示连接点,表示一个程序在执行过程中的一个点,比如一个方法的执行,比如一个异常的处理,在Spring AOP中,一个连接点通常表示一个方法的执行。就是被代理的方法
-
Advice:表示通知,表示在一个特定连接点上所采取的动作。Advice分为不同的类型,后面详细讨论,在很多AOP框架中,包括Spring,会用Interceptor拦截器来实现Advice,并且在连接点周围维护一个Interceptor链。代理逻辑,就是在方法前或后执行的部分
-
Pointcut:表示切点,切入点,用来匹配一个或多个连接点,Advice与切点表达式是关联在一起的,Advice将会执行在和切点表达式所匹配的连接点上
-
Introduction:可以使用@DeclareParents来给所匹配的类添加一个接口,并指定一个默认实现
-
Target object:目标对象,被代理对象
-
AOP proxy:表示代理工厂,用来创建代理对象的,在Spring Framework中,要么是JDK动态代理,要么是CGLIB代理
-
Weaving:表示织入,表示创建代理对象的动作,这个动作可以发生在编译时期(比如Aspejctj),或者运行时,比如Spring AOP
下面这个 Pointcut的概念更加清晰,就是切入点,指定哪个方法
Advice就是可以在这个切入点环绕执行自定义的相应代码
JoinPoint 对象
JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象.
常用api: