五、spring之AOP
一、aop概念
AOP(Aspect Oriented Programming),即面向切面编程,是面向对象编程的的有力补充。面向对象编程关注的主要是业务处理,与之关系不大的部分是切面关注点。他们经常发生在核心业务的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心业务和切面关注点分离开来。主要是利用动态代理来实现AOP的。
1、连接点(joinpoint)
程序执行过程中明确的点,如方法的调用,在Spring中连接点指的就是被拦截到的方法;
2、切入点(pointcut)
对连接点的定义(也可以说是被增强的方法)
3、通知(Advice)
aop在特定切入点执行的增强处理,(前置通知,后置通知,异常通知,最终通知,环绕通知)等
4、切面(aspect)
切入点和通知(引介)的结合: 对哪个方法进行怎么样增强(增强的方法,执行通知类型)advice;
5、织入(weave)
将切面应用到目标对象并创建代理对象的过程
6、引介(Introduction)是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field。
7、Target(目标对象): 代理的目标对象。
8、Proxy(代理): 一个类被AOP织入增强后,就产生一个结果代理类。
二、基于xml的aop配置
1.将切面类交给spring容器管理
2. aop:config标签: 声明aop配置
3. aop:pointcut子标签 :在aop:config中,定义切入点
expression属性:定义切入点表达式,例如:”execution( cn.it...(..))”
id属性:定义该切入点在spring容器中的唯一表示*
4.aop:aspect子标签:定义通知类型(相当于一个切面)
1|.ref属性:切面类的引用
2|.子标签:
1)aop:before:前置通知
2)aop:after 最终通知
3)aop:after-returning 后置通知
4)aop:after-throwing 异常通知
5)aop:around 环绕通知
3|子标签的属性:
1)method:通知引用的方法(从切面类中引用)
2)pointcut-ref:切入点的引用
3)pointcut:切入点表达式
说明 aspect的ref属性(切面类引用)和其子标签method属性(该通知从切面类中引用的方法),可以完整定位一个通知,再通过pointcut切入点的引用,完成织入过程。
<!-- 配置springAop(使用aop的名称空间)
1.将切面类交给spring管理
-->
<bean id="TransactionManger" class="cn.it.transaction.TransactionManger"></bean>
<!--aop配置信息-->
<aop:config>
<!-- 配置一个切入点表达式 :定位被增强方法的
aop:pointcut :
id:切入点的唯一标识
expression: 切入点表达式
完整写法: execution(类的修饰符 返回值 包名.包名.类名.方法名(参数列表))
1.类的修饰符可以省略
2.返回值可以使用*号代替,标识任意返回值类型
3.包名可以使用*号代替,代表任意包,有一个包,需要写一个*号
4.可以使用..表示此包,以及此包下的所有子包
5.类名可以使用*号代替,表示所有类
6.方法名可以使用*号代替,表示任意方法
7.可以使用..代表任意参数
-->
<aop:pointcut expression="execution(* cn.it.service.impl.*.*(..))" id="cutPoint"/>
<!-- 配置通知类型
aop:aspect (切面)
1.ref属性:切面类的引用
2.子标签:
1)aop:before:前置通知
2)aop:after 最终通知
3)aop:after-returning 后置通知
4)aop:after-throwing 异常通知
5)aop:around 环绕通知
3子标签属性:
1)method:通知引用的方法(从切面类中引用)
2)pointcut-ref:切入点的引用
3)pointcut:切入点表达式
-->
<aop:aspect ref="TransactionManger">
<aop:before method="Before" pointcut-ref="**cutPoint**"/>
<aop:after-returning method="AfterReturning" pointcut-ref="cutPoint"/>
<aop:after-throwing method="AfterThrowing" pointcut="execution(* cn.it.service.impl.*.*(..))"/>
<aop:after method="After" pointcut-ref="cutPoint"/>
<!-- <aop:around method="around" pointcut-ref="cutPoint"/>-->
</aop:aspect>
</aop:config>
三、基于xml和注解的aop实现
xml配置
<!-- 1开启对ioc注解的支持:定义扫描的包 -->
<context:component-scan base-package="cn.it" ></context:component-scan>
<!-- 2开启对Aop注解的支持 -->
<aop:aspectj-autoproxy ></aop:aspectj-autoproxy>
切面类注解
package cn.it.transaction;
import java.sql.Connection;
import java.sql.SQLException;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import cn.it.utils.ConnectionUtils;
/**
* 此类:是一个切面类
* * 定义的方法,全部都是增强的代码逻辑
* 一个通知类型,对应一个切面方法
*
* 1.声明切面类
* @Aspect
* 2.定义切入点
* @Ponitcut
* value:切入点表达式
* 方法名()
* 3.定义通知类型
* @Before
* @AfterReturning
* @AfterThrowing
* @After
* @Around
* * value :配置切入点的引用,或者切入点表达式
*
*/
@Component
@Aspect
public class TransactionManger {
/**
* @Pointcut:定义公共的切入点
* 1.需要配置到一个空的方法上
* 2.value:切入点表达式
* 3.其他的地方引用此切入点的时候 : 方法名()
*/
@Pointcut(value="execution(* cn.it.service.impl.*.*(..))")
public void cutPoint() {};
//@Before("cutPoint()")
public void Before() throws SQLException {
System.out.println("前置通知");
Connection connection = ConnectionUtils.getInstance().getConnection();
connection.setAutoCommit(false);
}
//@After("execution(* cn.it.service.impl.*.*(..))")
public void After() throws SQLException {
System.out.println("最终通知");
Connection connection = ConnectionUtils.getInstance().getConnection();
connection.setAutoCommit(true);
connection.close();
}
//@AfterReturning("cutPoint()")
public void AfterReturning() throws SQLException {
System.out.println("后置通知");
Connection connection = ConnectionUtils.getInstance().getConnection();
connection.commit();
}
//@AfterThrowing("cutPoint()")
public void AfterThrowing() throws SQLException {
System.out.println("异常通知");
Connection connection = ConnectionUtils.getInstance().getConnection();
connection.rollback();
}
/**
* 环绕通知:
* spring提供的一种通知类型,可以让程序员灵活的配置增强代码逻辑的执行过程
* 具有拦截和放行的功能
* 代码写法:和自定义动态代理方法中的增强逻辑类似
* 将前置通知,后置通知,异常通知,最终通知,全部都要定义在环绕通知中!
* 1.ProceedingJoinPoint : 连接点对象
* 2.proceed:内部可以调用被代理对象的方法
* 3.返回值 : object
* 被代理对象方法的返回值
*/
@Around("cutPoint()")
public Object around(ProceedingJoinPoint pj) throws Throwable {
Connection conn = ConnectionUtils.getInstance().getConnection();
Object proceed = null;
try {
System.out.println("前");
conn.setAutoCommit(false);
proceed = pj.proceed();
System.out.println("后");
conn.commit();
} catch (Exception e) {
System.out.println("异常");
conn.rollback();
} finally {
System.out.println("最终");
conn.setAutoCommit(true);
conn.close();
}
return proceed;
}
}
四、基于注解的aop实现
- 将切面类对象交给spring容器管理(ioc注解)
- 开启对注解们的支持
* 对ioc注解的支持
* 开启对aop注解的支持
1.声明切面类
@Aspect
@Component 交spring管理
2.定义切入点
@Ponitcut
@Pointcut:定义公共的切入点
- 1).需要配置到一个空的方法上
- 2).value:切入点表达式
- 3.)其他的地方引用此切入点的时候 : 方法名()
3.定义通知类型
@Before
@AfterReturning
@AfterThrowing
@After
@Around
- value :配置切入点的引用,或者切入点表达式
@EnableAspectJAutoProxy:开启对aop注解的支持
@ComponentScan(basePackages=”cn.it”):开启对包扫描的支持
核心配置类的声明
- value :配置切入点的引用,或者切入点表达式
/**
* 1.声明配置类
* 2.定义扫描的包
* 3.开启对Aop注解的支持
* 4.导入其他配置文件
*
*/
@Configuration
@ComponentScan(basePackages="cn.it")
@EnableAspectJAutoProxy
@Import(jdbcConfig.class)
public class SpringConfig {
}
切面类的注解
package cn.it.transaction;
import java.sql.Connection;
import java.sql.SQLException;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import cn.it.utils.ConnectionUtils;
/**
* 此类:是一个切面类
* * 定义的方法,全部都是增强的代码逻辑
* 一个通知类型,对应一个切面方法
*
* 1.声明切面类,交给spring管理
* @Aspect 声明切面类
@Component 将切面类交spring管理
* 2.定义切入点
* @Ponitcut
* value:切入点表达式
* 方法名()
* 3.定义通知类型
* @Before
* @AfterReturning
* @AfterThrowing
* @After
* @Around
* * value :配置切入点的引用,或者切入点表达式
*
*/
@Component
@Aspect
public class TransactionManger {
/**
* @Pointcut:定义公共的切入点
* 1.需要配置到一个空的方法上
* 2.value:切入点表达式
* 3.其他的地方引用此切入点的时候 : 方法名()
*/
@Pointcut(value="execution(* cn.it.service.impl.*.*(..))")
public void cutPoint() {
};
@Autowired
private ConnectionUtils cUtils;
//@Before("cutPoint()")
public void Before() throws SQLException {
System.out.println("前置通知");
Connection connection = cUtils.getConnection();
connection.setAutoCommit(false);
}
//@After("cutPoint()")
public void After() throws SQLException {
System.out.println("最终通知");
Connection connection = cUtils.getConnection();
connection.setAutoCommit(true);
connection.close();
}
//@AfterReturning("cutPoint()")
public void AfterReturning() throws SQLException {
System.out.println("后置通知");
Connection connection = cUtils.getConnection();
connection.commit();
}
//@AfterThrowing("execution(* cn.it.service.impl.*.*(..))")
public void AfterThrowing() throws SQLException {
System.out.println("异常通知");
Connection connection = cUtils.getConnection();
connection.rollback();
}
/**
* 环绕通知:
* spring提供的一种通知类型,可以让程序员灵活的配置增强代码逻辑的执行过程
* 具有拦截和放行的功能
* 代码写法:和自定义动态代理方法中的增强逻辑类似
* 将前置通知,后置通知,异常通知,最终通知,全部都要定义在环绕通知中!
* 1.ProceedingJoinPoint : 连接点对象
* 2.proceed:内部可以调用被代理对象的方法
* 3.返回值 : object
* 被代理对象方法的返回值
*/
@Around("cutPoint()")
public Object around(ProceedingJoinPoint pj) throws Throwable {
Connection conn = cUtils.getConnection();
Object proceed = null;
try {
System.out.println("前");
conn.setAutoCommit(false);
proceed = pj.proceed();
System.out.println("后");
conn.commit();
} catch (Exception e) {
System.out.println("异常");
conn.rollback();
} finally {
System.out.println("最终");
conn.setAutoCommit(true);
conn.close();
}
return proceed;
}
}
其他配置类的声明
package cn.it.config;
import java.beans.PropertyVetoException;
import javax.sql.DataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
/**
* 1.@Configuration:声明该类为配置类
* 2.@PropertySource:读取property配置信息交给spring容器管理
* value属性为:文件名的类路径字符串 classpath:开头代表类路径,后面跟路径
* 3.@Value("${jdbc.username}"):为成员变量赋值(基本类型值),用springEl表达式从spring容器中取值
* 4.@Bean 调用该方法,将该方法的返回值交给spring容器管理
* value属性,指定该对象在spring容器中的id为value的值
* 5.@Qualifier:value属性指定从spring注入时,由通过类型查找,改为通过id查找,id为value属性的值
*/
@Configuration
@PropertySource("classpath:jdbc.properties")
public class jdbcConfig {
@Value("${jdbc.username}")
private String User;
@Value("${jdbc.password}")
private String password;
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String Url;
@Bean("queryRunner")
public QueryRunner getQueryRunner(@Qualifier("dataSource")DataSource ds) {
return new QueryRunner(ds);
}
@Bean("dataSource")
public DataSource getDataSource() {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setUser(User);
System.out.println("====================");
ds.setPassword(password);
try {
ds.setDriverClass(driver);
} catch (PropertyVetoException e) {
e.printStackTrace();
}
ds.setJdbcUrl(Url);
return ds;
}
}
连接池工具类
package cn.it.utils;
import java.sql.Connection;
import java.sql.SQLException;
import javax.annotation.Resource;
import javax.sql.DataSource;
import javax.xml.crypto.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@Service
public class ConnectionUtils {
@Autowired
private DataSource dataSource;
private ThreadLocal<Connection> tl = new ThreadLocal<>();
public Connection getConnection() {
Connection conn = null;
try {
conn = tl.get();
if(conn == null) {
conn = dataSource.getConnection();
tl.set(conn);
}
} catch (Exception e) {
}
return conn;
}
}