框架学习之Spring的AOP技术

一.AOP概述

1.AOP为Aspect Oriented Programming的缩写,意为:面向切面编程;是一种编程范式,隶属于软工范畴,指导开发者如何组织程序结构;AOP最早由AOP联盟的组织提出的,制定了一套规范;通过预编译方式运行期动态代理实现程序功能的统一维护的一种技术;利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率

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

3.可以在不修改源代码的前提下,对程序进行增强

二.AOP的底层实现

1. Srping框架的AOP技术底层采用的代理技术,代理的方式提供了两种
        1. 基于JDK的动态代理
            * 必须是面向接口的,只有实现了具体接口的类才能生成代理对象
        
        2. 基于CGLIB动态代理
            * 对于没有实现了接口的类,也可以产生代理,产生这个类的子类的方式

2. Spring的传统AOP中根据类是否实现接口,来采用不同的代理方式
        1. 如果实现类接口,使用JDK动态代理完成AOP
        2. 如果没有实现接口,采用CGLIB动态代理完成AOP

三.JDK的动态代理

使用Proxy类来生成代理对象的一些代码如下:

public class MyProxyUtils {
	public static UserDao getProxy(final UserDao dao) {
	    // 使用Proxy类生成代理对象
		UserDao proxy = (UserDao) Proxy.newProxyInstance(dao.getClass().getClassLoader(),
						dao.getClass().getInterfaces(), new InvocationHandler() {
							
        // 代理对象方法一执行,invoke方法就会执行一次
	//参数1 就是那个生成的代理对象
    	//参数2  代理对象所调用的方法对象
    	//参数3  代理对象所调用的方法对象中的参数对象数组
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		if("save".equals(method.getName())){
			System.out.println("记录日志...");
			// 开启事务
		}
			// 提交事务
			// 让dao类的save或者update方法正常的执行下去
			return method.invoke(dao, args); 
		}
	});
	// 返回代理对象
	return proxy;
	}
}

四.CGLIB的代理技术

1. 引入CBLIB的开发包
        * 如果想使用CGLIB的技术来生成代理对象,那么需要引入CGLIB的开发的jar包,在Spring框架核心包中已经引入了CGLIB的开发包了。所以直接引入Spring核心开发包即可!

2. 编写相关的代码

public static OrderDaoImpl getProxy(){
	// 创建CGLIB核心的类
    Enhancer enhancer = new Enhancer();
    // 设置父类
	enhancer.setSuperclass(OrderDaoImpl.class);
	// 设置回调函数
	enhancer.setCallback(new MethodInterceptor() {
	@Override
	public Object intercept(Object obj, Method method, Object[] args,
    MethodProxy methodProxy) throws Throwable {
		if("save".equals(method.getName())){
			// 记录日志
			System.out.println("记录日志了...");
		}
		return methodProxy.invokeSuper(obj, args);
	}
	});
	// 生成代理对象
	OrderDaoImpl proxy = (OrderDaoImpl) enhancer.create();
	return proxy;
}

五.AOP的相关术语

  1. Joinpoint(连接点)    -- 所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
    2. Pointcut(切入点)    -- 所谓切入点是指我们要对哪些Joinpoint进行拦截的定义 ,也就是我们想对那个方法做增强,这个方法叫做切入点
    3. Advice(通知/增强)    -- 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
    4. Introduction(引介)    -- 引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field
    5. Target(目标对象)    -- 代理的目标对象
    6. Weaving(织入)    -- 是指把增强应用到目标对象来创建新的代理对象的过程,简单来说也就是生成代理对象的这个过程,称之为织入。
    7. Proxy(代理)    -- 一个类被AOP织入增强后,就产生一个结果代理类
    8. Aspect(切面)        -- 是切入点和通知的结合,切面是以后咱们自己来编写和配置的

六.切入点的表达式

