阅读原文
之前关于切面的创建我们都是基于Java配置讲解的,这篇我们将以XML配置的方式来对之前的讲解做一次回顾,以下的代码我就不做讲解了,只是换了一种配置方式,实现原理之前都讨论过了,我们直接看代码。
一、创建一个简单的切面
1、创建UserService接口以及实现类
public interface UserService {
public boolean saveUser(Long id, String userName);
}
public class UserServiceImpl implements UserService {
@Override
public boolean saveUser(Long id, String userName) {
return false;
}
}
2、创建一个简单的切面类
public class SimpleAspect {
public static Logger logger = LoggerFactory.getLogger(SimpleAspect.class);
/**
* 前置通知方法
*/
public void before() {
logger.info("*************执行方法前调用*************");
}
/**
* 后置通知方法
*/
public void after() {
logger.info("*************执行方法后或者抛出异常后调用*************");
}
/**
* 异常通知方法
*/
public void throwable() {
logger.info("*************执行方法异常后调用*************");
}
/**
* 返回通知方法
*/
public void returning() {
logger.info("*************执行方法后后调用, 异常不会调用*************");
}
}
3、基于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">
<!-- ****************简单切面配置********************** -->
<!-- 启用动态代理 -->
<aop:aspectj-autoproxy/>
<!-- 配置userServiceBean -->
<bean id="userService" class="com.icypt.learn.service.impl.UserServiceImpl"/>
<bean id="simpleAspect" class="com.icypt.learn.aspect.SimpleAspect"/>
<!-- aop配置-->
<aop:config>
<aop:aspect ref="simpleAspect">
<!--定义切点-->
<aop:pointcut id="process" expression="execution(* com.icypt.learn.service.UserService.saveUser(..))"/>
<!--前置通知-->
<aop:before method="before" pointcut-ref="process"/>
<!--后置通知-->
<aop:after method="after" pointcut-ref="process"/>
<!--异常通知-->
<aop:after-throwing method="throwable" pointcut-ref="process"/>
<!--返回通知-->
<aop:after-returning method="returning" pointcut-ref="process"/>
</aop:aspect>
</aop:config>
</beans>
4、编写测试类
public class TestUserService {
public static Logger logger = LoggerFactory.getLogger(TestUserService.class);
public static ClassPathXmlApplicationContext cpxac = new ClassPathXmlApplicationContext("simpleAspect.xml");
@Test
public void testSaveUser() {
UserService userService = cpxac.getBean("userService", UserService.class);
//调用目标方法
boolean flag = userService.saveUser(1l,"daguo");
logger.info("***********执行目标类saveUser方法结果:" + flag + "**********");
}
}
5、运行测试类
21:32:20,689 INFO main aspect.SimpleAspect:21 - *************执行方法前调用*************
21:32:20,689 INFO main aspect.SimpleAspect:28 - *************执行方法后或者抛出异常后调用*************
21:32:20,689 INFO main aspect.SimpleAspect:42 - *************执行方法后后调用, 异常不会调用*************
21:32:20,689 INFO main service.TestUserService:25 - ***********执行目标类saveUser方法结果:false**********
6、结果分析
通过运行结果可以发现我们配置简单切面已经正常运行了,下面就个配置节点予以说明:
:声明一个Bean组件,功能与@Bean或者@Component注解相同。
<aop:aspectj-autoproxy/>
:表示开启自动代理,功能与@EnableAspectJAutoProxy注解相同。
<aop:aspect/>
:表示配置一个切面,功能与@Aspect注解相同。
<aop:pointcut/>
:表示配置一个切点,功能与@Pointcut注解相同。
<aop:before/>
:表示配置前置通知,功能与@Before注解相同。
<aop:after/>
:表示配置后置通知,功能与@After注解相同。
<aop:after-throwing/>
:表示配置异常通知,功能与@AfterThrowing注解相同。
<aop:after-returning/>
:表示配置返回通知,功能与@AfterReturning注解相同。
二、创建一个环绕通知的切面
1、创建一个环绕通知的切面类
public class AroundAspect {
public static Logger logger = LoggerFactory.getLogger(AroundAspect.class);
public Object around(ProceedingJoinPoint pjp) {
Object object = null;
logger.info("*************执行方法前调用*************");
try {
object = pjp.proceed();
logger.info("*************执行方法后后调用, 异常不会调用*************");
} catch (Throwable throwable) {
logger.error("*************执行方法异常后调用*************", throwable);
} finally {
logger.info("*************执行方法后或者抛出异常后调用*************");
}
return object;
}
}
2、基于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">
<!-- *******************切面环绕通知配置 -->
<!-- 启用动态代理 -->
<aop:aspectj-autoproxy/>
<!-- 配置Bean -->
<bean id="userService" class="com.icypt.learn.service.impl.UserServiceImpl"/>
<bean id="aroundAspect" class="com.icypt.learn.aspect.AroundAspect"/>
<!-- aop配置-->
<aop:config>
<aop:aspect ref="aroundAspect">
<!--定义切点-->
<aop:pointcut id="process" expression="execution(* com.icypt.learn.service.UserService.saveUser(..))"/>
<!--前置通知-->
<aop:around method="around" pointcut-ref="process"/>
</aop:aspect>
</aop:config>
</beans>
3、修改测试类
public static ClassPathXmlApplicationContext cpxac = new ClassPathXmlApplicationContext("aroundAspect.xml");
4、运行测试类
22:15:57,270 INFO main aspect.AroundAspect:20 - *************执行方法前调用*************
22:15:57,270 INFO main aspect.AroundAspect:23 - *************执行方法后后调用, 异常不会调用*************
22:15:57,270 INFO main aspect.AroundAspect:27 - *************执行方法后或者抛出异常后调用*************
22:15:57,270 INFO main service.TestUserService:25 - ***********执行目标类saveUser方法结果:false**********
5、结果分析
由运行结果可知我们配置的环绕通知的切面已正常共工作,其中<aop:around/>
表示配置的是一个环绕通知,与@Around注解功能相同。
三、创建一个接受参数的切面
1、创建一个接受参数的切面类
public class GetParamsAspect {
public static Logger LOGGER = LoggerFactory.getLogger(GetParamsAspect.class);
public void before(Long userId, String userName) {
LOGGER.info("***********************进入前置通知**********************************");
LOGGER.info("前置通知接受的userId:" + userId);
LOGGER.info("前置通知接受的userName:" + userName);
}
}
2、基于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">
<!-- *****************切面获取参数配置********************* -->
<!-- 启用动态代理 -->
<aop:aspectj-autoproxy/>
<!-- 配置Bean -->
<bean id="userService" class="com.icypt.learn.service.impl.UserServiceImpl"/>
<bean id="getParamsAspect" class="com.icypt.learn.aspect.GetParamsAspect"/>
<!-- aop配置-->
<aop:config>
<aop:aspect ref="getParamsAspect">
<!--定义切点-->
<aop:pointcut id="process" expression="execution(* com.icypt.learn.service.UserService.saveUser(Long,String)) and args(userId, userName)"/>
<!--前置通知-->
<aop:before method="before" pointcut-ref="process"/>
</aop:aspect>
</aop:config>
</beans>
3、修改测试类
public static ClassPathXmlApplicationContext cpxac = new ClassPathXmlApplicationContext("getParamsAspect.xml");
4、运行测试类
22:22:54,766 INFO main aspect.GetParamsAspect:12 - ***********************进入前置通知**********************************
22:22:54,782 INFO main aspect.GetParamsAspect:13 - 前置通知接受的userId:1
22:22:54,782 INFO main aspect.GetParamsAspect:14 - 前置通知接受的userName:daguo
22:22:54,782 INFO main service.TestUserService:25 - ***********执行目标类saveUser方法结果:false**********
5、结果分析
通过运行结果可知我们已经能够成功获取目标方法执行时携带的参数,需要注意的是args()切点指示器指定的参数名称必须和目标方法的参数名称保持一致。
四、创建一个引入功能的切面
1、增加引入功能的接口以及实现类
public interface EnhanceUserService {
public boolean update();
}
public class EnhanceUserServiceImpl implements EnhanceUserService {
public static Logger logger = LoggerFactory.getLogger(EnhanceUserServiceImpl.class);
@Override
public boolean update() {
logger.info("************执行增强后新增的update方法****************");
return false;
}
}
2、基于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">
<!-- *******************切面引入功能配置********************** -->
<!-- 启用动态代理 -->
<aop:aspectj-autoproxy/>
<!-- 配置Bean -->
<bean id="userService" class="com.icypt.learn.service.impl.UserServiceImpl"/>
<bean id="aroundAspect" class="com.icypt.learn.aspect.AroundAspect"/>
<!-- aop配置-->
<aop:config>
<aop:aspect>
<aop:declare-parents types-matching="com.icypt.learn.service.UserService+"
<!--扩展功能的接口-->
implement-interface="com.icypt.learn.service.EnhanceUserService"
<!--扩展功能的默认实现-->
default-impl="com.icypt.learn.service.impl.EnhanceUserServiceImpl"/>
</aop:aspect>
</aop:config>
</beans>
3、创建测试类
public class TestEnhanceUserService {
public static Logger logger = LoggerFactory.getLogger(TestEnhanceUserService.class);
public static ClassPathXmlApplicationContext cpxac = new ClassPathXmlApplicationContext("enhanceAspect.xml");
@Test
public void testSaveUser() {
UserService userService = cpxac.getBean("userService", UserService.class);
//调用目标方法
boolean flag = userService.saveUser(1l,"daguo");
logger.info("***********执行目标类saveUser方法结果:" + flag + "**********");
//调用目标类引入方法
EnhanceUserService enhanceUserService = (EnhanceUserService) userService;
enhanceUserService.update();
}
}
4、运行测试类
22:34:50,859 INFO main service.TestEnhanceUserService:24 - ***********执行目标类saveUser方法结果:false**********
22:34:50,859 INFO main impl.EnhanceUserServiceImpl:14 - ************执行增强后新增的update方法****************
5、结果分析
由运行结果可以看到,我们通过扩展接口新增的方法已经成功执行,可见切面引入功能正常。
<aop:declare-parents/>
:表示将创建一个代理类同时代理目标类和功能扩展类,这样调用端就可以同时调用目标类和功能扩展类的方法了。到此,我们关于Spring的核心依赖注入和切面编程已全部讲解完毕,接下来我们将进入SpringWeb的学习。
关于之前的讲解大家有什么疑问或者觉得我的理解有偏差,可以在公众号或者qq留言,我会及时更正。
更多最新技术文章,请关注“冰点IT”公众号