Spring学习笔记之AOP

一、什么是AOP

AOP (Aspect Oriented Programing) 面向切面编程

AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)。
Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码。

AspecJ是一个基于Java语言的AOP框架,Spring2.0开始,Spring AOP引入对Aspect的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入。

AOP的底层原理

就是代理机制:

* JDK的动态代理,对实现了接口的类生成代理.

有两种代理模式:

1.JDK动态代理:对实现了接口的类生成代理

2.CGLib代理机制:对类生成代理

AOP中的术语

Joinpoint(连接点):连接点是指那些可以被拦截到的点(方法)。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点.
Pointcut(切入点):切入点是指我们要对哪些Joinpoint进行拦截的定义.需要被增强的方法.
Advice(通知/增强):通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能).
Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
Target(目标对象):代理的目标对象.
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程.
                spring采用动态代理织入,而AspectJ采用编译期织入和类装在期织入.
Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类.

Aspect(切面): 是切入点和通知(引介)的结合.

二、AOP的底层实现

1.JDK动态代理

编写TranscationInvocationHandler类实现InvocationHandler接口,实现其invoke方法

@Component
public class TranscationInvocationHandler implements InvocationHandler {
    private TranscationManager tran;
    private UserService service;

    public TranscationInvocationHandler(TranscationManager tran, UserService service) {
        this.tran = tran;
        this.service = service;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //如果是addUser方法才增强
        if("addUser".equals(method.getName())){
            try {
                //开启事务
                tran.begin();
                //执行被增强的方法
                Object o = method.invoke(service,args);
                //提交事务
                tran.commit();
                //返回代理对象
                return o;
            }catch (Exception e){
                //出异常则回滚事务
                tran.rollback();
                e.printStackTrace();
            }
        }
        //其他方法不进行增强,直接执行
        return method.invoke(service,args);
    }
}
测试类:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext4.xml")
public class JDKProxyTest {
    @Autowired
    @Qualifier("userService")
    private UserService service;
    @Autowired
    @Qualifier("txManager")
    private TranscationManager tran;
    @Test
    public void test(){
        service = (UserService) Proxy.newProxyInstance(service.getClass().getClassLoader(),
                service.getClass().getInterfaces(),new TranscationInvocationHandler(tran,service));
//        service = (UserService) Proxy.newProxyInstance(service.getClass().getClassLoader(),
//                new Class[]{UserService.class},new TranscationInvocationHandler(tran,service));
        service.addUser();
//        service.updateUser();
    }
}

2.CGLIB动态代理

编写TranscationInterceptor类实现MethodInterceptor接口,并实现其intercept方法

public class TranscationInterceptor implements MethodInterceptor {
    private UserService service;
    private TranscationManager txManager;

    public TranscationInterceptor(UserService service, TranscationManager txManager) {
        this.service = service;
        this.txManager = txManager;
    }

    public UserService creatService(){
        //使用CGLib生成代理
        //1.创建核心类
        Enhancer enhancer = new Enhancer();
        //2.设置父类
        enhancer.setSuperclass(UserService.class);
        //设置回调
        enhancer.setCallback(this);
        //创建代理
        return (UserService) enhancer.create();
    }

    public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //拦截指定方法
        if ("addUser".equals(method.getName())){
            try {
                txManager.begin();
                Object invoke = methodProxy.invoke(service, objects);
                txManager.commit();
                return invoke;
            }catch (Exception e){
                txManager.rollback();
                e.printStackTrace();
            }
        }
        return methodProxy.invoke(service,objects);
    }
}

测试类:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext5.xml")
public class CGLibProxyTest {
    @Autowired
    @Qualifier("userService")
    private UserService service;
    @Autowired
    @Qualifier("txManager")
    private TranscationManager tran;

    @Test
    public void test(){
        UserService proxyService = new TranscationInterceptor(service,tran).creatService();
        proxyService.addUser();
//        proxyService.updateUser();
    }
}

三、Spring中的AOP

1.Spring的传统AOP

AOP:不是由Spring定义.AOP联盟的组织定义.
Spring中的通知:(增强代码)
前置通知 org.springframework.aop.MethodBeforeAdvice    在目标方法执行前实施增强
后置通知 org.springframework.aop.AfterReturningAdvice    在目标方法执行后实施增强
环绕通知 org.aopalliance.intercept.MethodInterceptor        在目标方法执行前后实施增强
异常抛出通知 org.springframework.aop.ThrowsAdvice          在方法抛出异常后实施增强

引介通知 org.springframework.aop.IntroductionInterceptor 在目标类中添加一些新的方法和属性

自定义通知可以通过实现上述接口。

2.Spring中AOP开发

  • 针对所有方法增强:(不带有切点的切面)

第一步:导入相应jar包.
* spring-aop-3.2.0.RELEASE.jar
* com.springsource.org.aopalliance-1.0.0.jar

