Spring的AOP开发实战和疑问解析

Spring AOP  面向切面编程

什么是aop?

Spring把横切式的应用,比如事务,日志等。单独抽出来,声明给某些目标对象,然后这些被声明过得对象就具有了该横切式应用能力,或者说会执行横切应用。理解上可以参照filter和interceptor。

 

概念:

·        通知(Advice): 在切面的某个特定的连接点(Joinpoint)上执行的动作。通知有各种类型,其中包括“around”、“before”和“after”等通知。 通知的类型将在后面部分进行讨论。许多AOP框架,包括Spring,都是以拦截器做通知模型, 并维护一个以连接点为中心的拦截器链。

·        切入点(Pointcut): 匹配连接点(Joinpoint)的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时)。切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法。

 

Aop底层是怎么实现的呢,如何做到在拦截到某个业务类之后,执行通知的动作?

答案是采用代理模式

 

什么是代理:

为某个对象提供一个代理,以控制对这个对象的访问。代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。

 

示意图:

 

 

代理分类:静态代理和动态代理

 

 

 

Java静态代理

 

由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

所谓代理就是在方法前后加上执行代码,但不更改被代理的类本身的方法。

 

课堂举例:

 

 

动态代理

动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。

使用jdk的动态代理需要为被代理类设置接口。

 

方面代码:

1.      首先定义一个接口:

public interface IUserService {

    publicvoid addUser();

}

2.      定义一个目标类,实现这个接口

public class UserServiceImp implements IUserService {

 

         @Override

         publicvoid addUser() {

                   //TODOAuto-generated method stub

       System.out.println("添加一个用户成功!!!!");

         }

 

}

3.      定义切面代码

/**

 * 自定义的切面

 * @authorchidianwei

 *

 */

public class MyAspectHanlder implements InvocationHandler {

         private Object target;//被代理者,目标对象

        

 

         public Object getTarget() {

                   return target;

         }

 

 

         public void setTarget(Object target) {

                   this.target = target;

         }

 

 

         @Override

         public Object invoke(Object proxy, Method method, Object[] args)

                            throws Throwable {

                   // 横切时候做的事情

                   System.out.println("执行具体任务之前,额外做的事情,获取页面传过来的参数形成user对象");

//               反射机制执行targetmethod方法

                   Object obj=method.invoke(target, args);

                  

                   return obj;

         }

 

}

4.      定义测试类Test,动态生成代理类对象

public class Test {

 

         public static void main(String[] args) {

                   // 代理类刚开始并不是写死的,而是通过jdk的动态代理机制,根据反射自动生成的。

                   MyAspectHanlder in=new MyAspectHanlder();

                   in.setTarget(new UserServiceImp());//注入一个目标对象

                   IUserService proxy=(IUserService)Proxy.newProxyInstance(Test.class.getClassLoader(),

                                     new Class[]{IUserService.class},

                                     in);

                   proxy.addUser();

         }

 

}

 

 

 

 

 

 

jdk动态代理机制

要求被代理类必须实现一个接口,然后还要有一个处理类,来完成被代理类本来就要完成的业务。

Spring aop的配置使用

判断具体addUser等具体业务之前,用户是否登录?具体编程步骤:

1.       引入aop依赖的jar包

 

2.编写横切方面业务代码MyHanlder

 

@Component(value="securityHandler")

public class MyHandler  {

 

          public void checkSecurity(){

                    

                     System.out.println("check login or nothanlder....");

          }

}

 

2.配置文件中定义横切点piontcut和advice

 

<?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:context="http://www.springframework.org/schema/context"

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/context

http://www.springframework.org/schema/context/spring-context.xsd

http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd">

         <context:component-scanbase-package="sdibt.group"></context:component-scan>

         <aop:config>

         <aop:aspect id="securityAspect" ref="securityHandler">

         <!--add开头的方法

         <aop:pointcutid="addAddMethod" expression="execution(* add*(..))"/>

          -->

          <!--

         <aop:pointcutid="addAddMethod" expression="execution(*com.jmt.bean.*.*(..))"/>

          -->

         <aop:pointcutid="addAddMethod" expression="execution(* sdibt.group.action.*.add*(..)) ||execution(* sdibt.group.dao.*.add*(..))"/>

         <aop:before method="checkSecurity" pointcut-ref="addAddMethod"/>

         </aop:aspect>

         </aop:config>

</beans>

 

 

