‘壹’ 如何理解spring中的切面和过滤
1, 什么是AOP ?
面向方面/切面的编程:将程序中的交叉业务逻辑(公用代码)提取出来,称为切面,
将这些切面动态的织入到目标对胡搏象,生成一个新的代理对象的过程;
把程序划分为两部分:
1) 通用的交叉业务,公用的代码(日志,事务,安全);
2) 具体的业务逻辑处理代码;
将切面动态的织入到业务代码中;
面向方面/切面编程:将业务分解成切面与具体的业务逻辑模块,将切面透明的接入到业务逻辑模块中,形成一个完整的程序。比如:字符编码;日志;事务等等
2, AOP中的基本概念:
1)切面(Aspect): 指交叉业务逻辑的统称, 比如日志,事务,安全;
2)通知(Advice): 指切面的具体实现;
3)连接点(Joinpoint):指切面可以薯汪织入到(应用到)目标对象的位置(级别), 两个: 方法/属性
代理模式, 调用的是代理对象, 代理维护一个目标对象的属性;
调用方法之前, 先写日志; 再调用具体的实现方法;
调用属性之前, 拦截一下做处理,很少用;Spring目前不支持这个;
4)切入点(Pointcut):指通知(切面的具体实现)应用到哪些目标对象的哪些方法或者哪些属性上;
org.springframework.aop.Pointcut
Spring根据类名称及方法名称定义Pointcut,表示Advice织入至应用程序的时机;
5)引入(introction):指动态的给一个对象增加方法或者属性的过程;是一种特殊的通知;
6)织入(weaving):将切面(通知)应用到目标对象生成代理的过程;
7)代理对象(Proxy): 目标对象应用切面以后生成的代理(切面织入之后生成的新的代理对象);
8)目标对象(Target): 具体业务逻辑的实现(需要织入切面的对象);
3, Spring的AOP, 使用的是动态代理;
1) 代理的裤手祥两种方式:
静态代理:
针对每个具体类分别编写代理类;
针对一个接口编写一个代理类;
IRegister
RegisterService <--- RegisterProxy
目标对象 代理对象
IRegister ir; 调用代理的方法, 代理再去调用目标对象的方法;
IReport
ReportService <--- ReportProxy
目标对象 代理对象
每一个代理对象只为一个目标对象服务;
动态代理:
针对一个方面编写一个InvocationHandler,
然后借用JDK反射包中的Proxy类为各种接口动态生成相应的代理类
告诉一个类,我的接口是什么, 目标类是什么, 自动生成代理;
2) 动态代理例子:
public class MyProxyCreater implements java.lang.reflect.InvocationHandler {
private Object target;
public MyProxyCreater(Object target) {
super();
this.target = target;
}
public static Object createProxy(Object o){
//类加载器,实现的接口,代理生成的类
//根据三个参数创建一个代理对象;
//通过类加载器,借口,和实现类得到一个代理对象
//目标对象最少要实现一个接口
return
Proxy.newProxyInstance(o.getClass().getClassLoader(),
o.getClass().getInterfaces(),new MyProxyCreater(o));
}
//代理对象,目标方法,目标对象的参数
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object rst=null;
//加切面
System.out.println("loging...");
rst=method.invoke(target, args);
return rst;
}
}
spring思想:
1,将切面使用动态代理的方式动态地织入到目标对象(被代理对象)生成新的代理对象;
2,目标对象如果没有实现代理接口,spring采用CGLIB来生成代理对象,
该代理对象是目标对象的子类;
3,目标对象如果是final类,并且没有实现代理接口,就不能运用AOP了;
目标对象如果没有实现接口, spring采用CGLIB库来动态生成一个代理,
代理是目标对象的子类; 目标对象不能是final的;
Spring采用动态代理来实现AOP, 在运行时,spring将切面应用到目标对象生成动态代理,
客户端使用动态代理来调用;
AspectJ: 需要学习一些别的语言,
支持属性连接点; 可以在编译期生成代理; 需要把编译器重新写一遍;
难度比较大;
3) Advice :
切面的实现:
类型:
(1)org.springframework.aop.MethodBeforeAdvice
在方法调用之前,做处理。
不能够改变返回值
不能够改变目标方法的流程,也不能中断流程的处理过程(除非抛出异常)
before(目标方法,目标方法的参数,目标对象);
public void before(Method method, Object[] objectArray, Object object) throws Throwable
(2)org.springframework.aop.AfterReturningAdvice
在方法调用之后,做处理。
不能够改变返回值
不能够改变目标方法的流程,也不能中断流程的处理过程(除非抛出异常)
void afterReturning(Object returnValue, Method method, Object[] args, Object target)
(3)org.aopalliance.intercept.MethodInterceptor
aop联盟框架; 保证代码兼容别的aop框架;
拦截器, 通知,跟filter差不多;
在方法调用之前以及之后,做处理。
可以改变返回值,也可以改变流程。
public Object invoke(MethodInvocation methodInvocation) throws Throwable
(4)org.springframework.aop.ThrowsAdvice
在目标方法抛出异常后,会根据异常类型选择的做处理。
当该通知处理完异常后,会简单地将异常再次抛出给客户端调用。
public void afterThrowing(IllNameException e);
public void afterThrowing(IllAgeException e);
内置的创建代理类:
org.springframework.aop.framework.ProxyFactoryBean
proxyInterfaces,interceptorNames,target
作用:依照配置信息,将切面应用到目标对象,生成动态代理对象;
配置过程:
(1)配置目标对象,
(2)注入切面: 通知, Advisor, 拦截器
(3)利用ProxyFactoryBean将切面织入到目标对象,生成动态代理对象;
客户端调用的是代理的对象;
如果目标对象没有实现指定接口,不用配置ProxyInterfaces, (cglib会生成一个);
实现了接口,也可以不写的;
(4)客户端使用动态代理来访问目标对象的方法。
注: 在默认情况下,通知会应用到目标对象的所有方法之上。
编码过程: 目标对象--通知--代理--客户端调用;
例子: (aop3)四种通知 ;
<bean id="registerServiceTarget" class="aop3.RegisterServiceImpl"/>
<bean id="logingAdvice" class="aop3.LogingAdvice"/>
<bean id="welcomeAdvice" class="aop3.WelcomeAdvice"/>
<bean id="registerInterceptor" class="aop3.RegisterInterceptor"/>
<bean id="registerThrowingAdvice" class="aop3.RegisterThrowingAdvice"/>
<bean id="registerService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces"><!-- 目标对象实现的接口 -->
<value>aop3.IRegisterService</value>
</property>
<property name="target"><!-- 配置(指定)目标对象 -->
<ref bean="registerServiceTarget"/>
</property>
<property name="interceptorNames"><!-- 需要织入到目标对象的切面(通知,advisor,拦截器) -->
<list>
<value>logingAdvice</value>
<!--
<value>welcomeAdvice</value>
<value>registerInterceptor</value>
<value>registerThrowingAdvice</value>
-->
</list>
</property>
</bean>
练习: (aop4)在拦截器设置一个属性,记录方法超时的时间;
4) 切入点:
通知具体应用到哪些类的哪些方法之上;
自定义切入点:用来约束Advice
步骤:
1)实现org.springframework.aop.ClassFilter--->用来过滤类(哪些类可以应用切面)
如果返回值为true时, 则应用切面; 否则不应用切面;
对类进行过滤,看要不要将切面应用到该类上;
2)实现org.springframework.aop.MethodMatcher--->用来过滤方法(类中的哪些方法应用切面)
方法1: boolean matches(Method, Class);//
方法2: boolean isRunTime(); 返回值为真,该切入点是动态切入点;
否则是静态切入点; 方法3不会被执行;
方法3: boolean matches(Method, Class,Object[]);//这个方法可以动态的获得方法的信息;
解释:
静态切入点:
在目标方法被执行以前, 确定切面是否应用到目标方法之上;
根据目标方法的静态特征(方法所在的类名A, 方法名f1), 来决定;
一般用这个;
动态切入点:
在目标方法每一次执行,会依照方法的动态的特征来决定是否应用切面;
动态特征: 当前方法的参数值, 等等;
会影响性能很大, 很少使用;
3)实现org.springframework.aop.Pointcut
4)实现org.springframework.aop.PointcutAdvisor:将Pointcut与Advice结合到一起。
是一个特殊的通知,它包含了切入点在内;
注意:
在此可定义两个属性,并且通过配置文件来完成装配工作;
private Advice advice;//通知的实现;
private Pointcut pointcut;//通知的应用地点;
在配置文件中,将自定义的切入点与通知绑订到一起,成为Advisor;
5)利用ProxyFactoryBean将advisor织入到目标对象
‘贰’ 使用java语言,如何对一个类中的静态方法做切面编程
aop的事务代理机制最重要的放心是确定切入点,面,通知.具体看代码,下面是在spring中配置的我自己写的一个异常处理的aop作用类 ,该配置切入面在于在controller包下的所有类的所有注解为aspect的切面类,通液绝知类型为表示在目标方法之前切入,切入点为controller包下的所有类所有方法.至于楼主所说的静态方法对于事务机制应该没什么区别吧,只要用within方法一样可以的
<!-- 定义共同处理组件 -->
<bean id="loggerBean"
class="org.te.cloudnote.aspect.LoggerBean">
</bean>
<!-- 将loggerBean组件切入到Controller方法上 -->
<aop:config>
<斗蚂!-- 要切入哪个共同处理组件,ref指定共同组件id值 -->
<aop:aspect ref="loggerBean">
<!-- aop:before表示在目标方法之前切入,
method指定方法名;pointcut指定目标组件 -->
<aop:before method="logController"
pointcut="within(org.te.cloudnote.controller..*)"/>
</aop:aspect>
</aop:config>
之后这个bean所定义的 自定义类的代码如下 ,希望楼主给个采纳,如果问友喜欢,也可以给我个赞哦,摸摸大
package org.te.cloudnote.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/空埋埋/封装共同处理的组件
@Component//扫描,等价于<bean>定义
@Aspect//等价于<aop:aspect ref="">
public class LoggerBean {
//要在Controller.execute开始位置切入
//方法规则:public void 方法名(){...} (前置通知)
@Before("within(org.te.cloudnote.controller..*)")
//等价于<aop:before method="logController" pointcut="">
public void logController(){
System.out.println("进入Controller组件处理");
}
}
‘叁’ spring切面反射,JoinPoint怎么分辨基本数据类型和封装类型
可磨腊以通过getClass()获取神搭对象的类对象,然后通过isPrimitive()函数做判断。瞎瞎滑即
arg.getClass().isPrimitive()
‘肆’ 简述识别木材三个切面的方法 木材的横切、纵切、*切怎么识别
(一)横睁猛岁切面
良材1.与树干主轴(或木材纹理)垂直方向的切面.
良材2.在肉眼或放大镜下识别木材特别重要.
(二)纵切面
弦切1.弦切面,与木材纹理悉睁或生长轮正切与木射线垂直的切面.
弦切2.径切面,与木材纹理平行,与木材射线平行的切面;实际上径切面是弦切面通知洞过髓心的一个特殊的切面.
‘伍’ spring aop切面表达式详解及例子
切面类型
execution格式
通配符
例子
一、 execution :使用“ execution (方法表达式)”匹配方法执行;
二、 within :使用“ within (类型表达式)”匹配指定类型内的方法执行;
三、 this :使用“ this (类型全限定名)”匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口方法也可以匹配;注意this中使用的表达式必须是类型全限定名,不支持通配符;
四、 target :使用“ target (类型全限定名)”匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;注意target中使用的表达式必须是类型全限定名,不消档支持通配符;
五、 args :使用“ args (参数类型列表)”匹配当前执行的方法传入的参数为指定类型的执行方法;注意是匹配传入的参数类型,不是匹配方法签名的参数类型;参数类型列表中的参数必须是类型全限定名,通配符不支持;args属于动态切入点,这祥桥空种切入点开销非常大,非特殊情况最好不要使用;
六、 @within :使用“ @within (注解类型)”匹配所以持有谨瞎指定注解类型内的方法;注解类型也必须是全限定类型名;
七、 @target :使用“ @target (注解类型)”匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;注解类型也必须是全限定类型名;
八、 @args :使用“ @args (注解列表)”匹配当前执行的方法传入的参数持有指定注解的执行;注解类型也必须是全限定类型名;
九、 @annotation :使用“ @annotation (注解类型)”匹配当前执行方法持有指定注解的方法;注解类型也必须是全限定类型名;
十、 bean :使用“ bean (Bean id或名字通配符)”匹配特定名称的Bean对象的执行方法;Spring AOP扩展的,在AspectJ中无相应概念;
‘陆’ 如何获得被前置切面切入类的类名和方法名
在方法中添加一个参数就行了,然后掉用那个参数的方法,纯肆禅具体的看看这个:
@Around("execution(* cn.e.pdsu..*.*(..))")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
Class clazz = pjp.getTarget().getClass();
Logger logger = Logger.getLogger(clazz);
StringBuffer log = new StringBuffer();// 日志信息
log.append("执行 ").append(clazz).append("类的 ")
.append(pjp.getSignature().getName()).append("方法!");
logger.info(log);
Object retVal = null;
try {
retVal = pjp.proceed();
} catch (Exception e) {
logger.error(log);
logger.error(e.getMessage(), e.fillInStackTrace());// 记录异常信息
}
return retVal;
}
这个是环绕通知,雹逗要是按Lz的方法只需要改改参数就行了;
@Before("execution(* com.dlmu.aml..*Service.update*(..))")
public void logBeforeChange(JoinPoint pjp)//添加参数的地方,剩下的就和上边一样了
{
Class clazz = pjp.getTarget().getClass();//得到当前执行的类
pjp.getSignature().getName();//得到执行的方法
System.out.println("调用类名:"做尘+className+";调用方法名:"+methodName);
}
‘柒’ 被调用方法如何加切面
1、首先定义一个被调族液用方法的切面实现类做穗扮。
2、其次在Spring配置文件中配置切面。
3、最后通过SpringIoC容器获纯灶取要拦截的类的实例就可以加切面了。
‘捌’ spring切面Aspects
导入aop模块:spring-aspects
xml实现: 开启基于注解版的切面功能<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
1.通知方法
1.1 前置通知@Before
1.2 后置通知@After(目标睁渣方法运行结束后执行,【正常结束或异常结束都调用】)
1.3 返回通知@AfterReturning (目标方法正常返回后执行)
1.4 异常通知@AfterThrowing
1.5 环绕通知@Around (动态代理 ,手动推进悉稿悄目标方法运行[joinPoint.proceed])
2.定义业务逻辑类,在运行时执行通知方法
3.定义日志切面类,里面方法要动态感知逻辑类方法(给切面类方法标注敬厅何时何地运行通知方法)
4.将切面类和逻辑类都加入到容器,切面类加@Aspect注解
5.在配置类加@EnableAspectJAutoProxy开启基于注解版的切面功能
使用切面总结:
1.将业务逻辑组件和切面类都加在容器中,告诉spring哪个是切面类@Aspect
2.在切面类上每一个通知方法上标注通知注解,告诉Spring什么时候运行
3.开启aop模式@EnableAspectJAutoProxy
‘玖’ SpringBoot在切面中怎么查询修改前的数据
以下的问题就可以完美的解决了
自定姿隐义注解,切入点是controller的增、删、改方法,切面中获取到了参数、类名、方法名,在修改前要通过id查询数据库内容,怎么获取对应的service对象
非常简单:
1. 加一个Interceptor,
2.在onPreHandle中看看能不能处理
注:只是这样一来的话,就相当于又把请求拦迹者厅截了一嫌培遍