第二步:编写被代理对象:
* UserService接口
* UserServiceImpl实现类

第三步:编写增强的代码:
public class MyBeforeAdvice implements MethodBeforeAdvice{
/**
* method:执行的方法
* args:参数
* target:目标对象
*/
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("前置增强...");
}
}

第四步:生成代理:(配置生成代理:)
* Spring生成代理基于ProxyFactoryBean类.底层自动选择使用JDK的动态代理还是CGLIB的代理.
* 属性:
target : 代理的目标对象
proxyInterfaces : 代理要实现的接口
如果多个接口可以使用以下格式赋值
<list>
    <value></value>
    ....
</list>
proxyTargetClass : 是否对类代理而不是接口,设置为true时,使用CGLib代理
interceptorNames : 需要织入目标的Advice
singleton : 返回代理是否为单实例,默认为单例

optimize : 当设置为true时,强制使用CGLib

自定义的前置通知类MyBeforeAdvice实现MethodBeforeAdvice接口

@Component
public class MyBeforeAdvice implements MethodBeforeAdvice {
    @Resource(name = "txManager")
    private TranscationManager txmanager;

    /*前置增强的具体内容*/
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        txmanager.begin();
    }
}

我的具体配置:

<!--配置启用注解-->
    <context:annotation-config/>
    <bean id="txManager" class="com.wl.spring_aop.aop_demo1.TranscationManager"/>
    <bean id="userDao" class="com.wl.spring_aop.aop_demo1.UserDaoImpl"/>
    <!--定义实现类(需要增强的类)-->
    <bean id="userService" class="com.wl.spring_aop.aop_demo1.UserServiceImpl"/>
    <!--定义增强-->
    <bean id="myBeforeAdvice" class="com.wl.spring_aop.aop_demo1.MyBeforeAdvice"/>
    
    <!--Spring支持配置自动生成代理,使用Spring自带的ProxyFactoryBean-->
    <bean id="userServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--设置代理的目标对象target-->
        <property name="target" ref="userService"/>
        <!--设置代理要实现的接口,这里就是UserService接口的全限定名-->
        <property name="proxyInterfaces" value="com.wl.spring_aop.aop_demo1.UserService"/>
        <!--设置增强器的id名称,注意使用value而不是ref-->
        <property name="interceptorNames" value="myBeforeAdvice"/>
    </bean>

测试类中直接使用配置好的service代理对象即可:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext6.xml")
public class AopTest {
    @Autowired
    @Qualifier("userServiceProxy")
    private UserService serviceProxy;

    @Test
    public void test(){
        serviceProxy.addUser();
        serviceProxy.updateUser();
    }
}
  • 针对指定方法增强:(带有切点的切面)

PointcutAdvisor 接口:配置切面和切点时需要用到
DefaultPointcutAdvisor 最常用的切面类型,它可以通过任意Pointcut和Advice 组合定义切面
RegexpMethodPointcutAdvisor 构造正则表达式切点切面。

自定义一个MyAroundAdvice类实现org.aopalliance.intercept.MethodInterceptor接口

@Component
public class MyAroundAdvice implements MethodInterceptor {
    @Resource(name = "txManager")
    private TranscationManager txmanager;
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        //前置通知
        System.out.print("前置通知:");
        txmanager.begin();
        //执行目标方法
        Object result = methodInvocation.proceed();
        //后置通知
        System.out.print("后置通知:");
        txmanager.commit();
        return result;
    }
}

配置文件

<!--配置启用注解-->
    <context:annotation-config/>
    <bean id="txManager" class="com.wl.spring_aop.aop_demo2.TranscationManager"/>
    <bean id="userDao" class="com.wl.spring_aop.aop_demo2.UserDaoImpl"/>
    <!--定义实现类(需要增强的类)-->
    <bean id="userService" class="com.wl.spring_aop.aop_demo2.UserServiceImpl"/>
    <!--定义增强-->
    <bean id="myAroundAdvice" class="com.wl.spring_aop.aop_demo2.MyAroundAdvice"/>

    <!--定义切点和切面-->
    <bean id="myPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
    <!--定义正则表达式,规定哪些方法被拦截-->
        <!--.表示任意字符 *表示任意个数-->
        <!--所有方法增强-->
        <!--<property name="pattern" value=".*"/>-->
        <!--指定add方法增强-->
        <!--<property name="pattern" value=".*add.*"/>-->
        <property name="pattern" value="com\.wl\.spring_aop\.aop_demo2\.UserServiceImpl\.addUser"/>
        <!--应用增强-->
        <property name="advice" ref="myAroundAdvice"/>
    </bean>
    <!--Spring支持配置自动生成代理-->
    <bean id="userServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--设置代理的目标对象target-->
        <property name="target" ref="userService"/>
        <!--设置代理要实现的接口,这里就是UserService接口的全限定名-->
        <property name="proxyInterfaces" value="com.wl.spring_aop.aop_demo2.UserService"/>
        <!--设置增强器的id名称,注意使用value而不是ref,使用自定义的切面-->
        <property name="interceptorNames" value="myPointcutAdvisor"/>
    </bean>

