实现AOP——注解和XML配置

Spring生成代理对象的过程

    1. 创建容器对象的时候, 根据“切入点表达式”拦截的类,生成代理对象;
    2. 如果目标对象有实现接口,使用jdk代理!
    3. 如果目标对象没有实现接口,使用cglib代理!
    4. 从容器获取代理后的对象
    5. 执行代理对象的方法,在运行时期,动态织入“切面”类中的“通知”!

1.注解行式

1.1引入aop名称空间,开启aop注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<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: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>

1.2 使用Aop相关注解

@Aspect 指定一个类为切面类(切面类也需要实例化)(切面类中的方法,也叫做通知)
@Before 前置通知 【在执行目标对象方法之前执行】
@After 后置通知 【在执行目标对象方法之后执行】
@AfterReturning 返回后通知 【在执行目标对象方法结束后执行, 出现异常不执行】
@AfterThrowing 异常通知 【在执行目标对象方法出现异常时候执行】
@Around 环绕通知 【环绕目标方法执行】
@Pointcut 定义一个切入点表达式变量 (后面使用这个切入点表达式的时候,直接引用方法名即可)

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//接口对象IUserDao
public interface IUserDao {

void save();

}
//目标对象UserDao
@Repository // 把对象加入ioc容器
public class UserDao implements IUserDao {

public void save() {
System.out.println("保存...");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//切面类TransactionAop
@Component("aop")
@Aspect // 指定一个类为切面类
public class TransactionAop {

// 定义一个切入点表达式变量 (后面使用这个切入点表达式的时候,直接引用方法名即可)
@Pointcut("execution(* cn.itcast.e_aop_anno.UserDao.*(..))")
public void pointcut_(){
}

//【前置通知】
// 在执行业务方法,之前执行
@Before("pointcut_()")
public void beginTransaction() {
System.out.println("[前置通知] 开启事务..");
}

//【后置通知】
// 在执行业务方法,之后执行
@After("pointcut_()")
public void commit() {
System.out.println("[后置通知] 提交事务..");
}

// 【返回后通知】 在执行目标方法结束后执行, 出现异常不会执行
@AfterReturning("pointcut_()")
public void afterReturing(){
System.out.println("[返回后通知]");
}

// 【异常通知】 在执行目标方法的时候出现异常执行
@AfterThrowing("pointcut_()")
public void afterThrowing(){
System.out.println("[异常通知]");
}

// 【环绕通知】 会环绕目标方法执行
@Around("pointcut_()")
public void arroud(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("[环绕前:]");
pjp.proceed(); // 执行目标方法
System.out.println("[环绕后:]");
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//调用类
public class App {

private ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml",getClass());

// jdk代理
@Test
public void testApp() throws Exception {
// springIOC容器中获取对象,用接口接收!
// IUserDao userDao = (IUserDao) ac.getBean("userDao");
// System.out.println(userDao.getClass());
// userDao.save();


// springIOC容器中获取对象,用实现接收? 报错!!!!!!!!!
/*
* java.lang.ClassCastException:
* $Proxy13 cannot be cast to cn.itcast.e_aop_anno.UserDao
*/
// 总结:在spring的aop编程中,符合切入点表达式的目标类, 如果目标对象有实现接口,从容器获取对象的时候,一定要通过接口接收!
// 否则,包类型转换错误!
UserDao userDao = (UserDao) ac.getBean("userDao");
System.out.println(userDao.getClass());
userDao.save();
}

// cglib代理
@Test
public void testApp_cglib() throws Exception {
UserDao userDao = (UserDao) ac.getBean("userDao");
System.out.println(userDao.getClass());
userDao.save();
}

// 没有生成代理对象,因为没有被切入点表达式拦截
// execution(* cn.itcast.e_aop_anno.UserDao.*(..))
@Test
public void testApp_save_order() throws Exception {
OrderDao orderDao = (OrderDao) ac.getBean("orderDao");
System.out.println(orderDao.getClass());
orderDao.save();
}
}

2.XML配置

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?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: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="aop" class="cn.itcast.f_aop_xml.TransactionAop"></bean>

<!-- Aop相关配置 -->
<aop:config>
<!-- 切入点表达式定义 -->
<aop:pointcut expression="execution(* cn.itcast.f_aop_xml.UserDao.*(..))" id="pt"/>

<!-- 切面配置 -->
<aop:aspect ref="aop">

<!-- 【环绕通知】 -->
<aop:around method="arroud" pointcut-ref="pt"/>

<!-- 【前置通知】 在目标方法之前执行 -->
<aop:before method="beginTransaction" pointcut-ref="pt" />

<!-- 【后置通知】 -->
<aop:after method="commit" pointcut-ref="pt"/>

<!-- 【返回后通知】 -->
<aop:after-returning method="afterReturing" pointcut-ref="pt"/>

<!-- 异常通知 -->
<aop:after-throwing method="afterThrowing" pointcut-ref="pt"/>

</aop:aspect>
</aop:config>

</beans>