1. AOP
1. AOP:即面向切面编程,采用横向抽取机制,取代了传统的继承体系的重复代码问题,如下图所示,性能监控、日志记录等代码围绕业务逻辑代码,而这部分代码是一个高度重复的代码,也就是在每一个业务逻辑的代码中都会有相同的代码围绕业务逻辑代码,而AOP就是将这些重复代码抽取出
2. AOP实现原理:就是代理模式,主要有两种方式,分别是静态代理和动态代理,
- 静态代理:采用一些工具类对原来的类生成一个代理类,代理类以.class存在
- 动态代理(重点,spring基于动态代理实现aop):在运行中,通过反射生成类的代理对象,在代理对象中对原来的对象进行增强。
3. spring采用动态代理的技术实现包括:
- 基于接口生成动态代理对象:使用jdk提供的反射机制实现,参考这篇博客,JDK动态代理实现详解
- 基于类生成动态代理对象:通过继承实现,根据类生成一个子类(代理对象),在代理对象(子类)中对父类进行增强,参考这篇博客。实现
cglib通过继承的形式来实现动态功能的代理 那么就无法避免一些继承的缺点,那就是无法代理final方法和无法代理final类,示例使用代码如下
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生成代理。
5. 相关术语:
- Pointcut(切入点):确定在哪个方法上增强,一个类有多个切点
- Advice(通知/增强):在切点上进行增强,包括:前置增强、后置增强、抛出异常增强
- Target(目标对象):对目标对象进行增强,生成代理对象
- Proxy(代理):对目标对象进行增强,生成代理对象
- Weaving(织入):生成代理的时机(过程)
- 动态代理织入,在运行期为目标类生成代理对象
- 编译期织入
- Aspect(切面): 包括切点和增强,面向哪个切点进行增强(编程)。
2. SpringAOP的简单使用
1. 搭建环境:通过Maven加入spring-webmvc.jar和aspectjweaver.jar包即可导入所有需要依赖的包
2. AspectJ相关语法:
- 配置切点的语法:切点就是方法,所以必须要确认是那一个类的哪一个方法,
- * 用法:匹配任意字符
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配置:
- 使用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>
来源:oschina
链接:https://my.oschina.net/u/3352298/blog/2877693