spring实现aop的原理(有接口优先使用动态代理 没有接口使用cglib代理)
动态代理(被代理对象必须要实现接口 才能产生代理对象)
cglib代理
可以对任何类生成代理 代理的原理是对目标对象进行继承代理 如果目标对象被final修饰 那么该类无法被cglib代理
导包
准备目标对象
public interface TestService {
public void add();
public void del();
public void update();
public void find();
}
public class TestServiceImpl implements TestService{
public void add() {
System.out.println("增");
}
public void del() {
System.out.println("删");
}
public void update() {
System.out.println("改");
}
public void find() {
System.out.println("查");
}
}
准备通知
前置通知 目标方法之前调用
后置通知(如果痴线异常将不会调用) 目标方法之后电泳
环绕通知 在目标方法之前和之后都会调用
异常拦截通知 如果出现异常就会调用
后置通知(无论是否出现异常 都会调用) 目标方法之后调用
public class MyAdvice {
//前置通知
public void before() {
System.out.println("前置通知");
}
//后置通知
public void afterReturning() {
System.out.println("后置通知(出现异常不在调用)");
}
//环绕通知 一定要传入参数
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕通知之前的部分");
//调用目标方法
Object proceed = proceedingJoinPoint.proceed();
System.out.println("环绕通知之后的部分");
return proceed;
}
//异常通知
public void afterException() {
System.out.println("异常通知");
}
//后置通知
public void after() {
System.out.println("后置通知(出现异常仍会调用)");
}
}
配置进行织入 将通知织入目标对象 (需要加入aop约束)
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
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-4.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">
<!-- 配置目标对象 -->
<bean name="testService" class="cn.a2490.test.impl.TestServiceImpl"></bean>
<!-- 配置通知对象 -->
<bean name="myAdvice" class="cn.a2490.aspect.MyAdvice"></bean>
<!-- 将通知织入目标对象 -->
<aop:config>
<!--
配置切入点
execution:配置切入函数的类型 返回值 哪个函数
函数的类型当为public时 可以省略不写 例如 void cn.a2490.test.impl.TestServiceImpl.add()
返回值不做要求时可以写* 例如 * cn.a2490.test.impl.TestServiceImpl.add()
不指定函数时可以写* 例如 * cn.a2490.test.impl.TestServiceImpl.*()
当对函数的参数不做要求时 可以为.. 例如 * cn.a2490.test.impl.TestServiceImpl.*(..)
当还需要切入其子包下的方法时可以在包名后加. 例如 * cn.a2490.test.impl..TestServiceImpl.*(..)
当需要写入以xxx结尾的类中的方法时 可以使用* 例如 * cn.a2490.test.impl.*ServiceImpl.*(..)
id:给切入点起个名字
-->
<aop:pointcut expression="execution(public void cn.a2490.test.impl.TestServiceImpl.add())" id="ts"/>
<!--
通知描述
ref:指定通知类
-->
<aop:aspect ref="myAdvice">
<!--
指定切入类型、切入方法及切入点
aop:before 前置通知
aop:after-returning 后置通知(如果痴线异常将不会调用)
aop:around 环绕通知
aop:after-throwing 异常拦截通知
aop:after 后置通知(无论是否出现异常 都会调用)
method:指定调用通知类中的哪个方法
pointcut-ref:指定切入点
-->
<aop:before method="" pointcut-ref="ts">
</aop:aspect>
</aop:config>
</beans>
测试
导入测试包
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo1 {
@Resource(name="testService")
private TestService testService;
@Test
public void fun1() {
testService.del();
}
}