Spring两大底层机制,一个是IOC,控制反转,用于减低计算机代码之间的耦合度。最常见的方式是依赖注入(Dependency Injection,简称DI)。另一个则是AOP,面向切面编程。
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
对程序进行增强:不修改源码的情况下,AOP可以进行权限的校验,日志记录,性能监控,事务控制
JDK动态代理: 面向接口的,只能对实现了接口的类产生代理
Cglib动态代理(类似于JavaSsit第三方代理技术):对没有实现接口的类产生代理对象(生成子类对象)
类实现了接口,Spring就用JDK动态代理,没有实现接口的,用Cglib动态代理,Spring底层可以自动切换
1.通知(Advice)
就是你想要的功能,也就是上面说的 安全,事物,日志等。你给先定义好把,然后在想用的地方用一下。
2.连接点(JoinPoint)
spring允许你使用通知的地方,基本每个方法的前,后(两者都有也行),或抛出异常时都可以是连接点,spring只支持方法连接点.和方法有关的前后(抛出异常),都是连接点。
3.切入点(Pointcut)
上面说的连接点的基础上,来定义切入点,你的一个类里,有5个方法,那就有5个连接点了,但是你并不想在所有方法附近都使用通知(使用叫织入),你只想让其中的几个,在调用这几个方法之前,之后或者抛出异常时干点什么,那么就用切点来定义这几个方法,让切点来筛选连接点,选中那几个你想要的方法。
4.切面(Aspect)
切面是通知和切入点的结合。通知说明了干什么和什么时候干(什么时候通过方法名中的before,after,around等就能知道),而切入点说明了在哪干(指定到底是哪个方法),这就是一个完整的切面定义。
5.引入(introduction)
允许我们向现有的类添加新方法属性。就是把切面(也就是新方法属性:通知定义的)用到目标类中
6.目标(target)
引入中所提到的目标类,也就是要被通知的对象,也就是真正的业务逻辑,他可以在毫不知情的情况下,被咱们织入切面。而自己专注于业务本身的逻辑。
7.代理(proxy)
怎么实现整套aop机制的. 就是通过代理.织入增强功能后产生的代理对象
8.织入(weaving)
把切面应用到目标对象来创建新的代理对象的过程。有3种方式,spring采用的是运行时。
图解:
▪ 引入基本开发包
▪ 引入AOP开发的相关包
第一个aop联盟
第二个aspectj的依赖
第三个AOP核心包
第四个Spring与aspects整合的包
引入xml配置约束文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
beans>
配置web项目
编写一个切面类
配置切面类
<aop:config>
<aop:pointcut expression="execution(* com.spring.service.UserServiceImpl.save(..))" id="p1"/>
<aop:aspect ref="myAspectj">
<aop:before method="check" pointcut-ref="p1"/>
aop:aspect>
aop:config>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userServiceImpl" class="com.spring.service.impl.UserServiceImpl">bean>
<bean id="myAspectj" class="com.spring.aspectj.MyAspectj">bean>
<aop:config>
<aop:pointcut expression="execution(* com.spring.service.UserService.add(..))" id="p1"/>
<aop:aspect ref="myAspectj">
<aop:before method="check" pointcut-ref="p1"/>
aop:aspect>
aop:config>
beans>
通知的类型
▪ 前置通知before:在目标方法执行之前的操作
可以获取切入点的信息
▪ 后置通知after-returning:在目标方法执行之后的操作
获得目标方法的返回值
▪ 环绕通知around:在目标方法执行之前和之后进行操作
▪ 异常抛出通知after-throwing:在目标方法抛出异常时的操作(比如出现异常后进行回滚)。
▪ 最终通知after:无论是否有异常都会执行的,相当于try-catch-finally中的finally块
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userServiceImpl" class="com.spring.service.impl.UserServiceImpl">bean>
<bean id="myAspectj" class="com.spring.aspectj.MyAspectj">bean>
<aop:config>
<aop:pointcut expression="execution(* com.spring.service.UserService.add(..))" id="p1"/>
<aop:pointcut expression="execution(* com.spring.service.UserService.select(..))" id="p2"/>
<aop:pointcut expression="execution(* com.spring.service.UserService.selectReturn(..))" id="p3"/>
<aop:pointcut expression="execution(* com.spring.service.UserService.update(..))" id="p4"/>
<aop:pointcut expression="execution(* com.spring.service.UserService.delete(..))" id="p5"/>
<aop:pointcut expression="execution(* com.spring.service.UserService.deleteAfter(..))" id="p6"/>
<aop:aspect ref="myAspectj">
<aop:before method="check" pointcut-ref="p1"/>
<aop:after-returning method="back" pointcut-ref="p2"/>
<aop:after-returning method="backReturn" pointcut-ref="p3" returning="obj"/>
<aop:around method="around" pointcut-ref="p4"/>
<aop:after-throwing method="doException" pointcut-ref="p5"/>
<aop:after method="doAfter" pointcut-ref="p6"/>
aop:aspect>
aop:config>
beans>
package com.spring.aspectj;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* 这是做增强功能的类
*/
public class MyAspectj {
public void check(){
//通知
System.out.println("--校验身份--");
}
public void back(){
System.out.println("--之后执行--");
}
public void backReturn(Object obj){
System.out.println("--之后执行,返回的数据:"+(String)obj);
}
public void around(ProceedingJoinPoint point) throws Throwable {
//point用来接收切入点update()
System.out.println("--之前操作--");
point.proceed(); //执行跌入点
System.out.println("--之后操作--");
}
public void doException(){
//被增强的切入点报异常之后会执行,不报异常不会执行
System.out.println("--异常抛出了--");
}
public void doAfter(){
System.out.println("--不管有没有异常始终都会执行--");
}
}
使用场景:
前置通知:转账之前校验用户名和密码及余额
切入点表达式语法
基于execution的函数完成的
语法
execution([访问修饰符] 方法返回值 包名.类名.方法名(参数))
com.spring.service.UserServiceImpl.add(…)
com.spring.service.UserServiceImpl.(…) 开发中用的最多的是这种,对当前类下所有的方法做增强处理(场景:事务处理)
com.spring.service.impl.save(…)
com.spring.service.impl.(…) 表示: com.spring.service.impl类下所有方法被增强
Spring注解的通知类型
开启注解的aop开发
aop:aspectj-autoproxy
前置通知:@Before
后置通知:@AfterReturning(value=“execution()”,returning=“result”)
环绕通知:@Around
异常抛出通知:@AfterThrowing(value=“execution()”,throwing=“e”)
最终通知:@After
package com.spring.aspectj;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Value;
/**
* 这是做增强功能的类
*/
@Aspect //注解通知类,告知Spring这是通知所在的类
public class MyAspectj {
@Before(value = "execution(* com.spring.service.UserService.add(..))")
public void check(){
//通知
System.out.println("--校验身份--");
}
@AfterReturning(value = "execution(* com.spring.service.UserService.select(..))")
public void back(){
System.out.println("--之后执行--");
}
@AfterReturning(value = "execution(* com.spring.service.UserService.selectReturn(..))",returning = "obj")
public void backReturn(Object obj){
System.out.println("--之后执行,返回的数据:"+(String)obj);
}
@Around(value = "execution(* com.spring.service.UserService.update(..))")
public void around(ProceedingJoinPoint point) throws Throwable {
//point用来接收切入点update()
System.out.println("--之前操作--");
point.proceed(); //执行跌入点
System.out.println("--之后操作--");
}
@AfterThrowing(value = "execution(* com.spring.service.UserService.delete(..))",throwing = "e") //e可以接收切入点抛出的异常
public void doException(Exception e){
//被增强的切入点报异常之后会执行,不报异常不会执行
System.out.println("--异常抛出了--");
System.out.println(e.toString());
}
@After(value = "execution(* com.spring.service.UserService.deleteAfter(..))")
public void doAfter(){
System.out.println("--不管有没有异常始终都会执行--");
}
}
配置文件:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:aspectj-autoproxy>aop:aspectj-autoproxy>
<bean id="userServiceImpl" class="com.spring.service.impl.UserServiceImpl">bean>
<bean id="myAspectj" class="com.spring.aspectj.MyAspectj">bean>
beans>
目标类没有实现接口,Spring底层会自动使用Cglib的代理
目标类写了接口,Sping底层会使用jdk的动态代理