Spring应用学习——AOP

1. AOP

    1. AOP:即面向切面编程,采用横向抽取机制,取代了传统的继承体系的重复代码问题,以下图所示,性能监控、日志记录等代码围绕业务逻辑代码,而这部分代码是一个高度重复的代码,也就是在每个业务逻辑的代码中都会有相同的代码围绕业务逻辑代码,而AOP就是将这些重复代码抽取出java

    2. AOP实现原理:就是代理模式,主要有两种方式,分别是静态代理和动态代理,web

  • 静态代理:采用一些工具类对原来的类生成一个代理类,代理类以.class存在
  • 动态代理(重点,spring基于动态代理实现aop):在运行中,经过反射生成类的代理对象,在代理对象中对原来的对象进行加强。

    3. spring采用动态代理的技术实现包括:spring

  • 基于接口生成动态代理对象:使用jdk提供的反射机制实现,参考这篇博客,JDK动态代理实现详解
  • 基于类生成动态代理对象:经过继承实现,根据类生成一个子类(代理对象),在代理对象(子类)中对父类进行加强,参考这篇博客。实现

    cglib经过继承的形式来实现动态功能的代理 那么就没法避免一些继承的缺点,那就是没法代理final方法和没法代理final类,示例使用代码以下express

import java.lang.reflect.Method;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class CglibProxy implements MethodInterceptor  {

	//obj生成的代理对象
	//method对目标对象方法的引用
	//args目标对象方法调用参数
    //MethodProxy代理对象中对方法的引用
	@Override
	public Object intercept(Object obj, Method method, Object[] args,
			MethodProxy methodProxy) throws Throwable {
		//在方法以前加强,开启事务
        
		System.out.println("开启事务...");
		//执行父类方法
		Object result = methodProxy.invokeSuper(obj, args);
		//在方法以后加强,提交事务
		System.out.println("提交事务...");
		return result;
	}
}

import org.springframework.cglib.proxy.Enhancer;
public class CglibProxyFactory {
	/**
	 * 
	 * <p>Title: createCglibProxy</p>
	 * <p>Description: </p>
	 * @param clazz 父类的class,经过class操做字节码建立代理对象
	 * @return
	 */
	public static Object createCglibProxy(Class clazz){
		//代理对象,这里边有加强的代码
		CglibProxy cglibProxy = new CglibProxy();
		//建立加强器,须要设置父类及代理对象
		Enhancer enhancer = new Enhancer();
		//在加强器设置父类
		enhancer.setSuperclass(clazz);
		//在加强器设置代理对象(包括加强代码)
		enhancer.setCallback(cglibProxy);
		//建立一个代理对象
		return enhancer.create();
		
	}

}

 

    4. cglib与jdk实现动态代理的区别:jdk是基于接口生成代理对象,而cglib是基于类生成代理对象。spring底层使用jdk和cglib,若是原始对象实现了一个接口,spring使用jdk,不然 使用cglib生成代理。apache

    5. 相关术语:编程

  • Pointcut(切入点):肯定在哪一个方法上加强,一个类有多个切点
  • Advice(通知/加强):在切点上进行加强,包括:前置加强、后置加强、抛出异常加强
  • Target(目标对象):对目标对象进行加强,生成代理对象
  • Proxy(代理):对目标对象进行加强,生成代理对象
  • Weaving(织入):生成代理的时机(过程)
    • 动态代理织入,在运行期为目标类生成代理对象
    • 编译期织入
  • Aspect(切面): 包括切点和加强,面向哪一个切点进行加强(编程)。

