ssm:spring面向切面编程-----AOP编程初试身手

1.传统编程模式的弊端
通过以下案例你将会发现,虽然在调用addUser()方法时实现了日志的输出,但这个程序有一个问题,
就是不得不在addUser()方法的主业务代码前面添加"log.info(“开始添加用户。。。”)语句,
以及在主业务代码后面添加"log.info("完成添加用户。。。)"语句。
这些语句以硬编码的方式混入主业务代码中,难以分割,可移植性差。

解决问题的思路是将这些日志之类的功能独立出来,作为独立的一个或多个类,在需要时调用,
而且最好不是显示调用,因为如果时显示调用,仍然要在目标方法前面或后面嵌入代码,改进效果有限,最好能自动调用。
这种设想就是一种面向切面编程(AOP)的思想。

应用实例:创建一个分层架构项目,在添加用户的同时输出日志。

项目目录结构图
ssm:spring面向切面编程-----AOP编程初试身手

IUserDao.java

package com.xiaochen.dao;

public interface IUserDao {
	public void addUser();
}

UserDaoImpl.java

package com.xiaochen.dao;

import org.springframework.stereotype.Component;

//使用注解方式定义一个Bean,其id为userDao
@Component("userDao")
public class UserDaoImpl implements IUserDao{
	@Override
	public void addUser() {
		System.out.println("新增一个用户到数据库中");
		
	}

}

UserService.java

package com.xiaochen.service;

import javax.annotation.Resource;

import org.springframework.stereotype.Component;

import com.sun.istack.internal.logging.Logger;
import com.xiaochen.dao.IUserDao;

//使用注解方式定义Bean,其id为userService
@Component("userService")
public class UserService {
	//使用注解注入在UserDaoImpl定义好的Bean
	@Resource(name="userDao")
	private IUserDao userDao;
	//用于实现在addUser操作时记录日志
	private static final Logger log=Logger.getLogger(UserService.class);
	
	//给域属性赋值
	public void setUserDao(IUserDao userDao) {
		this.userDao=userDao;
	}
	
	//调用IUserDao的addUser()方法
	public void addUser() {
		log.info("使用传统方式测试开始添加用户。。。");
		userDao.addUser();
		log.info("使用传统方式测试完成添加用户。。。");
	}
}

Test.java

package com.xiaochen.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.xiaochen.service.UserService;

public class Test {

	public static void main(String[] args) {
		//1.加载配置文件
		ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
		
		//2.从配置文件中获取Bean
		UserService userService=(UserService)context.getBean("userService");
		
		//3.使用Bean
		userService.addUser();

	}

}

log4j.properties

# Global logging configuration
log4j.rootLogger=ERROR, stdout
# MyBatis logging configuration...
log4j.logger.com.xiaochen=DEBUG
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

applicationContext.xm

<?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" 
    xsi:schemaLocation=
       "http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd"> 
        
	<!-- 配置组件扫描器,在指定的基本包中扫描注解 -->
	<context:component-scan base-package="com.xiaochen"/>
	
</beans>

运行结果图
ssm:spring面向切面编程-----AOP编程初试身手

2.AOP的一些基本概念
a.切面:
一个单独的类,通常在此类种定义一些辅助功能或系统功能的方法。

b.切点:
主业务类的有些方法只想专注于完成核心业务逻辑,不想混入一些辅助性的功能,
可以把这些方法定义为切点,切点就是确定什么位置放置切面。

c.通知:
切点确定了使用切面的位置,但什么时候应用切面就由通知来确定。

d.织入:
切面和切点都是独立的功能类,通过织入才能让切面切入切点,
所以织入就是一种配置过程,让切面能准确的切入指定的位置。

应用实例:通过面向切面编程方法改造上面这个项目。
项目目录结构图
ssm:spring面向切面编程-----AOP编程初试身手

关键代码

<!--定义切面-->
	<bean id="loggerBefore" class="com.xiaochen.aop.LoggerBefore"/>
	<bean id="loggerAfter" class="com.xiaochen.aop.LoggerAfter"/>
	
	
	<aop:config>
		<!-- 定义切点 -->
		<aop:pointcut expression="execution(* com.xiaochen.service.UserService.addUser())" id="pointcut"/>
			
	<!-- 通知切点,切入advice-ref指定的bean(切面)里面的方法,切入位置在前还是在后由切面的接口决定 -->
		<aop:advisor pointcut-ref="pointcut" advice-ref="loggerBefore"/>
		<aop:advisor pointcut-ref="pointcut" advice-ref="loggerAfter"/>
	</aop:config>	

需要修改的地方与主要步骤
1.删除日志相关代码
2.新建包aop用于定义两个类作为切面(一个类实现前置通知接口,一个类实现后置通知接口)
3.在spring配置文件中引入aop命名空间,依次完成:定义切面,定义切点,通知切点

改动的代码只有1处(即删除UserService中addUser()方法前后的两句日志相关代码),其他代码同上一个传统项目

applicationContext.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/context 
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">	
        
    <!-- 配置组件扫描器,在指定的基本包中扫描注解 -->
	<context:component-scan base-package="com.xiaochen"/>
	
	<!--定义切面-->
	<bean id="loggerBefore" class="com.xiaochen.aop.LoggerBefore"/>
	<bean id="loggerAfter" class="com.xiaochen.aop.LoggerAfter"/>
	
	
	<aop:config>
		<!-- 定义切点 -->
		<aop:pointcut expression="execution(* com.xiaochen.service.UserService.addUser())" id="pointcut"/>
			
	<!-- 通知切点,切入advice-ref指定的bean(切面)里面的方法,切入位置在前还是在后由切面的接口决定 -->
		<aop:advisor pointcut-ref="pointcut" advice-ref="loggerBefore"/>
		<aop:advisor pointcut-ref="pointcut" advice-ref="loggerAfter"/>
	</aop:config>	
</beans>

LoggerBefore.java

package com.xiaochen.aop;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

import com.sun.istack.internal.logging.Logger;

//实现前置通知接口,意味着将来这个方法会作用到目标的开始位置
public class LoggerBefore implements MethodBeforeAdvice{
	private static final Logger log=Logger.getLogger(LoggerBefore.class);
	@Override
	public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
		log.info("aop方式,开始添加学生。。。");
	}
	
}

LoggerAfter.java

package com.xiaochen.aop;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

import com.sun.istack.internal.logging.Logger;

public class LoggerAfter implements AfterReturningAdvice{
	private static final Logger log=Logger.getLogger(LoggerAfter.class);
	@Override
	public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
		log.info("aop方式,完成添加学生。。。");
	}

}

运行结果图
ssm:spring面向切面编程-----AOP编程初试身手

上一篇:ubuntu中创建新用户并添加管理员权限


下一篇:tp6 加 微信小程序获取opendid (附带源代码)