Spring提供了4种类型的AOP支持:
- 基于代理的经典Spring AOP;
- 纯POJO切面;
- @AspectJ注解驱动的切面;
- 注入式AspectJ切面(适用于Spring各个版本)
SpringAOP框架的一些必须要了解的关键知识
- Spring通知是JAVA编写的
Spring所创建的通知都是用标准的Java类编写的。因此可以用普通Java开发一样的IDE来开发切面。而且,定义通知所应用的切点通常会使用注解或在Spring 配置文件里采用XML来编写。 - Spring在运行时通知对象
通过在代理类中包裹切面,Spring在运行时期把切面织入到Spring管理的bean中。
直到应用需要被代理的bean时,Spring才创建代理对象。如果使用的是ApplicationContext的话,在ApplicationContext从BeanFactory中加载所有的bean的时候,Spring才会被创建到代理对象。因为Spring运行时才创建代理对象,所以我们不需要特殊的编译器来织入SpringAOP的切面。 - Spring只支持方法级别的连接点
因为Spring基于动态代理,所以Spring只支持方法级别连接点。这与一些其他的AOP框架是不同的,例如AspectJ和JBoss,除了方法切点,它们还提供了字段和构造器接入点。Spring缺少对字段连接点的支持,无法让我们创建细粒度的通知,例如拦截对象字段的修改。而且它不支持构造器连接点,我们就无法再bean创建时应用通知。
但是方法拦截可以满足绝大部分的需求。如果需要方法拦截之外的连接点拦截功能,我们可以利用AspectJ来补充SpringAOP的功能。
1. 注解方式实现AOP编程
步骤:
1) 先引入aop相关jar文件
2) bean.xml中引入aop名称空间
3) 开启aop注解
4) 使用注解
@Aspect 指定一个类为切面类
@Pointcut(“execution(* cn.itcast.e_aop_anno..(..))”) 指定切入点表达式
@Before(“pointCut_()”) 前置通知: 目标方法之前执行
@After(“pointCut_()”) 后置通知:目标方法之后执行(始终执行)
@AfterReturning(“pointCut_()”) 返回后通知: 执行方法结束前执行(异常不执行)
@AfterThrowing(“pointCut_()”) 异常通知: 出现异常时候执行
@Around(“pointCut_()”) 环绕通知: 环绕目标方法执行
下面是我项目中的maven配置
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.cyy</groupId>
<artifactId>Maven_01</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>Maven_01 Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.5.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/aspectj/aspectjrt -->
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
</dependencies>
</project>
//1. IUserDao.java
// 接口
public interface IUserDao {
void save();
}
//2. UserDao.java
/**
* 目标对象
* @author Jie.Yuan
*
*/
@Component // 加入容器
public class UserDao implements IUserDao{
@Override
public void save() {
System.out.println("-----核心业务:保存!!!------");
}
}
//3. Aop.java 切面类
@Component
@Aspect // 指定当前类为切面类
public class Aop {
// 指定切入点表单式: 拦截哪些方法; 即为哪些类生成代理对象
@Pointcut("execution(* cn.itcast.e_aop_anno.*.*(..))")
public void pointCut_(){
}
// 前置通知 : 在执行目标方法之前执行
@Before("pointCut_()")
public void begin(){
System.out.println("开始事务/异常");
}
// 后置/最终通知:在执行目标方法之后执行 【无论是否出现异常最终都会执行】
@After("pointCut_()")
public void after(){
System.out.println("提交事务/关闭");
}
// 返回后通知: 在调用目标方法结束后执行 【出现异常不执行】
@AfterReturning("pointCut_()")
public void afterReturning() {
System.out.println("afterReturning()");
}
// 异常通知: 当目标方法执行异常时候执行此关注点代码
@AfterThrowing("pointCut_()")
public void afterThrowing(){
System.out.println("afterThrowing()");
}
// 环绕通知:环绕目标方式执行
@Around("pointCut_()")
public void around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("环绕前....");
pjp.proceed(); // 执行目标方法
System.out.println("环绕后....");
}
}
//4. bean.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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 开启注解扫描 -->
<context:component-scan base-package="cn.itcast.e_aop_anno"></context:component-scan>
<!-- 开启aop注解方式 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
//App.java
public class App {
ApplicationContext ac =
new ClassPathXmlApplicationContext("cn/itcast/e_aop_anno/bean.xml");
// 目标对象有实现接口,spring会自动选择“JDK代理”
@Test
public void testApp() {
IUserDao userDao = (IUserDao) ac.getBean("userDao");
System.out.println(userDao.getClass());
userDao.save();
}
// 目标对象没有实现接口, spring会用“cglib代理”
@Test
public void testCglib() {
OrderDao orderDao = (OrderDao) ac.getBean("orderDao");
System.out.println(orderDao.getClass());
orderDao.save();
}
}
2. XML方式实现AOP编程
Xml实现aop编程:
1) 引入jar文件 【aop 相关jar, 4个】
2) 引入aop名称空间
3)aop 配置
* 配置切面类 (重复执行代码形成的类)
* aop配置
拦截哪些方法 / 拦截到方法后应用通知代码
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- dao 实例 -->
<bean id="userDao" class="cn.itcast.f_aop_xml.UserDao"></bean>
<bean id="orderDao" class="cn.itcast.f_aop_xml.OrderDao"></bean>
<!-- 切面类 -->
<bean id="aop" class="cn.itcast.f_aop_xml.Aop"></bean>
<!-- Aop配置 -->
<aop:config>
<!-- 定义一个切入点表达式: 拦截哪些方法 -->
<aop:pointcut expression="execution(* cn.itcast.f_aop_xml.*.*(..))" id="pt"/>
<!-- 切面 -->
<aop:aspect ref="aop">
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pt"/>
<!-- 前置通知: 在目标方法调用前执行 -->
<aop:before method="begin" pointcut-ref="pt"/>
<!-- 后置通知: -->
<aop:after method="after" pointcut-ref="pt"/>
<!-- 返回后通知 -->
<aop:after-returning method="afterReturning" pointcut-ref="pt"/>
<!-- 异常通知 -->
<aop:after-throwing method="afterThrowing" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
</beans>
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"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- dao 实例 -->
<bean id="userDao" class="cn.itcast.g_pointcut.UserDao"></bean>
<bean id="orderDao" class="cn.itcast.g_pointcut.OrderDao"></bean>
<!-- 切面类 -->
<bean id="aop" class="cn.itcast.g_pointcut.Aop"></bean>
<!-- Aop配置 -->
<aop:config>
<!-- 定义一个切入点表达式: 拦截哪些方法 -->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.*.*(..))" id="pt"/>-->
<!-- 【拦截所有public方法】 -->
<!--<aop:pointcut expression="execution(public * *(..))" id="pt"/>-->
<!-- 【拦截所有save开头的方法 】 -->
<!--<aop:pointcut expression="execution(* save*(..))" id="pt"/>-->
<!-- 【拦截指定类的指定方法, 拦截时候一定要定位到方法】 -->
<!--<aop:pointcut expression="execution(public * cn.itcast.g_pointcut.OrderDao.save(..))" id="pt"/>-->
<!-- 【拦截指定类的所有方法】 -->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.*(..))" id="pt"/>-->
<!-- 【拦截指定包,以及其自包下所有类的所有方法】 -->
<!--<aop:pointcut expression="execution(* cn..*.*(..))" id="pt"/>-->
<!-- 【多个表达式】 -->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) || execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) or execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
<!-- 下面2个且关系的,没有意义 -->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) && execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) and execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
<!-- 【取非值】 -->
<!--<aop:pointcut expression="!execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
<aop:pointcut expression=" not execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>
<!-- 切面 -->
<aop:aspect ref="aop">
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
</beans>
3. Spring对jdbc支持
使用步骤:
1)引入jar文件
spring-jdbc-3.2.5.RELEASE.jar
spring-tx-3.2.5.RELEASE.jar
2) 优化
//UserDao.java
package jdbc;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
/**
* Created by Administrator on 2017/2/3.
*/
public class UserDao {
//IOC容器注入
private DataSource dataSource;
public void setDataSource(DataSource dataSource){
this.dataSource = dataSource;
}
public void save(){
String sql = "insert into t_dept(deptName) values('test')";
//使用jdbc模板工具类简化jdbc操作
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.update(sql);
}
public Dept findById(int id){
String sql = "select * from t_dept";
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
//List<Map<String,Object>> list = jdbcTemplate.queryForList(sql);
List<Dept> list = jdbcTemplate.query(sql, new RowMapper<Dept>() {
//如何封装一行数据
@Override
public Dept mapRow(ResultSet resultSet, int i) throws SQLException {
Dept dept = new Dept();
dept.setDeptId(resultSet.getInt("deptId"));
dept.setDeptName(resultSet.getString("deptName"));
return dept;
}
});
System.out.println(list);
return null;
}
}
//Dept.java
package jdbc;
/**
* Created by Administrator on 2017/2/3.
*/
public class Dept {
private int deptId;
private String deptName;
public int getDeptId() {
return deptId;
}
public void setDeptId(int deptId) {
this.deptId = deptId;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
@Override
public String toString() {
return "Dept{" +
"deptId=" + deptId +
", deptName='" + deptName + '\'' +
'}';
}
}
//bean.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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 1. 数据源对象: C3P0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///hib_demo"></property>
<property name="user" value="root"></property>
<property name="password" value="123456"></property>
<property name="initialPoolSize" value="3"></property>
<property name="maxPoolSize" value="10"></property>
<property name="maxStatements" value="100"></property>
<property name="acquireIncrement" value="2"></property>
</bean>
<!-- 2. 创建JdbcTemplate对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- dao 实例 -->
<bean id="userDao" class="jdbc.UserDao">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
//测试类
public class App {
//容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("jdbc/bean.xml");
@Test
public void testApp(){
UserDao dao = (UserDao) ac.getBean("userDao");
//dao.save();
dao.findById(2);
}
}
别忘了在数据库中建相应的数据库、表,注意字段与实体类之间的对应。