2. SpringAOP的简单使用

    1. 搭建环境:经过Maven加入spring-webmvc.jar和aspectjweaver.jar包便可导入全部须要依赖的包spring-mvc

    2. AspectJ相关语法:mvc

  • 配置切点的语法:切点就是方法,因此必需要确认是那一个类的哪个方法,
    • 用法:匹配任意字符
           1. com.cloud_note.entity.*:该包下全部类,仅限于此包下,不包括子包
           2. * com.cloud_note.controller.user.UserLoginController.excute(..):即com.cloud_note.controller.user.UserLoginController类中的任意返回值的excute方法
    •  .. 用法:匹配子包或方法形参,匹配子包下的类要与*结合使用
               1. com.cloud_note.entity..*:匹配该包下全部子包的全部类
               2. com.cloud_note.controller.user.UserLoginController.excute(..):即com.cloud_note.controller.user.UserLoginController类中的全部excute方法,不管该方法的参数是什么
               3. com.cloud_note.entity.*.*(..):匹配该包下全部类的全部方法
    • 用法:按类型匹配指定类的全部类
               1. com.cloud_note.controller.user.UserLoginController+:指定匹配继承UserLoginController类的全部子类,包括自己
    • 切点语法:表达式包括两部分,即函数和参数,函数有execution、within、target、args,参数就是通配表达式,如
      • execution(com.entity..*)
      • within(com.entity..*)
      • target(某个类的全限定名):也就表示匹配该类中的全部方法,每一个方法都是切点,参数必须是类的全限定名
      • args(java.lang.String):表示以java.lang.String类型数据做为参数的方法所有都是切点,参数必须是类的全限定名
      • 与或非逻辑操做:execution、within、target、args直接能够进行与或非操做,用符号&&、||、!或者用字符and、or、not表示
  • 加强:AspectJ支持的加强类型有
    • Before :前置加强/通知,至关于BeforeAdvice
    • AfterReturning :后置加强/通知,至关于AfterReturningAdvice
    • Around :环绕加强/通知,至关于MethodInterceptor
    • AfterThrowing:抛出加强/通知,至关于ThrowAdvice
    • After:最终finally加强/通知,无论是否异常,该通知都会执行
public void save(){
	try{
	//前置加强
	调用目标对象方法执行
	//后置加强

	}catch(Exception ex){
		//抛出异常加强
	}finally{
		//最终finally加强
	}
}

    3. 在Spring中的AOP配置:ide

  • 使用xml文件配置
    ​
    <?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:jdbc="http://www.springframework.org/schema/jdbc"  
    	xmlns:jee="http://www.springframework.org/schema/jee" 
    	xmlns:tx="http://www.springframework.org/schema/tx"
    	xmlns:aop="http://www.springframework.org/schema/aop" 
    	xmlns:mvc="http://www.springframework.org/schema/mvc"
    	xmlns:util="http://www.springframework.org/schema/util"
    	xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    	xsi:schemaLocation="
    		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
    		http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
    		http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
    		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
    		http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
    		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
    		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
    		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd">
    	
    	<!--配置AOP中的加强类,即将LoggerBean中的方法做用到Controller组件的全部方法上-->
    	<bean id="loggerBean" class="LoggerBean"></bean>
    
    	<!--配置AOP-->
    	<aop:config>
    	<!--配置切面:切面包括切点和加强  -->
    		<aop:aspect ref="loggerBean"><!--ref属性就表示指定加强类-->
    		<!--配置切点  -->
    			<aop:pointcut expression="execution(* controller.user.*.*(..))" id="point"/>
    		<!--配置前置加强 ,加强就是 LoggerBean类中的logController方法-->	 
    			<aop:before method="logController" pointcut-ref="point"/>
    		<!-- 配置后置加强,加强就是 LoggerBean类中的outController方法-->
    			<aop:after-returning method="outController" pointcut-ref="point"/>
    		</aop:aspect>
    	</aop:config>
    
    	<!--开启注解扫描,只有被扫描进Spring容器的类才能配置AOP来加强代码-->
    	<context:component-scan base-package="controller.user"></context:component-scan>
    	
    	<!--若是要经过AOP注解来配置AOP,除了context:component-scan标签,还有下面这个标签开启AOP注解标示的使用-->
    	<aop:aspectj-autoproxy/>
    </beans>
  • 经过AOP注解来配置:首先要编写加强类,而后再添加注解配置,AOP注解有
    • @Aspect:指定切面,将组件指定为切面组件
    • @AfterThrowing(throwing="e",pointcut="within(controller..*)"):指定切点、加强方法代码以及加强的位置,是前置加强、后置加强、环绕加强等,好比当前就是抛出异常加强,e就是目标组件方法抛出的异常对象
    • @Before("within(controller..*)"):等价于<aop:before>定义
    • @After
    • @Around
    • @AfterReturning
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
//一个简单的日志记录加强代码
@Component//扫描添加到spring容器
@Aspect//将组件指定为切面组件
public class ExceptionBean {
	//e就是目标组件方法抛出的异常对象
	@AfterThrowing(throwing="e",pointcut="within(* controller..*)")
	public void excute(Exception e){
		//将异常信息写入文件中
		System.out.println("发生异常:"+e.getMessage());
		FileWriter fw;
		try {
			fw = new FileWriter("D:note_error.log",true);
			PrintWriter pw=new PrintWriter(fw);
			//利用pw对象写信息
			Date time=new Date();
			SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			String str=sdf.format(time);
			pw.println("***************************");
			pw.println("异常类型:"+e);
			pw.println("发生时间:"+str);
			pw.println("异常详情:");
			e.printStackTrace(pw);
			pw.close();
			fw.close();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			System.out.println("记录异常失败");
		}
	
	}
}

