做公司网站报价/免费的网站推广
目录
- Spring AOP
- 切面
- 对应的接口
- xml配置文件
- 说明
- AspectJ
- xml配置方式
- 注解配置方式
- 总结
在Spring中使用AOP有2种方式
- Spring AOP:Spring封装了动态代理实现AOP
- AspectJ:专业的AOP框架,更强大
最初spring是自己实现的aop,即spring aop,后来spring集成了aspectj的切面语法,也可以使用aspectj。
Spring AOP
所需依赖:spring-aop
目标接口
public interface UserService {void login();void logout();
}
目标类
@Service
public class UserServiceImpl implements UserService {@Overridepublic void login() {System.out.println("正在执行login()...");}@Overridepublic void logout() {System.out.println("正在执行logout()...");}}
切面
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;@Component //根据增强时机实现对应的接口
public class UserServiceAspect implements MethodInterceptor {@Overridepublic Object invoke(MethodInvocation methodInvocation) throws Throwable {//前增强System.out.println("正在执行前增强...");//调用目标方法,返回值是Object类型Object object=methodInvocation.proceed();//后增强System.out.println("正在执行后增强...");//返回目标方法的返回值return object;}}
对应的接口
通知类型 | 增强时机 | 需要实现的接口 |
---|---|---|
环绕通知 | 目标方法的整个调用流程,可实现前增强、后增强、异常处理等 | MethodInterceptor |
前置通知 | 目标方法执行之前 | MethodBeforeAdvice |
后置通知 | 目标方法执行之后 | AfterAdvice(空接口) |
异常通知 | 目标方法执行过程中抛出异常后 | ThrowsAdvice(空接口) |
返回通知 | 目标方法返回值后 | AfterReturningAdvice |
一般不使用空接口,AfterReturningAdvice是返回通知,但很多时候都可以当作后置通知使用。
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"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 目标类、切面类要放到IOC容器中 --><context:component-scan base-package="com.chy.mall" /><!-- UserService的代理。代理的是接口,name一般就使用接口名;代理的是目标类,name使用xxxProxy --><bean name="userService" class="org.springframework.aop.framework.ProxyFactoryBean"><!-- 指定要代理的接口,如果实现了多个接口,用子元素<list>来写 --><property name="proxyInterfaces" value="service.UserService" /><!-- 指定目标对象 --><property name="target" ref="userService" /><!-- 通过要使用的切面,通过bean的name指定,只能用value,不能用ref --><property name="interceptorNames" value="userServiceAspect" /><!-- 是否直接代理目标类,默认false:代理接口 --><!--<property name="proxyTargetClass" value="false" />--><!-- 返回的代理对象是否是单例,默认为true --><property name="singleton" value="true" /></bean></beans>
说明
示例写的是代理接口,会代理该接口所有的实现类。如果只代理目标类
- 目标类不用实现接口
- 代理中不必配置 proxyInterfaces ,需要将 proxyTargetClass 设置为true
true:直接代理目标类,使用的是cglib动态代理
false:默认值,代理接口,目标类必须实现接口,使用的是jdk动态代理
spring aop需要在配置文件中给每个目标类|接口代理,很繁琐,且配置文件会很庞大。
AspectJ
所需依赖
- spring-aop
- spring-aspects
目标接口
public interface UserService {void login();void logout();
}
目标类
@Service
public class UserServiceImpl implements UserService {@Overridepublic void login() {System.out.println("正在执行login()...");}@Overridepublic void logout() {System.out.println("正在执行logout()...");}}
xml配置方式
切面
@Component
public class UserServiceAspect {/*** 前置通知要调用的方法*/public void before(){System.out.println("正在执行前置通知...");}/*** 后置通知要调用的方法*/public void after(){System.out.println("正在执行后置通知...");}/*** 返回通知要调用的方法*/public void afterReturning(Object obj){System.out.println("正在执行返回通知...");System.out.println("目标方法的返回值是:"+obj);}/*** 异常通知要调用的方法*/public void afterThrowing(JoinPoint point, Exception e){System.out.println("异常信息:"+e.getMessage());}}
/*** 环绕通知*/
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {//前增强System.out.println("正在执行前增强...");//调用目标方法Object object=proceedingJoinPoint.proceed();//后增强System.out.println("正在执行后增强...");return object;
}
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"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><!-- 目标类、切面类要放到IOC容器中 --><context:component-scan base-package="com.chy.mall" /><!-- aop配置 --><aop:config><!-- 全局切入点,所有切面中都可引用。配置切入点只能用id,不能用name --><!--<aop:pointcut id="pointCut" expression="execution(* com.chy.mall.service.*.*(..))" />--><!-- 一个<aop:aspect>配置一个切面。指定切入点、增强 --><aop:aspect ref="userServiceAspect"><!-- 局部切入点,只能在此切面中引用 --><aop:pointcut id="pointCut" expression="execution(* com.chy.mall.service.UserService.*(..))" /><!-- 前置通知 --><aop:before method="before" pointcut-ref="pointCut" /><!-- 切入点也可以现配 --><!-- <aop:before method="before" pointcut="execution(* com.chy.mall.service.UserService.*(..))"/>--><!-- 后置通知 --><aop:after method="after" pointcut-ref="pointCut" /><!-- 返回通知 --><!-- 如果返回通知调用的方法中要使用目标方法的返回值,可以用returning指定将目标方法的返回值传递给哪个形参 --><aop:after-returning method="afterReturning" pointcut-ref="pointCut" returning="obj"/><!-- 异常通知 --><!-- 如果要使用捕获的异常对象,可以用throwing指定将异常对象传递给哪个形参 --><aop:after-throwing method="afterThrowing" pointcut-ref="pointCut" throwing="e"/><!-- 环绕通知 --><!-- ProceedingJoinPoint是JoinPoint的子类,会自动传入 --><!-- <aop:around method="around" pointcut-ref="pointCut" /> --> </aop:aspect></aop:config></beans>
注解配置方式
切面
@Component
@Aspect //标识为切面
public class UserServiceAspect {/*** 配置切入点。此切入点是局部切入点,只能在当前切面中引用*/@Pointcut("execution(* com.chy.mall.service.UserService.*(..))")private void pointCut(){}/*** 前置通知* 切入点均可现配 @Before("execution(* com.chy.mall.service.UserService.*(..))")*/@Before("pointCut()")public void before(){System.out.println("正在执行前置通知...");}/*** 后置通知*/@After("pointCut()")public void after(){System.out.println("正在执行后置通知...");}/*** 返回通知。可将目标方法的返回值传给指定形参*/@AfterReturning(value = "pointCut()",returning = "obj" )public void afterReturning(Object obj){System.out.println("正在执行返回通知...");System.out.println("目标方法的返回值是:"+obj);}/*** 异常通知。可将目标方法的抛出的异常传给指定形参*/@AfterThrowing(value = "pointCut()",throwing = "e")public void afterThrowing(JoinPoint point,Exception e){System.out.println("异常信息:"+e.getMessage());}}
/*** 环绕通知*/
@Around("pointCut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {//前增强System.out.println("正在执行前增强...");//调用目标方法Object object=proceedingJoinPoint.proceed();//后增强System.out.println("正在执行后增强...");return object;
}
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"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><!-- 组件扫描,扫猫标注bean的注解--><context:component-scan base-package="com.chy.mall" /><!-- 启用AspectJ的注解 --><aop:aspectj-autoproxy /></beans>
总结
1、声明是声明为目标类型,注入是注入代理
@Resource(name="userService") //注入的是代理,按名称注入
private UserService userService; //如果代理的是接口,声明为接口类型;如果代理的目标类,声明为目标类
2、常见问题
<context:component-scan base-package=" " />
没有配置组件扫描,或者包未写全,出现bean未定义异常