一、认识Spring中的AOP
AOP(面向切面编程):简单地理解就是纵向重复的代码被横向地抽取,有点类似于动态代理中的开启事务、提交回滚操作被提取出来。
百度百科:
主要功能
日志记录,性能统计,安全控制,事务处理,异常处理等等。
AOP主要意图
将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
二、Spring中的AOP
1、 无需手动写动态代理的代码,spring可以将容器中管理对象生成动态代理对象,前提是进行对应的配置。
2、Spring-AOP是基于动态代理的-优先选用JDKProxy动态代理;
a.Proxy动态代理:被代理的对象必须要实现接口;
b.Cglib动态代理:被代理的对象不能被final修饰,基于继承;
三、动态代理例子:
1、建立UserService接口,其中简写几个方法以供调用:
public interface UserService {
//增
void save();
//删
void delete();
//查
void find();
//改
void update();
}
2、实现UserService接口的UserServiceImpl
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("save");
}
@Override
public void delete() {
System.out.println("delete");
}
@Override
public void find() {
System.out.println("find");
}
@Override
public void update() {
System.out.println("update");
}
}
3、撰写动态代理类UserServiceProxy
/*
* UserService代理对象
*/
public class UserServiceProxy {
public UserService getUserService(UserService us) {
//创建动态代理对象
return (UserService) Proxy.newProxyInstance(UserServiceProxy.class.getClassLoader(),
UserServiceImpl.class.getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//增强代码
System.out.println("开启事务");
//调用原始方法
Object invoke = method.invoke(us, args);
//增强代码
System.out.println("提交/回滚");
return invoke;
}
});
}
}
4、撰写测试类
public class aopTest {
@Test
public void test() {
UserServiceProxy usProxy=new UserServiceProxy();
UserService us = new UserServiceImpl();
UserService us_Proxy = usProxy.getUserService(us);
us_Proxy.find();
}
}
5、Junit测试运行。
四、Spring-aop中的名词解释
1、JoinPoint: 连接点,目标对象中,哪些方法会被拦截;save;delete;update;find
2、Pointcut: 切入点,筛选连接点,选择最终要增强的方法;
3、Advice: 通知/增强,增强的代码;
4、Introduction: 介入/引入,在执行时期动态加入一些方法或行为;
5、Aspect: 切面,通知+切入点,通知应用到哪个切点;
6、target: 目标,被代理对象;
7、weaving: 织入,把切面的代码应用到目标对象来创建新的代理对象的过程;
8、proxy: 代理,把切面的代码应用到目标对象来创建新的代理对象;
五、Spring-aop中的五种自定义通知
/**
* 自定义通知类
* @author Dunka
*
*/
public class MyAdvice {
// i.before 前置通知 在目标方法执行前 调用此方法
public void before() {
System.out.println("before");
}
// ii.after 最终通知(后置通知)在目标方法执行后 无论方法是否出现异常 都会调用此方法
public void after() {
System.out.println("after");
}
// iii.afterReturning 成功通知(后置通知)在目标方法执行成功后 调用此方法 如果出现异常则不调用
public void afterReturning() {
System.out.println("afterReturning");
}
// iv.afterThrowing 异常通知(后置通知)在目标方法出现异常后 调用此方法
public void afterThrowing() {
System.out.println("afterThrowing");
}
// v.around 环绕通知 需要手动调用目标方法,并且可以设置通知
public Object around(ProceedingJoinPoint point) throws Throwable {
System.out.println("around before");
Object object = point.proceed();
System.out.println("around after");
return object;
}
}
六、Spring-aop配置
1、导包:导入spring的基本包 、spring-aspects、spring-aop、aop联盟包:aopalliance、aop织入包:aspectj.weaver;
2、配置applicationContext.xml:
(1)为eclipse环境中添加aop的约束,利用spring的集成插件写文件头:
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
</beans>
(2)文件配置内容:
<!-- 目标对象 -->
<bean name="userService" class="com.dunka.service.UserServiceImpl"/>
<!-- 通知对象 -->
<bean name="myAdvice" class="com.dunka.aop.MyAdvice"/>
<!-- aop配置 -->
<aop:config>
<!-- 切入点 expression切入点表达式
public void com.dunka.service.UserServiceImpl.find()
(省略) * com.dunka.service.*ServiceImpl.*(..)
id表示唯一标识-->
<aop:pointcut expression="execution(* com.dunka.service.*ServiceImpl.*(..))" id="servicePc"/>
<!-- 切面 通知+切入点 -->
<aop:aspect ref="myAdvice">
<!-- 通知类型 -->
<!-- 前置通知 -->
<aop:before method="before" pointcut-ref="servicePc"/>
<!-- 最终通知 后置通知 -->
<aop:after method="after" pointcut-ref="servicePc"/>
<!-- 成功通知 后置通知 -->
<aop:after-returning method="afterReturning" pointcut-ref="servicePc"/>
<!-- 异常通知 后置通知 -->
<aop:after-throwing method="afterThrowing" pointcut-ref="servicePc"/>
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="servicePc"/>
</aop:aspect>
</aop:config>
3、编写测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class aopTest {
@Resource(name="userService")
UserService us;
@Test
public void test() {
us.delete();
}
}
4、运行测试,可在某个目标方法中写入异常,查看异常通知是否正确。
注意:过程中出现了一个错误:
java.lang.IllegalStateException: Failed to load ApplicationContext
原因是配置文件中的方法名与调用方法名不一致,导致产生错误,修正即可。