3. 配置开启Spring声明式事务控制

    1. 关于什么是事务,能够参考个人另外一篇博客JDBC,而关于Spring中的事务管理详细参考Spring事务管理函数

  • 经过注解开启:在想要添加事务控制的方法或类(类上标示类中全部方法都要进行事务管理)上添加@Transactional注解便可对该方法开启Spring事务,但必须先在Spring的xml配置文件中添加一行标签,org.springframework.jdbc.datasource.DataSourceTransactionManager该类就是JDBC提供的事务加强类,以下
    <context:property-placeholder location="classpath:conf/db.properties"/>
    	<bean id="datasource" class="org.apache.commons.dbcp.BasicDataSource">
    		<property name="driverClassName" value="${jdbc.driver}"></property>
    		<property name="url" value="${jdbc.url}"></property>
    		<property name="password" value="${jdbc.password}"></property>
    		<property name="username" value="${jdbc.name}"></property>
    	</bean>
    
    	<!--Spring事务管理-->
    	<!--1.定义事务管理Bean-->
    	<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    		<property name="dataSource" ref="ds"></property><!--注意,该行标签指定数据源-->
    	</bean>
    	<!--2.经过配置将txManager做用到Service方法上-->
    	<!--开启@Transactional标记,使用该标记就表示在方法上使用txManager所表明的加强类-->
    	<tx:annotation-driven transaction-manager="txManager"/>

     

  • 在Spring的xml配置文件中经过标签配置:
    <context:property-placeholder location="classpath:conf/db.properties"/>
    	<bean id="datasource" class="org.apache.commons.dbcp.BasicDataSource">
    		<property name="driverClassName" value="${jdbc.driver}"></property>
    		<property name="url" value="${jdbc.url}"></property>
    		<property name="password" value="${jdbc.password}"></property>
    		<property name="username" value="${jdbc.name}"></property>
    	</bean>
    	<!-- 事务管理配置 -->
    	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    		<property name="dataSource" ref="datasource"></property>
    	</bean>
    	<tx:advice id="txAdvice" transaction-manager="transactionManager">
    		<tx:attributes>
    		<!-- 传播行为 -->
    			<tx:method name="save*" propagation="REQUIRED"/>
    			<tx:method name="insert*" propagation="REQUIRED"/>
    			<tx:method name="add*" propagation="REQUIRED"/>
    			<tx:method name="update*" propagation="REQUIRED"/>
    			<tx:method name="delete*" propagation="REQUIRED"/>
    			<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
    		</tx:attributes>
    	</tx:advice>
    	
    	<!-- AOP配置 -->
    	<aop:config>
    		<aop:advisor advice-ref="txAdvice" pointcut="execution(* service.*.*(..))"/>
    	</aop:config>