面向切面的Spring
1、面向切面编程
1.1、什么是面向切面编程
在软件业,AOP为Aspect Oriented Programming
的缩写,意为:面向切面编程
,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低
,提高程序的可重用性
,同时提高了开发的效率
。 ------>来自百度百科
- 应用范围:事务管理、安全检查、缓存 、日志等
1.2、定义AOP术语
- 通知(Advice)
切面的工作、目标,称为通知
5种类型的通知:
1、Before:在方法被调用之前调用通知
2、After:在方法被调用之前调用通后,无论方法是否成功执行
3、After-returning:再方法成功执行之后调用通知
4、After-throwing:在方法抛出异常后调用通知
5、Around:通知包裹了被通知的方法,在被通知的方法被调用前后执行的自定义行为
- 连接点(Joinpoint)
连接点是指那些可能被拦截到的方法,连接点在应用执行过程中能够插入切面,切面代码可以利用这些点插入到正常的流程中,增加新的行为。 - 切点(Poincut)
匹配通知所要织入的一个或者多个连接点。已被通知的点。 - 切面(Aspect)
切面是通知与切点的结合。 - 引入(Introduction)
向现有的类添加新的方法或者属性 - 织入(Weaving)
将切面应用到目标对象来创建新的代理过程,在目标对象的生命周期里可以有多个点进行织入。
2、在XML中声明切面
- UserService
public interface UserService {
public void insertUser();
public void updateUser();
public void deleteUser();
}
- UserServiceImpl
import com.tcrush.service.UserService;
public class UserServiceImpl implements UserService {
@Override
public void insertUser() {
System.out.println("insertUser...");
}
@Override
public void updateUser() {
System.out.println("updateUser...");
}
@Override
public void deleteUser() {
System.out.println("deleteUser...");
}
}
- UserTest
import com.tcrush.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserTest {
@Test
public void testAspectXml() {
String xmlPath="spring-config.xml";
ApplicationContext context=new ClassPathXmlApplicationContext(xmlPath);
UserService userService= (UserService) context.getBean("UserServiceId");
userService.insertUser();
userService.updateUser();
userService.deleteUser();
}
}
- MyAspect
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAspect {
public void myBefore(JoinPoint joinPoint) {
System.out.println("前置通知 : " + joinPoint.getSignature().getName());
}
public void myAfterReturning(JoinPoint joinPoint, Object ret) {
System.out.println("后置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret);
}
public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知前");
Object obj = joinPoint.proceed();
System.out.println("环绕通知后");
return obj;
}
public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
System.out.println("抛出异常通知 : " + e.getMessage());
}
public void myAfter(JoinPoint joinPoint) {
System.out.println("最终通知");
}
}
- spring-config.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: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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 创建目标类 -->
<bean id="UserServiceId" class="com.tcrush.service.impl.UserServiceImpl"></bean>
<!-- 创建切面类(通知) -->
<bean id="MyAspectId" class="com.tcrush.aspect.MyAspect"></bean>
<aop:config>
<aop:aspect ref="MyAspectId">
<aop:pointcut id="MyPointCut" expression="execution(* com.tcrush.service.impl.UserServiceImpl.*(..))"/>
<!-- 前置通知
<aop:before method="" pointcut="" pointcut-ref=""/>
method :方法名
pointcut :切入点表达式,此表达式只能当前通知使用。
pointcut-ref :切入点引用,可以与其他通知共享切入点。
-->
<!-- <aop:before method="myBefore" pointcut-ref="MyPointCut"></aop:before>-->
<!-- 后置通知 ,目标方法后执行
-->
<!-- <aop:after-returning method="myAfterReturning" pointcut-ref="MyPointCut" returning="ret" />-->
<!-- 环绕通知
<aop:around method="" pointcut-ref=""/>
-->
<!-- <aop:around method="myAround" pointcut-ref="MyPointCut"/>-->
<!-- 抛出异常
<aop:after-throwing method="" pointcut-ref="" throwing=""/>
throwing :通知方法的第二个参数名称
-->
<!-- <aop:after-throwing method="myAfterThrowing" pointcut-ref="MyPointCut" throwing="e"/>-->
<!-- 抛出异常
<aop:after method="" pointcut-ref=""/>
-->
<!-- <aop:after method="myAfter" pointcut-ref="MyPointCut"></aop:after>-->
</aop:aspect>
</aop:config>
</beans>
- 声明POJ0配置元素
配置元素 | 描述 |
---|---|
</aop:advisor> | 定义AOP通知器 |
</aop:after> | 定义AOP后置通知(不管被通知的方法量否执行成功) |
</aop:after-returning> | 定义 AOP after-returning通知 |
</aop:after-throwing> | 定义 after-throwing通知 |
</aop:around> | 定义AOP环绕通知 |
</aop:aspectj-autoproxy> | 启用@AspectJ注解驱动的切面 |
</aop:before> | 定义AOP前置通知 |
</caop:config> | 顶层的AOP配置元素,大多数的<\aop:*>元素必须包含在 caop:config元素内 |
</aop:declare-parents> | 为被通知的对象引人入额外的接囗,并透明地实现 |
</aop:pointcut> | 定义切点 |
3、注解切面
使用注解替换配置文件
- EmployeeService
public interface EmployeeService {
public void insertEmp();
public void updateEmp();
public void deleteEmp();
}
- EmployeeServiceImpl
import com.tcrush.service.EmployeeService;
import org.springframework.stereotype.Service;
@Service("EmpServiceId") //注解,<bean>
public class EmployeeServiceImpl implements EmployeeService {
@Override
public void insertEmp() {
System.out.println("insertEmp...");
}
@Override
public void updateEmp() {
System.out.println("updateEmp...");
}
@Override
public void deleteEmp() {
System.out.println("deleteEmp...");
}
}
- EmployeeTest
import com.tcrush.service.EmployeeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class EmployeeTest {
@Test
public void testAspectNote() {
String xmlPath="spring-config.xml";
ApplicationContext context=new ClassPathXmlApplicationContext(xmlPath);
EmployeeService empService= (EmployeeService) context.getBean("EmpServiceId");
empService.insertEmp();
empService.updateEmp();
empService.deleteEmp();
}
}
- EmployeeAspect
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class EmployeeAspect {
//声明公共切入点
@Pointcut("execution(* com.tcrush.service.impl.EmployeeServiceImpl.*(..))")
private void myPointCut(){
}
@Before("execution(* com.tcrush.service.EmployeeService.*(..))")
public void myBefore(JoinPoint joinPoint) {
System.out.println("前置通知 : " + joinPoint.getSignature().getName());
}
// @AfterReturning(value = "execution(* com.tcrush.service.impl.EmployeeServiceImpl.*(..))",returning = "ret")
public void myAfterReturning(JoinPoint joinPoint, Object ret) {
System.out.println("后置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret);
}
// @Around(value = "myPointCut()")
public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知前");
Object obj = joinPoint.proceed();
System.out.println("环绕通知后");
return obj;
}
// @AfterThrowing(value = "myPointCut()",throwing = "e")
public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
System.out.println("抛出异常通知 : " + e.getMessage());
}
// @After(value = "myPointCut()")
public void myAfter(JoinPoint joinPoint) {
System.out.println("最终通知");
}
}
- spring-config.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: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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.tcrush"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>