横切关注点,可以被模块化的特殊类.
白话:定义了我要在程序执行时,在指定地方执行我编写的代码.
定义一个切面.定义了我要在某个地方执行时,执行我编写的切面的代码.
所以我需要定义一个AOP类:
1. 我需要它声明为切面类(用@Aspect注解放在类上实现)
2. 我需要指定切面做什么类型的事情和做什么事情.(五种通知和业务代码)
3. 我需要定义一个切点,用来指定我在什么地方执行切面类的内容(切点)
4. 我需要在配置我的工程,让切面可以被认识.(@Component
和
)
需要的依赖
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aopartifactId>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjrtartifactId>
dependency>
<dependency>
<groupId>aopalliancegroupId>
<artifactId>aopallianceartifactId>
<version>1.0version>
dependency>
切面类
@Aspect //声明切面
@Component //加入容器
public class LogAspect {
protected static final Logger logger = LoggerFactory.getLogger(LogAspect.class);
//定义一个切点(写法一)
@Pointcut("execution(public * com.min.spring.service..*(..))")
public void pt1(){}
//通知中指定上面定义的切点
@Before("pt1()")
public void start(JoinPoint joinPoint) {
logger.info("service start====" + joinPoint.getSignature().getName());
}
@After("pt1()")
public void after(JoinPoint joinPoint) {
logger.info("service after====" + joinPoint.getSignature().getName());
}
//直接在通知中写明起点,不需要专门定义切点(写法二)
@Before("execution(public * com.min.spring.controller..*(..))")
public void cstart(JoinPoint joinPoint) {
logger.info("controller start====" + joinPoint.getSignature().getName());
}
@After("execution(public * com.min.spring.controller..*(..))")
public void cafter(JoinPoint joinPoint) {
logger.info("controller after====" + joinPoint.getSignature().getName());
}
}
配置xml (spring-context.xml 和spring-mvc.xml中都要加上启用aspectJ自动代理的标记)
<aop:aspectj-autoproxy/>
执行结果
2019-08-14 17:45:06,345 8602 [nio-8080-exec-7] INFO om.min.spring.aspect.LogAspect - controller start====login
2019-08-14 17:45:06,346 8603 [nio-8080-exec-7] INFO om.min.spring.aspect.LogAspect - service start====login
2019-08-14 17:45:06,346 8603 [nio-8080-exec-7] INFO om.min.spring.aspect.LogAspect - service start====findByUsername
2019-08-14 17:45:06,517 8774 [nio-8080-exec-7] INFO om.min.spring.aspect.LogAspect - service after====findByUsername
2019-08-14 17:45:06,518 8775 [nio-8080-exec-7] INFO om.min.spring.aspect.LogAspect - service after====login
2019-08-14 17:45:06,518 8775 [nio-8080-exec-7] INFO om.min.spring.aspect.LogAspect - controller after====login
2019-08-14 17:45:06,540 8797 [nio-8080-exec-8] INFO om.min.spring.aspect.LogAspect - controller start====index
2019-08-14 17:45:06,548 8805 [nio-8080-exec-8] INFO om.min.spring.aspect.LogAspect - controller after====index
//任意公共方法的执行:
execution(public * *(..))
//任何一个名字以 set 开始的方法的执行:
execution(* set*(..))
//AccountService 接口定义的任意方法的执行:
execution(* com.xyz.service.AccountService.*(..))
//在 service 包中定义的任意方法的执行:
execution(* com.xyz.service.*.*(..))
//在 service 包或其子包中定义的任意方法的执行:
execution(* com.xyz.service..*.*(..))
//在 service 包中的任意连接点(在 Spring AOP 中只是方法执行):
within(com.xyz.service.*)
//在 service 包或其子包中的任意连接点(在 Spring AOP 中只是方法执行):
within(com.xyz.service..*)
//实现 AccountService 接口的代理对象的任意连接点 (在 Spring AOP 中只是方法执行):
this(com.xyz.service.AccountService)
//实现 AccountService 接口的目标对象的任意连接点 (在 Spring AOP 中只是方法执行):
target(com.xyz.service.AccountService)
//任何一个只接受一个参数,并且运行时所传入的参数是 Serializable 接口的连接点(在 Spring AOP 中只是方法执行):
args(java.io.Serializable)
//请注意在例子中给出的切入点不同于execution(* *(Java.io.Serializable)),args 版本只有在动态运行时候传入参数是 Serializable 时才匹配,而 execution 版本在方法签名中声明只有一个 Serializable 类型的参数时候匹配。
//目标对象中有一个 @Transactional 注解的任意连接点 (在 Spring AOP 中只是方法执行):
@target(org.springframework.transaction.annotation.Transactional)
//任何一个目标对象声明的类型有一个 @Transactional 注解的连接点 (在 Spring AOP 中只是方法执行):
@within(org.springframework.transaction.annotation.Transactional)
任何一个执行的方法有一个 @Transactional 注解的连接点 (在 Spring AOP 中只是方法执行):
@annotation(org.springframework.transaction.annotation.Transactional)
//任何一个只接受一个参数,并且运行时所传入的参数类型具有 @Classified 注解的连接点(在 Spring AOP 中只是方法执行):
@args(com.xyz.security.Classified)
任何一个在名为 tradeService 的 Spring bean 之上的连接点 (在 Spring AOP 中只是方法执行):
bean(tradeService)
//任何一个在名字匹配通配符表达式*Service的 Spring bean 之上的连接点 (在 Spring AOP 中只是方法执行):
bean(*Service)
//其中,this、tagart、args、 @target、 @with、 @annotation和@args在绑定表单中更加常用
spring-context.xml 和spring-mvc.xml中都要加上启用aspectJ自动代理的标记
<aop:aspectj-autoproxy/>
spring-context.xml 和spring-mvc.xml中不要分包扫描,
<context:component-scan base-package="com.min.spring">
context:component-scan>
在一般的项目配置web.xml中,Spring容器是由ContextLoaderListener
生成,而SpringMVC容器是由DispatcherServlet
来加载, Spring容器是父容器, SpringMVC容器是子容器.
子容器(SpringMVC容器
)可以访问父容器(Spring容器
)的内容,反之则不行
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring-context*.xmlparam-value>
context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
listener-class>
listener>
<servlet>
<servlet-name>springServletservlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath*:/spring-mvc*.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
在扫描包的时候父容器(Spring容器
)负责扫描@Controller
以外的注解,
子容器(SpringMVC容器
)负责只扫描@Controller
注解.
spring-context.xml
<context:component-scan base-package="com.min.spring">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
context:component-scan>
spring-mvc.xml
<context:component-scan base-package="com.min.spring"
use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
context:component-scan>
如果你写的切面既有Controller的,又有Service层的时候, 在父容器和子容器中都需要加上启用aspectJ自动代理
如果在只在一个xml中的时候,产生的自动代理只是对应容器的的部分.
所以要在2个容器的xml中都配置启动aspectJ自动代理.
spring-context.xml 和spring-mvc.xml
<aop:aspectj-autoproxy/>
如果在只在一个xml中的时候,产生的自动代理只是对应容器的的部分.