注意,如果是环绕通知,要求切面定义这样:

         publicvoidcheckSecurity(ProceedingJoinPoint p) throws Throwable{

                  

                   System.out.println("在此处检验用户是否已经登录....before...");

                   Object ret=p.proceed();

                   System.out.println("在此处检验用户是否已经登录...after....");

                    

         }

 

也可以直接使用注解方式:

1.      开发切面的代码

@Aspect

@Component(value="securityHandler")

public class MyHandler  {

          @Pointcut("execution(* sdibt.group.action.*.add*(..))|| execution(* sdibt.group.dao.*.add*(..))")

          private void addAddMethod(){}

          

      @Before("addAddMethod()")

          public void checkSecurity(){

                    

                     System.out.println("check login or nothanlder....");

          }

}

 

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:context="http://www.springframework.org/schema/context"

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/context

http://www.springframework.org/schema/context/spring-context.xsd

http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd">

         <context:component-scanbase-package="sdibt.group"></context:component-scan>

          <!-- 启用AspectJAnnotation的支持 -->      

         <aop:aspectj-autoproxy/>          

       

</beans>

 

如果想要在执行切面代码时获得切入点相关信息,代码如下:

 

 

Lijie

 

 

 

 

了解部分:

 

AOP 是 Spring 独有的概念吗?

不是,除了Spring AOP,AOP 常见的实现还有:

Aspectj

Guice AOP

Jboss AOP

 

AOP Alliance 是什么, 为什么 Spring AOP 需要 aopalliance.jar ?

 

AOP Alliance 是AOP的接口标准,定义了 AOP 中的基础概念(Advice、CutPoint、Advisor等),目标是为各种AOP实现提供统一的接口,本身并不是一种 AOP 的实现。

Spring AOP, GUICE等都采用了AOP Alliance中定义的接口,因而这些lib都需要依赖 aopalliance.jar。

Update: Spring 4.3之后内置了AOP Alliance接口,不再需要单独的aopalliance.jar。

 

Spring AOP 和 Aspectj 的区别?

Spring AOP采用动态代理的方式,在运行期生成代理类来实现AOP,不修改原类的实现;Aspectj 使用编译期字节码织入(weave)的方式,在编译的时候,直接修改类的字节码,把所定义的切面代码逻辑插入到目标类中。

Spring AOP可以对其它模块正常编译出的代码起作用,Aspectj 需要对其它模块使用acj重新编译

由于动态代理机制,Spring AOP对于直接调用类内部的其它方法无效,无法对定义为final的类生效。Aspectj没有这些限制

Spring AOP使用XML配置文件的方式定义切入点(CutPoint),Aspectj使用注解方式

注:Aspectj 除了编译期静态织入的方式之外,也支持加载时动态织入修改类的字节码。

 

Spring AOP 如何生成代理类?

Spring AOP使用JDK Proxy或者cglib实现代理类生成。对于有实现接口的类使用JDK Proxy,对于无接口的则是用cglib.通过

<aop:aspectj-autoproxyproxy-target-class="true"/>

指定proxy-target-class为true可强制使用cglib.

JDK Proxy 和 cglib 代理类生成什么区别?

JDK Proxy只适用于类实现了接口的情况,关系图:

Interface ----------> OriginClass

          |---------> ProxyClass

生成的代理类实现了原类的接口,但和原类没有继承关系.

cglib则是生成原来的子类,对于没有实现接口的情况也适用:

OriginClass --------> ProxyClass

cglib采用字节码生成的方式来在代理类中调用原类方法, JDK Proxy 则是使用反射调用,由于反射存在额外security check 的开销一集目前jvm jit对反射的内联支持不够好,JDK Proxy在性能上弱于cglib

 

Spring-aspects又是什么鬼?

因为Spring AOP XML配置文件定义的方式太繁琐遭到吐槽,所以spring从Aspectj中吸收了其定义AOP的方式,包括Aspectj Annotation和Aspectj-XML配置。然而其实现依然是动态代理的方式,与aspectj 字节码织入的方式不同。

为什么spring-aspects还需要 aspectjweaver.jar才能工作

Spring-aspects 实现XML配置解析和类似 Aspectj 注解方式的时候,借用了 aspectjweaver.jar 中定义的一些annotation 和 class,然而其并不使用 Aspectj 的字节码织入功能。

Spring-aspects不能把这些所需的类定义抄一份吗,这样就不需要aspectjweaver.jar了

他们可以,但是他们偏不这样做。

Spring 3.1 之前 spring-aspects 对 aspectjweaver 的依赖还是 optional 的,需要自己再添加依赖;Sprint 3.2 之后 依赖取消了 optional 设置,可以不用自己添加了。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值