测试同上。

3.自动代理

前面的案例中,每个代理都是通过ProxyFactoryBean织入切面代理,在实际开发中,非常多的Bean每个都配置ProxyFactoryBean开发维护量巨大。

使用Spring自动创建代理(基于后处理Bean.在Bean创建的过程中完成的增强.生成Bean就是代理.)
BeanNameAutoProxyCreator                                  根据Bean名称创建代理 
DefaultAdvisorAutoProxyCreator                            根据Advisor本身包含信息创建代理

AnnotationAwareAspectJAutoProxyCreator           基于Bean中的AspectJ 注解进行自动代理

  • 使用BeanNameAutoProxyCreator自动代理
配置文件:
<!--Spring AOP自动代理-->
    <!--配置启用注解-->
    <context:annotation-config/>
    <bean id="txManager" class="com.wl.spring_aop.aop_autoproxy.TranscationManager"/>
    <bean id="userDao" class="com.wl.spring_aop.aop_autoproxy.UserDaoImpl"/>
    <!--定义实现类(需要增强的类)-->
    <bean id="userService" class="com.wl.spring_aop.aop_autoproxy.UserServiceImpl"/>
    <!--定义增强-->
    <bean id="myBeforeAdvice" class="com.wl.spring_aop.aop_autoproxy.MyBeforeAdvice"/>
    <!--定义增强-->
    <bean id="myAroundAdvice" class="com.wl.spring_aop.aop_autoproxy.MyAroundAdvice"/>

    <!--使用BeanNameAutoProxyCreator自动代理,基于后处理Bean,不需要配置id-->
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <property name="beanNames" value="*Service"/>
        <property name="interceptorNames" value="myBeforeAdvice"/>
    </bean>
  • 使用DefaultAdvisorAutoProxyCreator自动代理

配置文件:

<!--配置启用注解-->
    <context:annotation-config/>
    <bean id="txManager" class="com.wl.spring_aop.aop_autoproxy.TranscationManager"/>
    <bean id="userDao" class="com.wl.spring_aop.aop_autoproxy.UserDaoImpl"/>
    <!--定义实现类(需要增强的类)-->
    <bean id="userService" class="com.wl.spring_aop.aop_autoproxy.UserServiceImpl"/>
    <!--定义增强-->
    <bean id="myBeforeAdvice" class="com.wl.spring_aop.aop_autoproxy.MyBeforeAdvice"/>
    <!--定义增强-->
    <bean id="myAroundAdvice" class="com.wl.spring_aop.aop_autoproxy.MyAroundAdvice"/>
    
    <!--定义切点和切面-->
    <bean id="myPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="pattern" value=".*add.*"/>
        <property name="advice" ref="myAroundAdvice"/>
    </bean>
    <!--使用DefaultAdvisorAutoProxyCreator自动生成代理-->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

4.基于ProxyFactoryBean的代理与Spring自动代理的区别

    proxyFactoryBean的代理:先有代理对象,然后将代理对象传入到代理类中生成代理。

    自动代理:基于后处理Bean,在生成Bean的过程中就产生了代理对象,生成的Bean已经是代理对象。

5.Spring中AspectJ的AOP(重要)

AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。
AspectJ是一个基于Java语言的AOP框架
Spring2.0以后新增了对AspectJ切点表达式支持
@AspectJ 是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面
新版本Spring框架,建议使用AspectJ方式来开发AOP

AspectJ表达式:
* 语法:execution(表达式)

execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)


* execution(“* cn.wl.spring3.demo1.dao.*(..)”) ---只检索当前包
* execution(“* cn.wl.spring3.demo1.dao..*(..)”) ---检索包及当前包的子包.
* execution(* cn.wl.dao.GenericDAO+.*(..)) ---检索GenericDAO及子类

AspectJ增强:
@Before 前置通知,相当于BeforeAdvice

* 就在方法之前执行.没有办法阻止目标方法执行的.

@AfterReturning 后置通知,相当于AfterReturningAdvice

* 后置通知,获得方法返回值.

@Around 环绕通知,相当于MethodInterceptor

* 在可以方法之前和之后来执行的,而且可以阻止目标方法的执行.

@AfterThrowing抛出通知,相当于ThrowAdvice
@After 最终final通知,不管是否异常,该通知都会执行
@DeclareParents 引介通知,相当于IntroductionInterceptor
实现方式:

  • 基于注解