1. 再配置切入点的时候,需要定义表达式,重点的格式如下:execution(public * *(..)),具体展开如下:
        * 切入点表达式的格式如下:
            * execution([修饰符] 返回值类型 包名.类名.方法名(参数))
        
        * 修饰符可以省略不写,不是必须要出现的。 public 可以省略不写
        * 返回值类型是不能省略不写的,根据你的方法来编写返回值。可以使用 * 代替。  例如: public * com.luo.BookDaoImpl.save()
        * 包名例如:com.luo.BookDaoImpl
            * 首先com是不能省略不写的,但是可以使用 * 代替  例如:com.*.BookDaoImpl
            * 中间的包名可以使用 * 号代替   例如:public void *.*.BookDaoImpl.save()
            * 如果想省略中间的包名可以使用 *..*  例如: public void *..*.BookDaoImpl.save()
        
        * 类名也可以使用 * 号代替,也有类似的写法:*DaoImpl  意思是这个类以DaoImp结尾  例如:public void com.luo.*DaoImpl.save()
        * 方法也可以使用 * 号代替 
            例如:public void com.luo.*DaoImpl.*() 通配所有方法名   
            例如: public void com.luo.*DaoImpl.save*()  通配方法以save开头
        * 参数如果是一个参数可以使用 * 号代替,如果想代表任意参数使用 ..
            例如:public void com.luo.*DaoImpl.save(*)  统配一个参数
            例如:public void com.luo.*DaoImpl.save(..)  统配任意个数的参数

七.AOP的通知类型

   1. 前置通知
        * 在目标类的方法执行之前执行。
        * 配置文件信息:<aop:after method="before" pointcut-ref="myPointcut3"/>
        * 应用:可以对方法的参数来做校验
    
    2. 最终通知
        * 在目标类的方法执行之后执行,如果程序出现了异常,最终通知也会执行。
        * 在配置文件中编写具体的配置:<aop:after method="after" pointcut-ref="myPointcut3"/>
        * 应用:例如像释放资源
    
    3. 后置通知
        * 方法正常执行后的通知。        
        * 在配置文件中编写具体的配置:<aop:after-returning method="afterReturning" pointcut-ref="myPointcut2"/>
        * 应用:可以修改方法的返回值
    
    4. 异常抛出通知
        * 在抛出异常后通知
        * 在配置文件中编写具体的配置:<aop:after-throwing method="afterThorwing" pointcut-ref="myPointcut3"/>    
        * 应用:包装异常的信息
    
    5. 环绕通知
        * 方法的执行前后执行。
        * 在配置文件中编写具体的配置:<aop:around method="around" pointcut-ref="myPointcut2"/>
        * 要注意:目标的方法默认不执行,需要使用ProceedingJoinPoint对来让目标对象的方法执行。
八.Spring框架的AOP技术(AspectJ的XML方式)

1. 步骤一:创建JavaWEB项目,引入具体的开发的jar包
        * 先引入Spring框架开发的基本开发包
            com.springsource.org.apache.commons.logging-1.1.1.jar
            com.springsource.org.apache.log4j-1.2.15.jar
            spring-beans-4.2.4.RELEASE.jar
            spring-context-4.2.4.RELEASE.jar
            spring-core-4.2.4.RELEASE.jar
            spring-expression-4.2.4.RELEASE.jar
        * 再引入Spring框架的AOP的开发包
            * spring的传统AOP的开发的包
                * spring-aop-4.2.4.RELEASE.jar
                * com.springsource.org.aopalliance-1.0.0.jar  这个在依赖包中找
            
            * aspectJ的开发包
                * com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar 这个在依赖包中找
                * spring-aspects-4.2.4.RELEASE.jar
                 *想要做测试的话可以引入Spring测试的包
                spring-test-4.2.4.RELEASE.jar
    
    2. 步骤二:创建Spring的配置文件,引入具体的AOP的schema约束
        <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.xsd
                http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    3. 步骤三:创建包结构,编写具体的接口和实现类
        
        * com.luo.demo2
            * CustomerDao   -- 接口
            * CustomerDaoImpl  -- 实现类
    
    4. 步骤四:将目标类配置到Spring中
        <bean id="customerDao" class="com.luo.demo2.CustomerDaoImpl"/>

