Spring学习之第三篇

AOP技术
使用jdk提供的动态代理技术,可以实现对横向性切点关注.实现对目标对象的方法拦截,做出相应的处理.动态代理请参见前面的博文.使用jdk提供的代理类,要求目标对象要实现相应的接口才行。

但在实际应用中,很多类其实并没有实现接口。那么我们又如何来实现拦截技术呢?
可以使用CGlib包提供的代理来实现。
注:CGLIB可以生成目标类的子类,并重写父类非final修饰符的方法。
示例代码如下:

public class CGLIBProxy implements MethodInterceptor{
        private Object targetObject;//代理的目标对象
        public Object createProxyInstance(Object targetObject){
                this.targetObject = targetObject;
                Enhancer enhancer = new Enhancer();//该类用于生成代理对象
`               enhancer.setSuperclass(this.targetObject.getClass());//设置父类
                enhancer.setCallback(this);//设置回调对象为本身
                return enhancer.create();//返回代理对象
        }

        public Object intercept(Object proxy,Method method,Object[] args,MethodProxy methodProxy)throws Throwable{
                Object result;//环绕通知
                //....preAdvice()--->前置通知
                try{
                        result = methodProxy.invoke(this.targetObject,args);
                        //....afterAdvice()--->后置通知
                }catch(RuntimeException e){
                        //exceptionAdvice()--->异常通知
                }finally{
                        //finallyAdvice()--->最终通知
                }
                return result;
        }
}

 

AOP中的概念:
Aspect(切面):指横切性关注点的抽象即为切面,它与类相识,只是两者的关注点不一样,类是对物体特征的抽象,而切面是横切性关注点的抽象。

Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点,实际上joinpoint还可以是field或类构造器。

Pointcut(切入点):所谓切入点是指我们要对那些joinpoint进行拦截的定义。匹配连接点的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时)。切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法。

Advice(通知):所谓通知是指拦截到joinpoint之后所要做的事情就是通知。通知分为前置通知,后置通知,异常通知,最终通知,环绕通知。

Target(目标对象):代理的目标对象

Weave(织入):指将aspects应用到target对象并导致proxy对象创建的过程称为织入。

Introduction(引入):在不修饰类代码的前提下,Introduction可以在运行期为类动态地添加一些方法或field。

使用Spring进行面向切面(AOP)编程
要进行AOP编程,首先我们要在spring的配置文件中引入AOP命名空间:

<?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-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd ">
<aop:aspectj-autoproxy/>
....//当然必须把bean和拦截器bean在配置文件中指定,交由spring容器进行管理
....
</beans>

注:蓝色部分启动对@Aspect注解的支持

spring提供了两种切面使用方式,实际工作中我们可以选用其中一种:
★基于XML配置方式进行AOP开发。
★基于注解方式进行AOP开发。

基于注解的方式声明切面:使用到了AspectJ的支持。
示例:
@Aspect
public class LogPrint{
@Pointcut("execution( * cn.jianchen.service.impl..* .*(..))")    //蓝色的*号表示拦截的业务方法的返回值可以为任意类型,可以指定为具体的类型,如java.lang.String。浅绿色部分表示包名。后面跟了两个点号,表示下面的子包。后面紧跟着的星号表示匹配任意的类,也可以使用具体的类名。在后面的*号表示方法名,可以指定具体的类的方法名,用星号表示拦截任意方法。后面的括号表示方法的参数。其中的两点表示参数可有可无。
        private void anyMethod(){}//声明了一个切入点

@Before("anyMethod() && args(userName)") //定义前置通知。这里还附加上了方法参数的限制,也就是说只会对具有唯一一个字符串类型参数的方法进行执行前置通知的拦截。同时获得传递进来的参数
        public void daAccessCheck(String userName){
        }
@AfterReturning( pointcut="anyMethod()",returning="revalue")//定义后置通知,同时会把业务方法返回结果传到revalue中。业务方法返回参数类型需要和通知方法中的参数类型一致。
        public void doReturnCheck(String revalue){
        }

@AfterThrowing (pointcut="anyMethod()",throwing="ex")//定义异常通知
        public void doExceptionAction(Exception ex){
        }

@After ("anyMethod()")//定义最终通知
public void doReleaseAction(){
        }

@Around ("anyMethod()")//环绕通知
public Object doBasicProfiling(ProceedingJoinPoint pjp){ //环绕通知的固定声明,返回类型和参数类型不可以被修改,其他部分不要求
                return pjp.proceed();//必须调用该方法,会导致先执行后面的切面,切面都执行完毕后,才执行业务bean的方法。
        }
}

注: 实现了环绕通知,完全可以代替使用上面的四种通知。只需要在pjp.proceed()方法前后加上相应的代码,便可以实现前置通知,后置通知,异常通知,最终通知。

基于XML配置方式声明切面
<bean id="personService" class="cn.jianchen.service.PersonServiceBean"/>
<bean id="log" class="cn.jianchen.service.LogPrint"/> //具体的拦截器
<aop:config>
    <aop:aspect id="myAspect" ref="log" > //定义一个切面
         <aop:pointcut id="mycut" expression="execution(* cn.jianchen.service.PersonServiceBean.*(..))"/>
         <aop:before pointcut-ref="mycut" method="doAccessCheck"/>
         <aop:after-returning pointcut-ref="mycut" method="doReturnCheck"/>
         <aop:after-throwing pointcut-ref="mycut" method="doExceptionAction"/>
         <aop:after pointcut-ref="mycut" method="doReleaseAction"/>
         <aop:around pointcut-ref="mycut" method="doBasicProfiling"/>
    </aop:aspect>
</aop:config>

切入点表达式的书写:
expression="execution(* cn.jianchen.service.PersonServiceBean.*(..))"
返回值类型:可以用expression="execution(!void cn.jianchen.service.PersonServiceBean.*(..))"来表示排除返回值类型为void的方法,即不拦截此类方法。
拦截的方法参数expression="execution(* cn.jianchen.service.PersonServiceBean.*(String,.. ))"表示拦截方法的第一个参数类型为String,后面的参数无所谓的方法。
返回值类型:expression="execution(java.lang.String cn.jianchen.service.PersonServiceBean.*(..))"表示只拦截返回为string类型的方法。

spring的AOP机制:
如果满足拦截条件的类实现了接口。则spring使用jdk提供的动态代理创建代理对象实现aop。
如果满足拦截条件的类未实现接口。则spring使用cglib提供的方法实现代理的对象的创建,实现aop。

你可能感兴趣的:(spring,AOP,jdk,编程,bean)