02总结导入的jar包是在原有的基础上导入的,如果没有导入,可以参考Spring总结--->01先行导入。
Spring AOP
SpringAOP的底层是通过代理来实现的,那么代理又分为静态代理和动态代理。
静态代理就是在代码中手动去写代理类,代理类要与目标类实现相同接口,通常用于对原有业务逻辑的扩充。
动态代理实在程序运行时创建的,代理类并不是在Java代码中定义的(动态代理是不需要在代码中手写代理类的)而是在运行时根据我们在Java代 码中的“指示”动态生成的。
其中动态代理又分为JDK动态代理和CGLIB动态代理。
AOP 相关术语
Joinpoint(连接点): 所谓连接点是指那些被拦截到的点。
Pointcut(切入点): 指我们要对哪些连接点进行拦截的定义。
Advice(通知/增强): 所谓通知是指拦截到连接点之后所要做的事情就是通知。 通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
Introduction(引介): 引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方 法或 Field。
Target(目标对象): 代理的目标对象。
Proxy(代理): 一个类被 AOP 织入增强后,就产生一个结果代理类。
Aspect(切面): 是切入点和通知(引介)的结合。
静态代理
1、创建主业务接口。
2、创建目标类实现主业务接口。
3、创建代理类实现主业务接口。
4、在代理类中对某项方法做增强处理(转化为大写)。
5、在测试类中进行测试。
静态带来的局限性:代理只能代理特定类型的目标,每增加一种类型都需要添加一个代理。为了解决静态代理的局限性,使用动态代理。
JDK动态代理
应用场景:
1、如果目标对象实现了接口,采用JDK的动态代理 。
2、如果目标对象没有实现了接口,必须采用CGLIB动态代理。
动态代理创建步骤:
1、定义目标类对象。
2、定于目标类对象的动态代理对象,在该对象创建后的三个参数的含义为:
loader:目标类的类加载器,通过目标类的对象找到目标类,然后获取目标类的类加载器。
interfaces:目标类实现的接口,通过目标类的对象找到目标类,然后获取目标类的所有接口。
h:调用处理器,通过new InvocationHandler()类获取。
InvocationHandler参数:
proxy:代理对象。
method:目标方法。
args:目标方法的参数。
3、调用方法。
接下来通过反射机制调用目标方法,method.invoke(目标类对象,args);
注意:
在传参的时候会报错,因为是内部类,在调用外部类的时候需要将目标类对象使用final进行修饰。
最终结果如下,可以看的出并没有任何的代理类,代理是动态生成的。
CGLIB动态代理
cglib动态代理. 本质上动态生成了一个被代理目标的子类。
步骤:
1、创建动态代理工厂类,实现MethodIntercepthor。
2、创建CGLIB代理对象的方法,方法类型为目标类。
2.1--->创建增强器。
2.2--->指定父类(需要增强的类)
2.3--->指定回调接口的对象
2.4--->创建CGLIB代理对象
3、重写接口方法:
拦截器方法中的参数:
第一个,obj:目标对象
第二个,method:目标方法。
第三个,args:目标方法的参数。
第四个,proxy:方法代理对象。
3.1--->调用目标方法,method.invoke(目标对象,args);
目标对象需要进行创建,在代理工厂类的内部进行创建,提供带参构造与无参构造。
3.2--->将增强结果进行返回。
Schame-based方式的通知,以前置通知举例
Schame-based方式步骤如下:
1、导入jar包。
spring-aop-4.1.6.RELEASE。
com.springsource.org.aopalliance-1.0.0。
2、创建java类,实现MethodBeforeAdvice接口,添加未实现方法,并在方法内部添加一条输出语句,用来测试在什么时候输出的。
3、在Spring容器中进行注册,配置applicationContext.xml文件。
3.1--->注册目标类
<bean id="someServiceImpl" class="目标类的全限定类名"></bean>
3.2--->注册切面,前置通知
<bean id="myMethodBeforeAdvice" class="全限定类名,也就是第二步创建的那个java类"></bean>
3.3--->通过动态代理,将前置通知注入到目标类中。
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="someServiceImpl"></property>
<property name="interfaces" value="全限定类名"></property>
<property name="interceptorNames" value="切面"></property>
4、测试
需要注意的是获取bean的时候,获取的是增强代理过的bean。
AspectJ的通知分类
AspectJ的切入点表达式
示例:
execution(public * *(..)) 指定切入点为:任意公共方法。
execution(* set *(..)) 指定切入点为:任何一个以“set”开始的方法。
execution(* com.xyz.service.*.*(..)) 指定切入点为:定义在service包里的任意类的任意方法。
execution(* com.xyz.service..*.*(..)) 指定切入点为:定义在service包或者子包里的任意类的任意方法。
“..”出现在类名中时, 后面必须跟“*”,表示包、子包下的所有类。
execution(* *.service.*.*(..)) 指定只有一级包下的serivce子包下所有类(接口)中的所有方法为切入点
execution(* *..service.*.*(..)) 指定所有包下的serivce子包下所有类(接口)中的所有方法为切入点
AspectJ注解方式配置通知,以前置通知举例。
3、
添加bean标签,该标签负责注册目标类,其中class属性的值是目标类的全限定类名。
添加bean标签,该标签负责注册切面,其中class属性的值是切面的全限定类名
注册自动代理。1、导入jar包
spring-aspects-4.1.6.RELEASE.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
2、配置文件中添加约束头信息
<?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: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">
3、
添加bean标签,该标签负责注册目标类,其中class属性的值是目标类的全限定类名。
添加bean标签,该标签负责注册切面,其中class属性的值是切面的全限定类名
注册自动代理。
4、测试。
常用的注解
@Aspect:表明当前类是一个切面。
@Before:前置通知。
@AfterReturning:后置通知
@Around:环绕通知。
@AfterThrowing:异常通知
@After:最终通知
其中,后置通知、环绕通知可以获得目标方法返回值。
AspectJ配置文件方式
搭配环境。
1、添加约束头信息。
2、注册目标类。
3、注册切面。
4、定义切入点。
注意:
method = "用于指定哪个一个切面的通知方法"
pointcut-ref = "对那个目标进行切入增强"
returning = "返回值要和参数内的名称一致"
如果遇到带有参数的方法,在对应标签中的method添加该方法名后再加上(),()里面填写给参数的全限定名称。