第一步:引入相应jar包.
* aspectj依赖aop环境.
* spring-aspects-4.3.13.RELEASE.jar
* com.springsource.org.aspectj.weaver-1.8.13.RELEASE.jar
第二步:编写被增强的类:

UserServiceImpl

第三步:使用AspectJ注解形式:

自定义切面类MyAspect:

@Aspect
public class MyAspect {
    @Autowired
    @Qualifier("txManager")
    private TranscationManager txManager;

    @Before("execution(* com.wl.spring_aop.aop_aspectj.UserServiceImpl.addUser(..))")
    public void before(){
        txManager.begin();
    }

    @AfterReturning(value = "execution(* com.wl.spring_aop.aop_aspectj.UserServiceImpl.addUser(..))",returning = "returnVal")
    /*returning = returnVal用来接收被代理方法的返回值*/
    public void afterReturn(Object returnVal){
        System.out.println(returnVal);
        txManager.commit();
    }

    @Around(value = "execution(* com.wl.spring_aop.aop_aspectj.UserServiceImpl.updateUser(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕通知前...");
        Object proceed = joinPoint.proceed();
        System.out.println("环绕通知后...");
        return proceed;
    }
    @AfterThrowing(value = "execution(* com.wl.spring_aop.aop_aspectj.UserServiceImpl.addUser(..))",throwing = "ex")
    /*throwing = "ex"用来接收被代理方法的异常信息*/
    public void afterThrow(Throwable ex){
        System.out.println("出现异常了..."+ex.getMessage());
        txManager.rollback();
    }

    @After(value = "execution(* com.wl.spring_aop.aop_aspectj.UserServiceImpl.addUser(..))")
    public void after(){
        System.out.println("最终通知");
    }
}

切点的注解:

    //自定义切点
    @Pointcut("execution(* com.wl.spring_aop.aop_aspectj.UserServiceImpl.addUser(..))")
    private void myPointcut(){}

    @Before("myPointcut()")
    public void before(){
        txManager.begin();
    }

第四步:创建配置文件applicationContext.xml:
<!--配置启用注解-->
    <context:annotation-config/>
    <bean id="txManager" class="com.wl.spring_aop.aop_aspectj.TranscationManager"/>
    <bean id="userDao" class="com.wl.spring_aop.aop_aspectj.UserDaoImpl"/>
    <!--定义实现类(需要增强的类)-->
    <bean id="userService" class="com.wl.spring_aop.aop_aspectj.UserServiceImpl"/>
    <!--定义增强-->
    <bean id="myAspect" class="com.wl.spring_aop.aop_aspectj.MyAspect"/>

    <!--配置aspectj自动代理,底层就是AnnotationAwareAspectJAutoProxyCreator-->
    <aop:aspectj-autoproxy/>
  • 基于XML配置

使用XML配置的方式,不需要在自定义通知类的方法上加上注解,而是在配置文件中将切点和通知组合为一个切面。

<!--定义实现类(需要增强的类)-->
    <bean id="userService" class="com.wl.spring_aop.aop_aspectj_xml.UserServiceImpl"/>
    <!--定义通知-->
    <bean id="myAspect" class="com.wl.spring_aop.aop_aspectj_xml.MyAspect"/>

    <aop:config>
        <!--定义切点-->
        <aop:pointcut id="myPointcut" expression="execution(* com.wl.spring_aop.aop_aspectj_xml.UserServiceImpl.add*(..))"/>
        <aop:aspect ref="myAspect">
            <!--前置通知-->
            <aop:before method="before" pointcut-ref="myPointcut"/>
            <!--后置通知,returning是返回值-->
            <aop:after-returning method="afterReturn" pointcut-ref="myPointcut" returning="returnVal"/>
            <!--环绕通知-->
            <aop:around method="around" pointcut-ref="myPointcut" />
            <!--抛出通知-->
            <aop:after-throwing method="afterThrow" pointcut-ref="myPointcut" throwing="e"/>
            <!--最终通知-->
            <aop:after method="after" pointcut-ref="myPointcut"/>
        </aop:aspect>
    </aop:config>

注意后置通知可以接收返回值,抛出通知可以接收异常:

//后置通知
    public void afterReturn(Object returnVal){
        System.out.print("后置通知...返回值是:"+returnVal);
        txManager.commit();
    }
    //环绕通知
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.print("环绕通知前...");
        Object proceed = joinPoint.proceed();
        System.out.print("环绕通知后...");
        return proceed;
    }
    //抛出通知
    public void afterThrow(Throwable e){
        System.out.print("前置通知...异常信息是:"+e.getMessage());
        txManager.rollback();
    }
小问题:Advisor和Aspect的区别?

Advisor是Spring传统意义上的切面,只支持一个切点对应一个通知。

而Aspect可以支持多个切点和多个通知的组合。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值