AOP(Aspect Oriented Programming):面向切面编程,指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的操做。如:性能监控、日志记录、权限控制等,经过AOP解决代码耦合问题,让职责更加单一。html
AOP技术它利用一种称为**“横切”**的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为”Aspect”,即切面。所谓”切面”,简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减小系统的重复代码,下降模块之间的耦合度,并有利于将来的可操做性和可维护性java
为了更好的理解aop的原理,咱们经过案例来一步步,首先,咱们先用spring框架,使用配置类注解方式注入bean,UserService做为业务代码:spring
@Service
public class UserService {
public void queryAll(){
System.out.println("业务代码:查询全部数据");
}
}
复制代码
@Configuration
@ComponentScan("com.star")
public class AppConfig {
}
复制代码
@Test
public void AOPTest(){
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
UserService bean = ac.getBean(UserService.class);
bean.queryAll();
}
复制代码
咱们运行 AOPTest 方法,能够看到控制台打印出:数据库
我们就在以上代码的基础上对功能进行加强。编程
如今咱们须要给业务代码执行先后加上打印日志,没有aop的时候,我们能够直接在 service 业务中增长相关方法进行加强:设计模式
@Service
public class UserService {
public void queryAll(){
System.out.println("before----业务代码执行前打印日志.....");
System.out.println("业务代码:查询全部数据");
System.out.println("after----业务代码执行前打印日志.....");
}
}
复制代码
这样一来,就把加强代码和业务代码放到了一块儿,这是很不合理的,而且增长了耦合,不利于代码的拓展。数组
所谓的动态代理,须要一个代理类,这个代理类是动态生成的,字节码要用的时候就建立,要用的时候就加载,在不修改源码的基础上对方法进行加强。有两种代理机制,一种是基于JDK的动态代理,另外一种是基于CGLib的动态代理,bean没有接口时使用 CGLib 代理,bean有接口则使用 JDK 代理。因为上面的案例中没有使用接口,因此这里用CGLib代理。markdown
有关动态代理能够参考以前的博客:blog.csdn.net/One_L_Star/…框架
@Test
public void AOPTest1(){
final UserService bean = new UserService();
UserService cglibProducer = (UserService) Enhancer.create(bean.getClass(), new MethodInterceptor(){
/** * 做用:执行被代理对象的任何借口方法都会通过该方法 * @param proxy:代理对象的引用 * @param method:当前执行的方法 * @param args:当前执行方法所需的参数 * @return:和被代理对象方法有相同的返回值 * @throws Throwable */
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("记录日志");
Object result = method.invoke(bean, args);
return result;
}
});
cglibProducer.queryAll();
}
复制代码
执行后打印以下:ide
能够看到,通过CGLib代理后,不修改业务代码的基础上,对方法进行了加强,而在spring aop 的底层,也是使用的动态代理,不过要远远复杂于上面的代码,若是要深究,须要查看spring的源码,这里只讲基本原理,源码有点太费头发。
最后,我们来看看spring是如何加强的,AOP是一个标准规范,而为了实现这个标准规范,有几种方式:
这四种方式都是实现aop的方法,这里讲一下经过AspectJ提供的注解实现AOP,但在spring官网中,有AspectJ 的概念,主要是由于在spring2.x的时候,spring aop的语法过于复杂,spring想进行改进,而改进的时候就借助了AspectJ 的语法、编程风格来完场aop的配置功能,这里使用AspectJ 注解方式来实现。
在配置类中添加@EnableAspectJAutoProxy注解,开启切面编程功能,添加后以下:
@Configuration
@ComponentScan("com.star")
@EnableAspectJAutoProxy
public class AppConfig {
}
复制代码
使用@Aspect注解声明一个切面,并使用@Before、@After等注解代表链接点
@Aspect
@Component
public class LogAspect {
@Pointcut("execution(* com.star.service..*.*(..))")
public void pointCut(){};
@Before("pointCut()")
public void logStart(){
System.out.println("查询以前打印日志....");
}
@After("pointCut()")
public void logEnd(){
System.out.println("查询以后打印日志....");
}
@AfterReturning("pointCut()")
public void logReturn(){
System.out.println("查询以后正常返回....");
}
@AfterThrowing("pointCut()")
public void logException(){
System.out.println("查询以后返回异常....");
}
}
复制代码
@Test
public void AOPTest(){
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
UserService bean = ac.getBean(UserService.class);
bean.queryAll();
}
复制代码
直接运行测试类,能够看到对方法进行了加强
直接获取一个代理对象 ,首先产生一个目标对象,而后对目标对象进行代理,返回代理对象,把目标对象放到了map中
在spring初始化的时候就已经完成了代理,也就是执行下面代码的时候就完成了代理
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AopConfig.class);
复制代码
术语的理解参考:yq.aliyun.com/articles/63…
在上面,咱们已经经过实例实现了经过AOP对方法进行加强,如今咱们来理解一下,首先,咱们必需要了解AOP的术语,这些术语在上面的AOP切面加强案例中都有体现,这里结合案例来理解一下。
链接点是一个比较空泛的概念,就是定义了哪一些地方是能够切入的,也就是全部容许你通知的地方。
切点就是定义了通知被应用的位置 (配合通知的方位信息,能够肯定具体链接点)
通知(Advice):切入链接点的时机和切入链接点的内容称为通知,Spring切面能够应用5种类型的通知:
前置通知(Before):在目标方法被调用以前调用通知功能;
后置通知(After):在目标方法完成以后调用通知,此时不会关心方法的输出是什么;
返回通知(After-returning):在目标方法成功执行以后调用通知;
异常通知(After-throwing):在目标方法抛出异常后调用通知;
环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用以前和调用以后执行自定义的行为。
通知就定义了,须要作什么,以及在某个链接点的何时作。 上面的切点定义了在哪里作。
目标对象(Target):指的是被加强的对象,也就是被通知的对象,也就是真正的业务逻辑,在上面案例中,UserService就是目标对象
引介(Introduction):容许咱们向现有的类添加新方法属性。经过引介,咱们能够动态地为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。
织入(Weaving):织入是将通知添加到目标类具体链接点上的过程。AOP像一台织布机,将目标类、通知或引介经过AOP这台织布机完美无缺地编织到一块儿。根据不一样的实现技术,AOP有三种织入的方式:
编译期织入,这要求使用特殊的Java编译器。
类装载期织入,这要求使用特殊的类装载器。
动态代理织入,在运行期为目标类添加通知生成子类的方式。
把切面应用到目标对象来建立新的代理对象的过程,Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
切点的通知的结合,切面知道全部它须要作的事:什么时候/何处/作什么
原理实现参考:www.cnblogs.com/stateis0/p/…
【1】AOP的设计
【2】代理的建立
注意:建立代理对象时,同时会建立一个外层拦截器,这个拦截器就是 Spring 内核的拦截器。用于控制整个 AOP 的流程。
【3】代理的调用
如图:
调用过程: