Spring_Aop的理解与学习

Spring_Aop的理解与学习

八月 19, 2019

面向切面编程

• Spring 的传统AOP的自动代理

– 基于Bean名称的自动代理
– 基于切面信息的自动代理

Aop采取横向抽取机制,取代了传统代码纵向继承体系重复性代码(性能监视,事务管理,安全检查,缓存)。 


Aop使用纯java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码

AOP相关术语

•Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指
的是方法,因为spring只支持方法类型的连接点.

•Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义.

•Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.
通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)

•Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下,

•Introduction可以在运行期为类动态地添加一些方法或Field.

AOP底层实现原理:

1.JDK动态代理(只能对实现接口的类实现代理)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MyJdkProxy implements InvocationHandler {
private UserDao userDao;

public MyJdkProxy(UserDao userDao){
this.userDao=userDao;
}

public Object createProxy(){
//jdk动态代理 对实现接口的类实现代理
//newProxyInstance的三个参数:类加载器,实现的接口,InvocationHandler接口
Object proxy = Proxy.newProxyInstance(userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(), this);
return proxy;
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("save".equals(method.getName())){
System.out.println("权限校验通过。。。");
return method.invoke(userDao,args);
}
return method.invoke(userDao,args);
}
}

2.使用cglib生成代理(底层生成一个类继承目标类,解决一个类没有实现接口而生成代理的问题)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class MyCglibProxy implements MethodInterceptor {
private ProductDao productDao;

public MyCglibProxy(ProductDao productDao){
this.productDao=productDao;
}

//编写方法创建代理
public Object createProxy(){
//1.创建核心类
Enhancer enhancer = new Enhancer();
//2.设置父类
enhancer.setSuperclass(productDao.getClass());
//3.设置回调
enhancer.setCallback(this);
//4.生成代理
Object proxy = enhancer.create();
return proxy;
}

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
if("save".equals(method.getName())){
System.out.println("权限校验。。。。");
methodProxy.invokeSuper(proxy,args);
}
return methodProxy.invokeSuper(proxy,args);
}
}

代理知识点总结

Spring Aop的底层是通过jdk动态代理或CGLib动态代理技术,为目标Bean执行横向织入

1.若目标对象实现若干接口,Spring使用jdk的Proxy类代理
2.若目标对象没有实现任何接口,Spring使用CGLib库生成目标对象的子类

程序中应优先对接口创建代理,便于程序解耦维护
Spring只支持方法连接点,不提供属性连接点

Spring通知类型:

1.前置通知:在目标方法执行前实施增强
2.后置通知:在目标方法执行后实施增强
3.环绕通知:在目标方法执行的前和后实施增强
4.异常抛出通知:在方法抛出异常之后实施增强

SpringAop切面类型

1.(一般切面)Advisor:代表一般切面,advice本身就是一个切面,对目标所有方法进行拦截
2.PointcutAdvisor:代表具有切点的切面,可以指定拦截目标类哪些方法

对所有的方法进行增强

1
2
3
4
5
6
7
8
9
<!--Spring的Aop产生代理对象-->
<bean id="studentDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--配置目标类-->
<property name="target" ref="studentDao"></property>
<!--实现的接口-->
<property name="proxyInterfaces" value="com.hairui.aop.demo3.StudentDao"></property>
<!--采用拦截的名称-->
<property name="interceptorNames" value="myBeforeAdvice"/>
</bean>

对某个类的某个方法进行增强

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!--配置目标类============-->
<bean id="customerDao" class="com.hairui.aop.demo4.CustomerDao"/>

<!--配置通知============== -->
<bean id="myAroundAdvice" class="com.hairui.aop.demo4.MyAroundAdvice"/>

<!--一般的切面是使用通知作为切面的,因为要对目标类的某个方法进行增强就需要配置一个带有切入点的切面-->
<bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!--pattern中配置正则表达式:.任意字符 *任意次数 -->
<!--<property name="pattern" value=".*save.*"/>-->
<property name="patterns" value=".*save.*,.*delete.*"/>
<property name="advice" ref="myAroundAdvice"/>
</bean>

<!-- 配置产生代理 -->
<bean id="customerDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="customerDao"/>
<property name="proxyTargetClass" value="true"/>
<property name="interceptorNames" value="myAdvisor"/>
</bean>

基于Bean名称的自动代理

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--配置目标类-->
<bean id="studentDao" class="com.hairui.aop.demo5.StudentDaoImpl"/>
<bean id="customerDao" class="com.hairui.aop.demo5.CustomerDao"/>

<!-- 配置增强-->
<bean id="myBeforeAdvice" class="com.hairui.aop.demo5.MyBeforeAdvice"/>
<bean id="myAroundAdvice" class="com.hairui.aop.demo5.MyAroundAdvice"/>

<!--配置自动代理-->
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="*Dao"/>
<property name="interceptorNames" value="myBeforeAdvice"/>
</bean>

基于切面信息产生代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!--配置目标类-->
<bean id="studentDao" class="com.hairui.aop.demo6.StudentDaoImpl"/>
<bean id="customerDao" class="com.hairui.aop.demo6.CustomerDao"/>

<!-- 配置增强-->
<bean id="myBeforeAdvice" class="com.hairui.aop.demo6.MyBeforeAdvice"/>
<bean id="myAroundAdvice" class="com.hairui.aop.demo6.MyAroundAdvice"/>

<!--配置切面-->
<bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="pattern" value="com\.hairui\.aop\.demo6\.CustomerDao\.save"/>
<property name="advice" ref="myAroundAdvice"/>
</bean>

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>