SpringAOP知识梳理
1.什么是AOP:
将公共的交叉业务逻辑(如添加日志、权限校验、性能统计,事务处理,异常处理等)提取出来封装为切面,不改变原有的代码情况下,在适当的时候(编译期,运行期)动态加入到代码中。(指扩展功能不修改源代码,将功能代码从业务逻辑代码中分离出来)
(编译期:改变class文件。运行期:在调用业务前先调用切面)2.AOP好处:
1.封装为切面的过程使我们的代码模块化.(任务分配简单。 让代码的复用率变高(减少维护的成本))。
2.更好的满足了开闭原则。(不改变原有的代码动态的添加新功能)。3.AOP术语:
- 切面 Aspect 交叉的业务逻辑(规则)(是切入点和通知(引介)的结合)
- 通知 Advice 切面的具体实现(所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
- 连接点 JoinPoint 位置信息(可以理解为接口)切点 Pointcut 定义具体的位置信息 (理解为连接点的实现)(类里面可以被增强的方法,这些方法称为连接点)
- Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义
- 代理 Proxy AOP的实现原理(一个类被AOP织入增强后,就产生一个结果代理类)
- 目标对象 Target 给哪个类添加功能,哪个类就是目标对象(代理的目标对象(要增强的类))
- 织入 Weaving 将通知添加到目标对象的过程(是把增强应用到目标的过程,把advice 应用到 target的过程)
- 引入 Intrduction 动态的给类添加功能(引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field)
原理:代理模式
静态代理:自己编写创建代理类,然后再进行编译,在程序运行前,代理类的.class文件就已经存在了。
动态代理:在实现阶段不用关心代理谁,而在运行阶段(通过反射机制)才指定代理哪一个对象。
①JDK动态代理:
只能对实现了接口的类生成代理,而不是针对类,该目标类型实现的接口都将被代理。原理是通过在运行期间创建一个接口的实现类来完成对目标对象的代理。步骤如下:
1. 定义一个实现接口InvocationHandler的类
2. 通过构造函数,注入被代理类
3. 实现invoke( Object proxy, Method method, Object[] args)方法
4. 在主函数中获得被代理类的类加载器
5. 使用Proxy.newProxyInstance( )产生一个代理对象
6. 通过代理对象调用各种方法
Proxy.newProxyInstance(
classLoader, //目标类的类加载器
interfaces, //目标类的实现接口列表
h, //交叉业务逻辑
)
InvocationHandler
invoke(Object proxy, //代理类的实例
Method method,//代理方法
Object[] args //代理方法的参数列表
)
示例:
LoginServiceI loginServiceI =(LoginServiceI) Proxy.newProxyInstance(
LoginServiceImpl.class.getClassLoader(),//目标类的类加载器
LoginServiceImpl.class.getInterfaces(), // 目标类的实现接口列表
newInvocationHandler(){ // 交叉业务逻辑
//invoke参数:代理的实例(可理解为loginServiceI使用时可忽略),目标类代理方法,代理方法参数列表
public Objectinvoke(Object proxy,Method method,Object[]args) throws Throwable {
System.out.println(method.getName()+"开始执行:"+new Date());//交叉业务逻辑
//交叉业务逻辑执行结束调用目标类的方法:方法.invoke(方法所在对象,方法参数);类似:对象.方法(参数);
return method.invoke(new LoginServiceImpl(),args);
}
});//该方法变成目标类的统一入口,每个方法调用前都会进入该方法。
②Cglib动态代理:采用的继承方式/或实现方式
针对类实现代理,对是否实现接口无要求。原理是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以被代理的类或方法最好不要声明为final类型。步骤如下:
1. 定义一个实现了MethodInterceptor接口的类
2. 实现其intercept()方法,在其中调用proxy.invokeSuper( )
示例:
publicclass LogCglibAop implements MethodInterceptor{
public ObjectgetProxy(Class<?> clazz) {
Enhancerenhancer =newEnhancer();
if(clazz.getInterfaces() !=null && clazz.getInterfaces().length > 0) {
returnenhancer.create(clazz, clazz.getInterfaces(),this);
}
returnenhancer.create(clazz,this);
}
//目标对象,目标对象原代理方法,方法参数,spring生成的方法代理,此时原方法已无效
public Objectintercept(Object target, Method method, Object[] args,MethodProxymethodProxy)throws Throwable {
System.out.println(method.getName()+"执行:" +new Date());
//调用父类原生态的方法
returnmethodProxy.invokeSuper(target, args);
}
}
5.使用Spring-AOP步骤:处理方式的选择:
Spring-aop默认采用的JDK动态代理,如果没有实现接口,它采用的就是Cglib技术,也就是说如果你的类没有实现接口,并且被final修饰,那么这个类就不能使用aop技术。
1. 如果目标对象实现了接口,默认情况下回采用JDK的动态代理实现AOP,也可以强制使用cglib实现AOP
2. 如果目标对象没有实现接口,必须采用cglib库,Spring会自动在JDK动态代理和cglib之间转换
1.导入jar
IOC jar包: spring-beans-4.2.5.RELEASE.jar
AOP jar包:spring-aop-4.2.5.RELEASE.jar
spring-context-4.2.5.RELEASE.jar spring-aspects-4.2.5.RELEASE.jar
spring-core-4.2.5.RELEASE.jar 核心包 aopalliance.jar aop编程思想包
spring-expression-4.2.5.RELEASE.jar aspectjweaver-1.6.10.jar AspectJ表达式配置切点包
spring-instrument-4.2.5.RELEASE.jar
第三方jar包:commons-logging-1.2.jar cglib-nodep-2.2.jar
2. 通知类型 实现接口
前置通知:在方法之前执行 MethodBeforeAdvice
后置通知:在方法之后执行 AfterReturningAdvice
异常通知:方法出现异常执行 ThrowsAdvice
环绕通知:在方法之前和之后执行 org.aopalliance.intercept.MethodInterceptor
3.编写通知
①前置通知://实现MethodBeforeAdvice接口
publicclass LoginAdviceimplements MethodBeforeAdvice {
//before(目标对象的代理方法,方法参数列表,目标对象)-->对象.方法(参数)
publicvoid before(Methodmethod, Object[] args, Object target)throws Throwable {
}System.out.println(method.getName()+newDate()); //只负责交叉业务逻辑
}
②后置通知://实现AfterReturningAdvice接口
publicclass GoodByeAdviceimplements AfterReturningAdvice {
//afterReturning(方法的返回值,目标对象的代理方法,方法参数列表,目标对象);
publicvoidafterReturning(Object returnValue,Method method,Object[] args, Object target)throws Throwable {
}System.out.println(method.getName()+returnValue);//调用业务逻辑代码
}
③异常通知:
publicclass ExceptionAdviceimplements ThrowsAdvice {
publicvoidafterThrowing(Exception ex) {
System.out.println(ex);
}
publicvoid afterThrowing(LoginExceptionex) {
System.out.println(ex);
}
publicvoid afterThrowing(Methodmethod, Object[] args, Object target,Exception ex) {
System.out.println(method.getName()+"args:"+Arrays.toString(args)+ex);
}
}
④环绕通知://实现MethodInterceptor接口
publicclass TimeAdviceimplements MethodInterceptor{
public Object invoke(MethodInvocationinvocation)throws Throwable {
}Method method =invocation.getMethod();//代理方法
Object target =invocation.getThis(); //目标对象
Object[] args =invocation.getArguments();//代理方法参数列表
//调用业务逻辑代码
Object returnValue =invocation.proceed();
System.out.println(method.getName()+"花费时间:"+(endTime-beginTime));
return returnValue;
}
4.织入(四种织如方式:①②基础,③④常用)(重点)
①采用FactoryBean(实现类ProxyFactoryBean)
<!--目标对象-->
<bean id="loginServiceTarget" class="aop04.Impl.LoginServiceImpl"></bean>
<!--通知(负责交叉业务逻辑)(前置,后置,环绕)-->
<bean id="loginAdvice" class="aop04.advice.LoginAdvice"></bean>
<bean id="goodByeAdvice" class="aop04.advice.GoodByeAdvice"></bean>
<bean id="timeAdvice" class="aop04.advice.TimeAdvice"></bean>
<!--采用FactoryBean接口的ProxyFactoryBean实现类织入-->
<bean id="loginServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--注入目标对象-->
<property name="target" ref="loginServiceTarget"></property>
<!--注入目标对象的实现接口列表-->
<property name="proxyInterfaces">
<list>
<value>aop04.LoginServiceI</value>
</list>
</property>
<!--注入交叉业务逻辑-->
<property name="interceptorNames">
<list>
<value>loginAdvice</value>
<value>goodByeAdvice</value>
<value>timeAdvice</value>
</list>
</property>
</bean>
②采用后处理器:自动代理:(适用条件:两个目标类要添加的功能相同,使用一个配置在一起(如给两个类添加事物))
org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator后处理器。
<!--自动代理-->
<!--目标类-->
<bean id="loginService" class="aop9.impl.LoginServiceImpl"></bean>
<bean id="userService" class="aop9.impl.UserServiceImpl"></bean>
<!--通知-->
<bean id="transactionAdvice" class="aop9.advice.TransactionAdvice">
<property name="transactionManager" ref="transactionManager"></property>
</bean>
<bean id="transactionManager" class="aop9.transaction.impl.TransactionManager"></bean>
<!--使用BeanNameAutoProxyCreator自动代理,该类已实现BeanPostProcess接口,原理是后处理器-->
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>*Service</value>
</property></list>
<property name="interceptorNames">
<list>
<value>transactionAdvice</value>
</list>
</property>
</bean>
自定义Pointcut和Advisor步骤:(Advisor = pointcut+advice)
1.自定义Pointcut需要实现接口 Pointcut
public ClassFiltergetClassFilter(){} //匹配类的类型
public MethodMatchergetMethodMatcher(){}//匹配方法
MethodMatcher 执行流程如下图:
matches(..)
matches()
isRuntime
2.整合pointcut和advice ===advisor
自定义一个Advisor需要实现接口PointcutAdvisor采用依赖注入的方式将advice和pointcut注入。
3.织入:将advice替换为advisor。
预定义的advisor:org.springframework.aop.support。
NameMatchMethodPointcut
NameMatchMethodPointcutAdvisor
DefaultIntroductionAdvisor
③采用命名空间的方式:(原理仍是后处理器)
1.采用命名空间的标签替代了bean的配置。
2.采用AspectJ表达式替代了pointcut的配置。
3.advice不需要实现任何接口。(低侵入性)
AspectJ书写:within(包名.类名) 表示匹配该类中所有的方法
<aop:before> 前置通知
<aop:after-returning> 后置通知
<aop:after-throwing 异常通知
<aop:around> 环绕通知
通知的写法:
publicvoid methodName(){} //既可以当成前置通知,又可以为后置通知同时还可以为异常通知
publicvoid methodName(JoinPointjoinPoint){}//既可以当成前置通知,又可以为后置通知同时还可以为异常通知
publicvoid methodName(JoinPointjoinpoint, Object returnValue){}//只能为后置通知
publicvoid methodName(JoinPointjoinpoint, Exception ex){}//只能为异常通知
环绕通知:
1.需要有一个Object类型返回值.
2.需要有一个参数ProceedingJoinPoint.
3.需要抛出一个异常Throwable
如:
public Object time(ProceedingJoinPointjoinPoint) throws Throwable {
Object target = joinPoint.getThis();
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
long startTime = System.currentTimeMillis();
Object returnValue = joinPoint.proceed();
long endTime = System.currentTimeMillis();
System.out.println(methodName+"cost "+(endTime-startTime)+"ms.");
return returnValue;
}
示例:
<bean id="loginService" class="aop10.impl.LoginServiceImpl"></bean> //目标类
<bean id="logAdvice" class="aop10.advice.LogAdvice"></bean> //通知
<!--织入-->
<aop:config>
<!--AspectJ表达式within,表示*ServiceImpl内所有方法均代理-->
<aop:pointcut expression="within(aop10.impl.*ServiceImpl)" id="pc"/>
//execution(方法的返回值 类.方法名 方法的参数)
<aop:pointcut expression="execution(*log*(..,java.lang.String))" id="pc1"/>
<aop:aspect ref="logAdvice">//切面
<aop:before method="log1" pointcut-ref="pc1"/>method指目标类中的代理方法 //前置通知
<aop:after-returning method="afterReturning" pointcut-ref="pc" returning="returnValue"/> //后置通知
<aop:after-throwing method="afterThrowing" pointcut-ref="pc" throwing="ex"/>//异常通知
<aop:around method="time" pointcut-ref="pc"/>//环绕通知
</aop:aspect>
</aop:config>
转载于:https://my.oschina.net/u/3676262/blog/1552891