5. 步骤五:定义切面类
        public class MyAspectXml {
            // 定义通知
            public void log(){
                System.out.println("记录日志...");
            }
        }    
    6. 步骤六:在配置文件中定义切面类
        <bean id="myAspectXml" class="com.luo.demo2.MyAspectXml"/>

7. 步骤七:在配置文件中完成aop的配置
        <aop:config>
            <!-- 引入切面类 -->
            <aop:aspect ref="myAspectXml">
                <!-- 定义通知类型:切面类的方法和切入点的表达式 -->
                <aop:before method="log" pointcut="execution(public void com.luo.demo2.CustomerDaoImpl.save())"/>
            </aop:aspect>
        </aop:config>

8. 完成测试
        @RunWith(SpringJUnit4ClassRunner.class)
        @ContextConfiguration("classpath:applicationContext.xml")
        public class Demo3 {
            @Resource(name="customerDao")  //Java的这个注解,没有开启注解扫描,也是可以用的
            private CustomerDao customerDao;
            @Test
            public void run1(){
                customerDao.save();
                customerDao.update();
                customerDao.delete();
            }
        }

九.Spring框架的AOP技术(注解方式)

1. 步骤一:创建JavaWEB项目,引入具体的开发的jar包
        * 先引入Spring框架开发的基本开发包
            com.springsource.org.apache.commons.logging-1.1.1.jar
            com.springsource.org.apache.log4j-1.2.15.jar
            spring-beans-4.2.4.RELEASE.jar
            spring-context-4.2.4.RELEASE.jar
            spring-core-4.2.4.RELEASE.jar
            spring-expression-4.2.4.RELEASE.jar
        * 再引入Spring框架的AOP的开发包
            * spring的传统AOP的开发的包
                * spring-aop-4.2.4.RELEASE.jar
                * com.springsource.org.aopalliance-1.0.0.jar
            
            * aspectJ的开发包
                * com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
                * spring-aspects-4.2.4.RELEASE.jar
    
    2. 步骤二:创建Spring的配置文件,引入具体的AOP的schema约束
        <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.xsd
                http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
            
        </beans>

3. 步骤三:创建包结构,编写具体的接口和实现类
        
        * com.luo.demo2
            * CustomerDao   -- 接口
            * CustomerDaoImpl  -- 实现类
    
    4. 步骤四:将目标类配置到Spring中
        <bean id="customerDao" class="com.luo.demo2.CustomerDaoImpl"/>

5. 步骤五:定义切面类
        * 添加切面和通知的注解
            * @Aspect -- 定义切面类的注解
            
            * 通知类型(注解的参数是切入点的表达式)
                * @Before                -- 前置通知
                * @AfterReturing        -- 后置通知
                * @Around                -- 环绕通知
                * @After                -- 最终通知
                * @AfterThrowing        -- 异常抛出通知
        
        * 具体的代码如下
            @Aspect
            public class MyAspectAnno {
                @Before(value="execution(public void com.luo.demo2.CustomerDaoImpl.save())")
                public void log(){
                    System.out.println("记录日志...");
                }
            }

    6. 步骤六:在配置文件中定义切面类
        <bean id="myAspectAnno" class="com.luo.demo2.MyAspectAnno"/>    
    7. 步骤七:在配置文件中开启自动代理
        <aop:aspectj-autoproxy/> 放在最前面 
    8. 完成测试
        @RunWith(SpringJUnit4ClassRunner.class)
        @ContextConfiguration("classpath:applicationContext.xml")
        public class Demo1 {
            
            @Resource(name="customerDao")
            private CustomerDao customerDao;
            
            @Test
            public void run1(){
                customerDao.save();
                customerDao.update();
            }
        }

猜你喜欢

转载自blog.csdn.net/Luna_ll/article/details/81540208