XML方式是我们在实际项目中使用比较多的,在我们没有切面类类的源代码时或者使用第三方的切面类时,就不能使用注解的方式,而且使用注解方式时,一旦程序编译后就不可以修改了。如果使用XML方式就不一样了,我们只需要修改XML文件就可以。
1.在pom.xml中引入依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.6</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.5</version>
</dependency>
</dependencies>
2.在applicationContext.xml中引入AOP命名空间
<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 ">
3.目标类有实现接口
(1)定义业务接口
使用前面定义的接口!
package com.etc.service;
public interface UserService {
//新增用户,为了简化不体现参数
void addUser();
//修改用户,为了简化不体现参数
void editUser();
}
(2)定义目标实现类
package com.etc.service.impl;
import com.etc.service.UserService;
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("增加一个用户...");
}
@Override
public void editUser() {
System.out.println("修改用户信息...");
}
}
(3)定义切面类
package com.etc.service.impl;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import java.lang.reflect.Method;
//定义切面类
public class MyAspects {
//前置通知
public void beforeAdvice(JoinPoint jp) throws Exception{
String classType = jp.getTarget().getClass().getName();
Class<?> clazz = Class.forName(classType);
String clazzName = clazz.getName();
System.out.println(clazzName);
MethodSignature signature = (MethodSignature) jp.getSignature();
Method method = signature.getMethod();
System.out.println("执行前置通知方法,当前方法名为:" + method.getName());
}
//后置通知
public void afterAdvice(){
System.out.println("执行后置通知方法");
}
//环绕通知
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕通知执行前");
Object result = pjp.proceed();
System.out.println("环绕通知执行后");
return result;
}
}
(4)目标类和切面配置
配置顺序
在Spring配置文件中,所有AOP相关定义必须放在<aop:config>标签下,该标签下可以有<aop:pointcut>、<aop:advisor>、<aop:aspect>标签,配置顺序不可变。
§<aop:pointcut>:用来定义切入点,该切入点可以重用;
§<aop:advisor>:用来定义只有一个通知和一个切入点的切面;
§<aop:aspect>:用来定义切面,该切面可以包含多个切入点和通知,而且标签内部的通知和切入点定义是无序的;和advisor的区别就在此,advisor只包含一个通知和一个切入点。
Point的expression语法
语法:execution(修饰符 返回值 包名.类名/接口名.方法名(参数列表))
其中:
A.返回类型、方法名、参数是必须有的。
B.* 表示任意值. 比如返回类型,方法名等。
C.(..)可以代表所有参数,(*)代表一个参数,(*,String)代表第一个参数为任何值,第二个参数为String类型。
举例说明:
A.任意公共方法的执行:
execution(public * *(..))
B.任何一个以“set”开始的方法的执行:
execution(* set*(..))
C.com.etc.service.UserService 接口的任意方法的执行:
execution(* com.etc.service.UserService.*(..))
D.定义在com.etc.service包里的任意方法的执行:
execution(* com.etc.service.*.*(..))
E.定义在com.etc.service包和所有子包里的任意类的任意方法的执行:
execution(* com.etc.service..*.*(..))
F.定义在com.etc.service包和所有子包里的Test类的任意方法的执行:
execution(* com.etc.service..Test.*(..))")
注意:最靠近(..)的为方法名,靠近.*(..))的为类名或者接口名
applicationContext.xml中bean和aop配置(针对前置通知和后置通知)
<!--目标对象-->
<bean id="userServiceImpl" class="com.etc.service.impl.UserServiceImpl"></bean>
<bean id="myAspects" class="com.etc.service.impl.MyAspects"></bean>
<aop:config>
<aop:pointcut id="pc1" expression="execution(* com.etc.service..*.*(..))"/>
<aop:aspect ref="myAspects">
<!--method指定切面类中的方法,pointcut-ref可以引用已定义的切点-->
<aop:before method="beforeAdvice" pointcut-ref="pc1" />
<!--也可以在pointcut属性中直接定义切点-->
<aop:after method="afterAdvice" pointcut="execution(* com.etc.service..*.*(..))"/>
<!--环绕通知-->
<aop:around method="aroundAdvice" pointcut-ref="pc1" />
</aop:aspect>
</aop:config>
(5)测试
package com.etc.test;
import com.etc.service.UserService;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestAop {
private ApplicationContext context;
@Before
public void init(){
context = new ClassPathXmlApplicationContext("applicationContext.xml");
}
@Test
public void testAdvice(){
UserService service = context.getBean("userServiceImpl",UserService.class);
service.addUser();
service.editUser();
}
}
测试结果
九月 08, 2018 2:16:42 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [applicationContext.xml]
初始化
com.etc.service.impl.UserServiceImpl
执行前置通知方法,当前方法名为:addUser
环绕通知执行前
增加一个用户...
环绕通知执行后
执行后置通知方法
com.etc.service.impl.UserServiceImpl
执行前置通知方法,当前方法名为:editUser
环绕通知执行前
修改用户信息...
环绕通知执行后
执行后置通知方法
4.目标类没有实现接口
(1)定义目标实现类
package com.etc.service.impl;
public class UserServiceImpl2 {
public void addUser() {
System.out.println("增加一个用户...!");
}
public void editUser() {
System.out.println("修改用户信息...!");
}
}
(2)定义切面类
使用前面定义的切面类
(3)目标类和切面配置
<bean id="userServiceImpl2" class="com.etc.service.impl.UserServiceImpl2"></bean>
使用前面定义的切面配置
(4)测试
package com.etc.test;
import com.etc.service.impl.UserServiceImpl2;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestAop4Cglib {
private ApplicationContext context;
@Before
public void init(){
context = new ClassPathXmlApplicationContext("applicationContext.xml");
}
@Test
public void testAdvice(){
UserServiceImpl2 userServiceImpl2 = (UserServiceImpl2)context.getBean("userServiceImpl2");
System.out.println("类名:" + userServiceImpl2.getClass().getName());
userServiceImpl2.addUser();
userServiceImpl2.editUser();
}
}
测试结果
信息: Loading XML bean definitions from class path resource [applicationContext.xml]
初始化
类名:com.etc.service.impl.UserServiceImpl2$$EnhancerBySpringCGLIB$$ebf405eb
com.etc.service.impl.UserServiceImpl2
执行前置通知方法,当前方法名为:addUser
环绕通知执行前
增加一个用户...!
环绕通知执行后
执行后置通知方法
com.etc.service.impl.UserServiceImpl2
执行前置通知方法,当前方法名为:editUser
环绕通知执行前
修改用户信息...!
环绕通知执行后
执行后置通知方法