第二章 Spring AOP

一.AOP?

 

AOP:面向切向,采用横向抽取机制,取代传统的纵向继承

AOP是一种面向切面的思想,但是我们平常说的spring使用了AOP,实际上说的是spring实现AOP思想的底层原理,而底层原理就是使用动态代理来增强某个方法。所以平常说AOP技术实际上就是指通过动态代理来对方法进行增强。

 

    举个例子:如果你想加强一个类中写好的方法,一般来说我们用继承来写,但是随着子类过多,那么重写方法就变得麻烦了,

         所以现在我们用AOP,将要增强的方法提取到一个类中,然后将需要增强的方法通过代理类将其方法增强

                                                                           

 

传统的纵向继承

 

 

 

 二.动态代理的两个方式

1.JDK动态管理 (接口+实现类)                       2.cglib代理(实现类)

 

下面是每个方式的实例

1.JDK动态管理(必须要有接口)

                                                      

2.目标类:

                                                     

3.切面类

                                                       

 

 

 4.代理类

 

package com.zlj;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * jdk代理类
 * @author 邹龄晋
 *
 */
public class JdkProxy implements InvocationHandler{
    //目标接口
    private UserDao userDao;
    //创建代理方法
    public Object createProxy(UserDao userDao){
        this.userDao = userDao;
        //1.类加载器
        ClassLoader classLoader = JdkProxy.class.getClassLoader();
        //2.被代理对象实现所有的接口
        Class [] clazz = userDao.getClass().getInterfaces();
        //3.使用代理类,进行增强,返回的是代理后的对象
        return Proxy.newProxyInstance(classLoader, clazz, this);
    }
    
    /**
     * 所有动态代理类的方法调用都会在invoke方法中进行
     * proxy是代理后的对象
     * method将要被执行的方法信息(反射)
     * args执行方法时需要的参数
     */

    public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {        
        //声明切面
        MyAspect myAspect = new MyAspect();
        //前增强
        myAspect.check_Permissions();
        //在目标类上调用方法并且传入参数
        Object obj = method.invoke(userDao, args);
        //后增强
        myAspect.log();
        return obj;
    }
}

 5.测试类

 

 

 

2.CGLIB代理

JDK代理使用前提示必须要有接口有一定的局限性,但是CGLIB是不需要的接口的

package com.zlj.cglib;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;


public class CglibProxy implements MethodInterceptor{
    
    //代理方法
    public Object createProxy (Object target){
        //创建一个动态类的对象
        Enhancer enhancer = new Enhancer();
        //确定需要增强的类.设置器父类
        enhancer.setSuperclass(target.getClass());
        //添加回调函数
        enhancer.setCallback(this);
        return enhancer.create();
    }
    
    /**
     * proxy cglib根据指定父类生成的代理对象
     * method拦截的方法
     * args拦截方法的参数数组
     * methodProxy方法的代理对象,用于执行父类的对象
     */
    
    public Object intercept(Object proxy, Method method, Object[] args,
            MethodProxy methodProxy) throws Throwable {
        //创建切面对象
        MyAspect myAspect = new MyAspect();
        //前增强
        myAspect.check_Permissions();
        //目标的执行方法
        Object obj = methodProxy.invokeSuper(proxy, args);
        //后增强
        myAspect.log();
        
        return obj;
    }
    

}

 

三.SpringAOP中的术语

总结:AOP代理就是由AOP框架动态生成的一个对象,该对象可以作为目标对象使用.

 

四.Spring如何实现AOP

下面为方法一:

1.导入环境

                                                                                                  

1.写切面类

 

package com.zlj.factorybean;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

//切面类:也就是增强&通知
public class MyAspect implements MethodInterceptor{
    public Object invoke(MethodInvocation mi) throws Throwable {
        pre();
        //执行目标方法
        Object obj = mi.proceed();
        next();
        return obj;
    }
    public void pre(){
        System.out.println("前");
    }
    public void next(){
        System.out.println("后");
    }

}

 

 

 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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <!-- 1.目标类 -->
    <bean id="userDao" class="com.zlj.jdk.UserDaoImpl"/>
    <!-- 2.切面类 -->
    <bean id="myAspect" class="com.zlj.factorybean.MyAspect"/>
    <!-- 3.使用Spring代理工厂定义一个代理对象 -->
    <bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--3.1指定代理实现的接口  -->
        <property  name="proxyInterfaces" value="com.zlj.jdk.UserDao"/>
        <!--3.2指定目标的对象 -->
        <property  name="target" ref="userDao"/>
        <!--3.3指定切面,植入环绕通知 -->
        <property  name="interceptorNames" value="myAspect"/>
        <!--3.4指定代理方式,true:使用cglib,false(默认):使用jdk动态代理 -->
        <property  name="proxyTargetClass" value="true"/>
    </bean>

</beans>

3.测试类

