1、AOP可配置元素
使用XML配置开发Spring AOP需要引入AOP命名空间,所以首先了解一下AOP可配置的元素,具体如下表所示:
AOP配置元素 | 用途 | 备注 |
aop:advisor | 定义AOP的通知器 | 一种较老的方式,目前很少使用 |
aop:aspect | 定义一个切面 | —— |
aop:before | 定义前置通知 | —— |
aop:after | 定义后置通知 | —— |
aop:around | 定义环绕通知 | —— |
aop:after-returning | 定义返回通知 | —— |
aop:after-throwing | 定义异常通知 | —— |
aop:config | 顶层的AOP配置元素 | AOP的配置是以它为开始的 |
aop:declare-parents | 给通知引入新的额外接口,增强功能 | —— |
aop:pointcut | 定义切点 | —— |
2、开发环境搭建
在Spring_Demo项目中创建名为“XMLForAOP”的模块module,并在lib文件夹中导入需要的jar包,并Add as Library,具体如下所示:
在com.ccff.spring.xml.aop.pojo包下创建名为“Role”的角色类,具体代码如下所示:
package com.ccff.spring.xml.aop.pojo;
public class Role {
private Long roleId;
private String roleName;
private String roleNote;
public Role() {
}
public Role(Long roleId, String roleName, String roleNote) {
this.roleId = roleId;
this.roleName = roleName;
this.roleNote = roleNote;
}
public Long getRoleId() {
return roleId;
}
public void setRoleId(Long roleId) {
this.roleId = roleId;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getRoleNote() {
return roleNote;
}
public void setRoleNote(String roleNote) {
this.roleNote = roleNote;
}
@Override
public String toString() {
return "Role{" +
"roleId=" + roleId +
", roleName='" + roleName + '\'' +
", roleNote='" + roleNote + '\'' +
'}';
}
}
然后在com.ccff.spring.xml.aop.service包下创建名为“RoleService”的接口,具体代码如下所示:
package com.ccff.spring.xml.aop.service;
import com.ccff.spring.xml.aop.pojo.Role;
public interface RoleService {
void printRole(Role role);
}
并在该包下创建名为“RoleServiceImpl”的实现类,实现RoleService接口,具体代码如下:
package com.ccff.spring.xml.aop.service;
import com.ccff.spring.xml.aop.pojo.Role;
public class RoleServiceImpl implements RoleService {
@Override
public void printRole(Role role) {
System.out.println("【打印角色信息】"+role.toString());
}
}
最后,在com.ccff.spring.xml.aop.aspect包下创建名为“XMLRoleAspect”的切面类,具体代码如下所示:
package com.ccff.spring.xml.aop.aspect;
public class XMLRoleAspect {
public void before(){
System.out.println("Before方法执行......");
}
public void after(){
System.out.println("After方法执行......");
}
public void afterReturning(){
System.out.println("AfterReturning方法执行......");
}
public void afterThrowing(){
System.out.println("AfterThrowing方法执行......");
}
}
3、前置通知、后置通知、返回通知和异常通知
首先通过引入的XML定义AOP的命名空间,然后定义了一个RoleService类和切面XMLRoleAspect类,最后通过aop:config去定义AOP的内容信息。具体配置如下所示:
<?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-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<bean id="xmlRoleAspect" class="com.ccff.spring.xml.aop.aspect.XMLRoleAspect" />
<bean id="roleService" class="com.ccff.spring.xml.aop.service.RoleServiceImpl" />
<aop:config>
<!--引用XMLRoleAspect作为切面类-->
<aop:aspect ref="xmlRoleAspect">
<!--定义切点-->
<aop:pointcut id="printRole" expression="execution(* com.ccff.spring.xml.aop.service.RoleServiceImpl.printRole(..))" />
<!--定义通知-->
<aop:before method="before" pointcut-ref="printRole" />
<aop:after method="after" pointcut-ref="printRole" />
<aop:after-returning method="afterReturning" pointcut-ref="printRole" />
<aop:after-throwing method="afterThrowing" pointcut-ref="printRole" />
</aop:aspect>
</aop:config>
</beans>
通过上面的配置就可以定义切点并进行引入,同时避免了多次书写同一正则表达式的麻烦,至此我们通过XML的配置成功定义了前置通知、后置通知、返回通知和异常通知。
4、环绕通知
和其他通知一样,环绕通知也可以织入到约定的流程当中,修改com.ccff.spring.xml.aop.aspect包中的XMLRoleAspect切面类,为其增加环绕通知,具体修改代码如下所示:
package com.ccff.spring.xml.aop.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
public class XMLRoleAspect {
public void before(){
System.out.println("Before方法执行......");
}
public void after(){
System.out.println("After方法执行......");
}
public void afterReturning(){
System.out.println("AfterReturning方法执行......");
}
public void afterThrowing(){
System.out.println("AfterThrowing方法执行......");
}
public void around(ProceedingJoinPoint joinPoint){
System.out.println("Around方法执行前......");
try {
joinPoint.proceed();
} catch (Throwable throwable) {
new RuntimeException("回调原有流程,产生异常");
}
System.out.println("Around方法执行后......");
}
}
然后修改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: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.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<bean id="xmlRoleAspect" class="com.ccff.spring.xml.aop.aspect.XMLRoleAspect" />
<bean id="roleService" class="com.ccff.spring.xml.aop.service.RoleServiceImpl" />
<aop:config>
<!--引用XMLRoleAspect作为切面类-->
<aop:aspect ref="xmlRoleAspect">
<!--定义切点-->
<aop:pointcut id="printRole" expression="execution(* com.ccff.spring.xml.aop.service.RoleServiceImpl.printRole(..))" />
<!--定义通知-->
<aop:before method="before" pointcut-ref="printRole" />
<aop:after method="after" pointcut-ref="printRole" />
<aop:around method="around" pointcut-ref="printRole" />
<aop:after-throwing method="afterThrowing" pointcut-ref="printRole" />
<aop:after-returning method="afterReturning" pointcut-ref="printRole" />
</aop:aspect>
</aop:config>
</beans>
在com.ccff.spring.xml.aop.test包下创建名为“XMLAOPTest”的测试类,具体代码如下所示:
package com.ccff.spring.xml.aop.test;
import com.ccff.spring.xml.aop.pojo.Role;
import com.ccff.spring.xml.aop.service.RoleService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class XMLAOPTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
RoleService roleService = (RoleService) context.getBean("roleService");
Role role = new Role();
role.setRoleId(1L);
role.setRoleName("role-name-1");
role.setRoleNote("role-note-1");
roleService.printRole(role);
}
}
运行该测试类,查看输出到控制台上的日志信息如下:
5、给通知传递参数
通过XML的配置,也可以引入参数到通知中,以前置通知为例,首先修改com.ccff.spring.xml.aop.aspect包下的切面类XMLRoleAspect中的before方法,具体修改如下所示:
package com.ccff.spring.xml.aop.aspect;
import com.ccff.spring.xml.aop.pojo.Role;
import org.aspectj.lang.ProceedingJoinPoint;
public class XMLRoleAspect {
public void before(Role role){
System.out.println("Before方法执行,打印Role对象:"+role);
}
public void after(){
System.out.println("After方法执行......");
}
public void afterReturning(){
System.out.println("AfterReturning方法执行......");
}
public void afterThrowing(){
System.out.println("AfterThrowing方法执行......");
}
public void around(ProceedingJoinPoint joinPoint){
System.out.println("Around方法执行前......");
try {
joinPoint.proceed();
} catch (Throwable throwable) {
new RuntimeException("回调原有流程,产生异常");
//throwable.printStackTrace();
}
System.out.println("Around方法执行后......");
}
}
然后再修改配置文件,具体配置修改如下:
<?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-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<bean id="xmlRoleAspect" class="com.ccff.spring.xml.aop.aspect.XMLRoleAspect" />
<bean id="roleService" class="com.ccff.spring.xml.aop.service.RoleServiceImpl" />
<aop:config>
<!--引用XMLRoleAspect作为切面类-->
<aop:aspect ref="xmlRoleAspect">
<!--定义切点-->
<aop:pointcut id="printRole" expression="execution(* com.ccff.spring.xml.aop.service.RoleServiceImpl.printRole(..))" />
<!--定义通知-->
<aop:before method="before" pointcut="execution(* com.ccff.spring.xml.aop.service.RoleServiceImpl.printRole(..)) and args(role)" />
<aop:after method="after" pointcut-ref="printRole" />
<aop:around method="around" pointcut-ref="printRole" />
<aop:after-throwing method="afterThrowing" pointcut-ref="printRole" />
<aop:after-returning method="afterReturning" pointcut-ref="printRole" />
</aop:aspect>
</aop:config>
</beans>
这里需要注意的是,在通过注解的方式为通知传递参数时使用的是&,而通过XML配置为通知传递参数时,由于在XML中&符号具有特殊含义,因此采用and代替。
最后再次运行测试类XMLAOPTest,查看输出到控制台上的日志信息如下:
6、引入
在注解当中,无论是使用JDK动态代理,还是CGLIB动态代理都可以将代理对象下挂到多个接口之下,这样就能够引入新的方法了,注解能做到的事情通过XML也可以实现。
仍然以在注解中使用的案例功能讲解,即对于printRole方法而言,如果角色为空时不再打印,那么要引入一个新的检测器对其进行检测。
首先在com.ccff.spring.xml.aop.verifier包下创建名为“RoleVerifier”的验证器接口,具体代码如下所示:
package com.ccff.spring.xml.aop.verifier;
import com.ccff.spring.xml.aop.pojo.Role;
public interface RoleVerifier {
boolean verify(Role role);
}
然后在该包中创建名为“RoleVerifierImpl”的类实现RoleVerifier接口,具体代码如下所示:
package com.ccff.spring.xml.aop.verifier;
import com.ccff.spring.xml.aop.pojo.Role;
public class RoleVerifierImpl implements RoleVerifier {
@Override
public boolean verify(Role role) {
return role != null;
}
}
接着修改com.ccff.spring.xml.aop.aspect包下的切面类XMLRoleAspect,为其添加一个新的属性RoleVerifier类的对象,具体代码如下所示:
package com.ccff.spring.xml.aop.aspect;
import com.ccff.spring.xml.aop.pojo.Role;
import com.ccff.spring.xml.aop.verifier.RoleVerifier;
import org.aspectj.lang.ProceedingJoinPoint;
public class XMLRoleAspect {
public RoleVerifier roleVerifier = null;
public void before(Role role){
System.out.println("Before方法执行,打印Role对象:"+role);
}
public void after(){
System.out.println("After方法执行......");
}
public void afterReturning(){
System.out.println("AfterReturning方法执行......");
}
public void afterThrowing(){
System.out.println("AfterThrowing方法执行......");
}
public void around(ProceedingJoinPoint joinPoint){
System.out.println("Around方法执行前......");
try {
joinPoint.proceed();
} catch (Throwable throwable) {
new RuntimeException("回调原有流程,产生异常");
//throwable.printStackTrace();
}
System.out.println("Around方法执行后......");
}
}
最后,使用配置文件进行配置,配置的内容和注解引入的方法相当,它是使用aop:declare-parents去引入的,具体配置如下所示:
<?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-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<bean id="xmlRoleAspect" class="com.ccff.spring.xml.aop.aspect.XMLRoleAspect" />
<bean id="roleService" class="com.ccff.spring.xml.aop.service.RoleServiceImpl" />
<aop:config>
<!--引用XMLRoleAspect作为切面类-->
<aop:aspect ref="xmlRoleAspect">
<!--定义切点-->
<aop:pointcut id="printRole" expression="execution(* com.ccff.spring.xml.aop.service.RoleServiceImpl.printRole(..))" />
<!--定义通知-->
<aop:before method="before" pointcut="execution(* com.ccff.spring.xml.aop.service.RoleServiceImpl.printRole(..)) and args(role)" />
<aop:after method="after" pointcut-ref="printRole" />
<aop:around method="around" pointcut-ref="printRole" />
<aop:after-throwing method="afterThrowing" pointcut-ref="printRole" />
<aop:after-returning method="afterReturning" pointcut-ref="printRole" />
<!--通过XML引入新功能,对目标类进行加强-->
<aop:declare-parents types-matching="com.ccff.spring.xml.aop.service.RoleServiceImpl+" implement-interface="com.ccff.spring.xml.aop.verifier.RoleVerifier" default-impl="com.ccff.spring.xml.aop.verifier.RoleVerifierImpl" />
</aop:aspect>
</aop:config>
</beans>
修改com.ccff.spring.xml.aop.test包下的测试类XMLAOPTest,具体代码如下所示:
package com.ccff.spring.xml.aop.test;
import com.ccff.spring.xml.aop.pojo.Role;
import com.ccff.spring.xml.aop.service.RoleService;
import com.ccff.spring.xml.aop.verifier.RoleVerifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class XMLAOPTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
RoleService roleService = (RoleService) context.getBean("roleService");
Role role = new Role();
role.setRoleId(1L);
role.setRoleName("role-name-1");
role.setRoleNote("role-note-1");
RoleVerifier roleVerifier = (RoleVerifier) roleService;
if (roleVerifier.verify(role))
roleService.printRole(role);
else
System.out.println("Role对象为空");
System.out.println("===============测试role为空========================");
role = null;
if (roleVerifier.verify(role))
roleService.printRole(role);
else
System.out.println("Role对象为空");
}
}
运行该测试类后,查看输出到控制台的日志信息如下: