一.SpringAop是什么?
Aop(Aspect Oriented Programming)面向切面编程,指的是抽取重复加在业务前后的代码,通过切面的方法应用在目标方法上。比如一些我们需要在某个方法前后记入日志,我们就可以使用aop动态代理来实现方法的增强。
二.AOP核心概念
1.连接点:连接点是一个非常抽象的概念,它表示一个时机,比如某个方法方法执行前后,或者方法出现异常时都是连接点。但在springAOP中仅仅支持方法的连接点。
2.通知:通知是指在连接点处执行的某段代码,所有被通知注解标注的方法都是通知。
3.切点:切点是用来匹配连接点的,下面通过例子说明
4.目标对象:指需要处理的目标类。如果没有AOP的支持,目标对象需要独立王朝所有业务逻辑。目标方法通常指切点映射的方法
5.切面:切面指的是AOP处理的类,此类封装了许多其他类需要植入的方法。
6.织入:织入是指将通知添加到目标类具体的连接点的过程。
三.实例
下面演示通过AOP来实现日志管理的例子:
BookHandel类
package com.example.aop;
import org.springframework.stereotype.Component;
@Component
public class BookHandel {
private String nime;
public String getNime() {
return nime;
}
public void setNime(String nime) {
this.nime = nime;
}
public BookHandel(String nime) {
this.nime = nime;
}
public void addbook(){
System.out.println("添加对象");
}
public static void main(String[] args) {
}
}
Logaspect类
package com.example.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
@Aspect
@Component
@EnableAspectJAutoProxy
public class Logaspect {
@Before("execution(* com.example.aop.BookHandel.addbook())")
public void dealAddlog(JoinPoint joinPoint){
System.out.println("开始添加");
}
@AfterThrowing("execution(* com.example.aop.BookHandel.addbook())")
public void dealAfeterReturn(JoinPoint joinPoint){
System.out.println("添加失败");
}
@AfterReturning("execution(* com.example.aop.BookHandel.addbook())")
public void dealException(JoinPoint joinPoint){
System.out.println("添加成功");
}
}
XML配置bean
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.example">
</context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<bean id="BookHandel" class="com.example.aop.BookHandel">
<constructor-arg name="nime" value="java">
</constructor-arg>
</bean>
</beans>
其中Logaspect就是一个切面类,通过@Aspect注解标注,里面有许多通知,通知包括前置通知、后置通知和环绕通知。
前置通知使用 @Before 注解标注,在目标方法执行之前被调用。
后置通知使用 @After 注解标注,在切点方法执行之后被调用。
环绕通知使用了 @Around 注解标注,在切点方法执行前后都可以进行一些额外的处理。(这里就不演示,大家可以自己实现一下)
这些注解里execution表达式就是AOP的切点表达式,指明了目标地址,用来告诉程序哪些方法需要用AOP处理
BookHandel就是目标对象,我们需要对该类的业务使用AOP进行日志记录。
运行结果:
这样就实现一个简单的日志管理。
四.AOP的实现原理
AOP在spring和综合是通过代理实现的。在AOP编程中,如果一个类被标记为目标对象,则spring的IOC容器会为该类生成一个代理对象,在获取此目标对象的时候,返回的式代理对象。在调用代理对象的方法时,会依次执行目标对象的前置通知,目标方法,目标方法的后置通知。
springAOP实现代理的方式有两种:
1.JDK动态代理:系统默认会采用这种代理方式,但目标对象必须是实现一个或者多个接口。(原因这里不说明,感兴趣的看一看这篇博客)【深度思考】聊聊JDK动态代理原理_jdk代理-CSDN博客
示例:
接口staff
public interface staff {
void work();
}
coder类:
public class coder implements staff{
@Override
public void work() {
System.out.println("写代码");
}
}
AttendanceInvocationHandler类
package com.example.aop;
import org.springframework.cglib.proxy.InvocationHandler;
import java.lang.reflect.Method;
public class AttendanceInvocationHandler implements InvocationHandler {
private Object target;
public AttendanceInvocationHandler(Object target){
this.target=target;
}
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
System.out.println("上班打卡");
Object invoke = method.invoke(target, objects);
System.out.println("下班打卡");
return invoke;
}
}
测试类:
import org.springframework.cglib.proxy.Proxy;
public class Text {
public static void main(String[] args) {
coder coder = new coder();
//获得代理对象
Object proxyInstance = Proxy.newProxyInstance(coder.class.getClassLoader(), coder.class.getInterfaces(),
new AttendanceInvocationHandler(coder));
staff staff=(staff) proxyInstance;
staff.work();
}
}
运行结果:
2.CGLIB动态代理:如果目标对象没有实现接口,Spring AOP 就会使用 CGLIB(Code Generation Library)来为目标对象创建代理。CGLIB 使用字节码生成技术,在运行时生成目标对象的子类,并重写其中的方法来织入切面逻辑。
示例:
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class Text1 {
public static void main(String[] args) {
coder coder = new coder();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(coder.getClass());
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("上班打卡");
Object invoke = method.invoke(coder, objects);
System.out.println("下班打卡");
return invoke;
}
});
coder o = (coder)enhancer.create();
o.work();
}
}
运行结果跟上面一样。