public class Test {
    public static void main(String[] args) {
        String xmlPath="ApplicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(xmlPath);
        UserDao userDao = (UserDao)ac.getBean("userDaoProxy");
        userDao.addUser();
        userDao.deleteUser();
    }
}

 

 4.结果

方式二:基于XML声明式AspectJ开发(比较好用,上面的简化)

1.先导入下面这个jar包

                                                                                                

2.

                                                                                                     

切面类

package com.zlj.aspectj.xml;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

/**
 * 切面类,在此处中编写通知
 * @author lenovo
 *
 */
public class MyAspect {
    //前置通知
    public void myBefore(JoinPoint j){
        System.out.println("前置通知:模拟执行权限检查...目标类是: "+j.getTarget()+" 被增强的目标方法  "+j.getSignature().getName());
    }
    //后置通知
    public void myAfterReturning(JoinPoint j){
        System.out.println("后置通知:模拟记录日志...被植入增强处理的目标方法 : "+j.getSignature().getName());
    }
    
    //环绕通知:ProceedingJoinPoint是Joinpoint的子接口,表示可执行的目标方法
    public Object myAround(ProceedingJoinPoint p)throws Throwable{
        //开始
        System.out.println("环绕通知开始");
        //执行当前目标方法
        Object obj = p.proceed();
        //结束
        System.out.println("环绕通知结束");
        return obj;
    }
    
    //异常通知
    public void myThrow(JoinPoint j,Throwable e){
        System.out.println("异常通知:出错了"+e.getMessage());
    }
    
    //最终通知
    public void myAfter(){
        System.out.println("最终通知,模拟方法结束,释放资源");
    }

}

配置文件

头文件记住加这个

 

<?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-3.0.xsd 
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
    <!-- 1.目标类 -->
    <bean id="userDao" class="com.zlj.jdk.UserDaoImpl"/>
    <!-- 2.切面类 -->
    <bean id="myAspect" class="com.zlj.aspectj.xml.MyAspect"/>
    <!-- 3.aop编程 -->
    <aop:config>
        <!-- 配置切面 -->
        <aop:aspect ref="myAspect">
        <!-- 3.1配置切面点,及需要加强的方法 -->
         <aop:pointcut expression="execution(* com.zlj.jdk.*.*(..))" id="myPointcut"/>
            <!-- 关联通知Advice和切入点Pointcut -->
            <aop:before method="myBefore" pointcut-ref="myPointcut"/>
        
            <aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut" returning="returnVal"/>
        
            <aop:around method="myAround" pointcut-ref="myPointcut"/>
            <!-- 若不发生异常不执行,throwing属性用于通知第二个参数的名称,类型为throwable -->
            <aop:after-throwing method="myThrow" pointcut-ref="myPointcut" throwing="e"/>
            <!-- 最终通知:无论程序发生任何事情,都将执行 -->
            <aop:after method="myAfter" pointcut-ref="myPointcut"/>
        
        </aop:aspect>
            

    </aop:config>
    

</beans>

 测试代码

 

 

 

方式三:基于注解的声明式AspectJ开发(非常方便,一定要掌握)

 在方式二的基础上进行修改切面类

package com.zlj.aspectj.annotation;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class MyAspect {
    
    //定义切入点表达式
    @Pointcut("execution(* com.zlj.jdk.*.*(..))")
    //使用一个返回值为void,方法体为空的方法来命名切入点
    private void myPointCut(){}
    //前置通知
    @Before("myPointCut()")
    public void myBefore(JoinPoint j){
        System.out.println("前置通知:模拟执行权限检查...目标类是: "+j.getTarget()+" 被增强的目标方法  "+j.getSignature().getName());
    }
    //后置通知
    @AfterReturning("myPointCut()")
    public void myAfterReturning(JoinPoint j){
        System.out.println("后置通知:模拟记录日志...被植入增强处理的目标方法 : "+j.getSignature().getName());
    }
    
    //环绕通知:ProceedingJoinPoint是Joinpoint的子接口,表示可执行的目标方法
    @Around("myPointCut()")
    public Object myAround(ProceedingJoinPoint p)throws Throwable{
        //开始
        System.out.println("环绕通知开始");
        //执行当前目标方法
        Object obj = p.proceed();
        //结束
        System.out.println("环绕通知结束");
        return obj;
    }
    
    //异常通知
    @AfterThrowing(value="myPointCut()",throwing="e")
    public void myThrow(JoinPoint j,Throwable e){
        System.out.println("异常通知:出错了"+e.getMessage());
    }
    
    //最终通知
    @After("myPointCut()")
    public void myAfter(){
        System.out.println("最终通知,模拟方法结束,释放资源");
    }

}

 

写完后写配置文件发现,配置文件要写的好少,感觉第三个好简单....

最后结果也相同

 

方法一和方法二让我更好的理解了AOP如何工作,方法三让我们今后方便使用AOP

 

posted @ 2018-08-17 13:26  leonard丶zou  阅读(159)  评论(0编辑  收藏  举报