1. 程式人生 > >spring關於面向切面程式設計AOP的操作

spring關於面向切面程式設計AOP的操作

首先說明:用一個要在進行增刪改操作的時候進行日誌資訊新增的操作來介紹AOP

1、首先需要在spring配置檔案applicationContext.xml中進行相應的配置

配置<aop:aspectj-autoproxy/>這樣一句話

<?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:p="http://www.springframework.org/schema/p"
	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-3.0.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">
	
	<!-- 開啟spring容器的自動掃描功能 -->
	<context:component-scan base-package="com.gezhi.springaop"></context:component-scan>

	<!-- 開啟切面的動態代理支援 -->
	<aop:aspectj-autoproxy/>
</beans>

2、編寫AOP類(切入點定位在某個方法上)這個不是日誌操作程式碼,只是介紹怎樣編寫

1)該類使用@Aspect註解宣告為一個切面物件,同時使用@Component註解將這個類丟進spring容器

2)寫一個方法作為切入點,在該方法上寫上@Pointcut註解定義切入點在哪

3)設定通知方式,寫一個方法,在方法上寫上@Before這種型別的通知註解,需要哪些視情況而定。


/**
 * 該類主要完成切面功能的演示
 * @Aspect 表示將一個Java類定義為一個切面
 * 一個切面就是一個業務交叉功能的處理面
 * @author Administrator
 *
 */
//@Component
//@Aspect
public class DemoAspectJ {

	/**
	 * @Pointcut制定切入點 切面關注物件的標準
	 * 
	 * execution()主要用來定義   切面上的通知(程式碼),什麼時候可以開啟執行
	 * 語法格式:execution(訪問修飾符? 返回型別  包結構.類結構? 方法名(引數列表) 丟擲的異常?)
	 * 
	 * ?代表可以省略
	 * 
	 *     * 表示通配        ..表示0-N個引數
	 * 
	 */
	@Pointcut("execution(* com.gezhi.springaop.*mag.service.impl.*ServiceImpl.*(..))")
	public void pointcut() {}
	
	/**
	 * 前置通知
	 * @param point 固定引數  連線點(代表的是:切面 和 正在執行的目標方法 取得連線的一個點)
	 */
	@Before("pointcut()")
	public void beforeAdvice(JoinPoint point) {
		Object obj = point.getTarget();//獲得目標物件(代理物件)
		String method = point.getSignature().getName();//獲取正在執行的目標方法
		Object[] params = point.getArgs();//獲得目標方法獲取到的引數
		
		
		System.out.println("目標物件是" + obj);
		System.out.println("目標方法是" + method);
		System.out.println("獲取到的引數是" + Arrays.toString(params));
		
		//通過反射取得目標物件的類物件
		Class<?> cls = obj.getClass();
		try {
			Field f = cls.getDeclaredField("encoding");
			f.setAccessible(true);//設定私有屬性更改許可權
			f.set(obj, "utf-8");
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
		
		System.out.println("我是前置通知,我執行在" + method + "執行之前!");
	}
	
	
	
	/**
	 * @AfterReturning 定義後置返回通知,該通知執行在目標方法 正常執行之後
	 * @param point 連線點
	 * @param obj 返回物件
	 */
	@AfterReturning(pointcut="pointcut()",returning="ret")
	public void afterReturningAdvice(JoinPoint point,Object ret) {
		String method = point.getSignature().getName();//獲取正在執行的目標方法
		System.out.println("後置返回通知,我執行在"+method+"正常執行之後,我收到的返回是:" + ret);
		
	}
	
	/**
	 * @AfterThrowing 定義後置異常通知,該通知執行在目標方法 丟擲異常之後
	 * @param point
	 * @param e
	 */
	@AfterThrowing(pointcut="pointcut()",throwing="e")
	public void afterThrowingAdvice(JoinPoint point,Exception e) {
		
		System.out.println("我是後置異常通知,我接收的異常是:" + e);
	}
	
	/**
	 * @After 定義後置返回通知 ,該通知物件,不管目標方法是否正常執行成功,該通知都要執行
	 * 該通知執行在@AfterThrowing || @AfterReturning 之前
	 * @param point
	 */
	@After("pointcut()")
	public void afterAdvice(JoinPoint point) {
		System.out.println("我是後置返回通知,我執行在目標方法返回結果或異常之前!");
	}
	
	
	
	
	@Around("pointcut()")
	public Object aroundAdive(ProceedingJoinPoint point) {
		Object obj = point.getTarget();//獲得目標物件(代理物件)
		String method = point.getSignature().getName();//獲取正在執行的目標方法
		Object[] params = point.getArgs();//獲得目標方法獲取到的引數
		
		params[0] = new UserBean();//可以修改引數
		Object ret = null;
		try {
			ret = point.proceed(params);//還可以控制目標方法的執行
			
			ret = new UserBean();//可以修改返回
			
			
		} catch (Throwable e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return ret;
	}
	
}

2、編寫AOP類(切入點定位在某個註解上)這個是關於日誌操作的

下面是定位到@MyLog註解上

@Component
@Aspect
public class OptLogAspectJ {

	@Resource
	private IOptLogService optLogServiceImpl;
	
	
	@Pointcut("@annotation(com.ali.springaop.anno.MyLog)")
	public void pointcut() {}
	
	
	/**
	 * @annotation(mylog) 表示將@MyLog 物件作為引數,傳遞到mylog形參中,使用&& @annotation(mylog)的方式可以更簡單的去獲取註解的內容
	 * @param point
	 * @param mylog
	 * @param ret
	 */
	@AfterReturning(pointcut="pointcut() && @annotation(mylog)",returning="ret")
	public void afterReturningAdvice(JoinPoint point,MyLog mylog, Object ret) {
		Object[] args = point.getArgs();//獲得目標方法的引數
		
		OptLogBean log = new OptLogBean();//例項化日誌javabean
		
		//從註解中獲取註解屬性的值
		log.setMenuName(mylog.value());
		log.setOptType(mylog.type().getValue());
		
		log.setOptTime(new Date());
		log.setUserName("");
		log.setOptData(Arrays.toString(args));
		
        //日誌資訊新增
		optLogServiceImpl.addOptLogBean(log);
		
	}
	
}

那麼在方法上就要有相應的註解:

/**
 * @Service 表示該Java類是一個需要被spring容器管理起來的業務層的元件
 * 預設情況下,spring容器掃描到該元件之後,將會將該類的類名 “首字母小寫後的字串”,作為該元件在容器中的ID
 * 當然你也可以通過@Service("sb")這種方式去改
 * @author Administrator
 *
 */
@Service
public class UserServiceImpl implements IUserService {
	
	/**
	 * @Autowired 代表自動裝配,只不過它預設採用的裝配方式是byType
	 * @Qualifier 代表精準裝配,改byType為byName
	 */
//	@Autowired
//	@Qualifier("userDaoImpl3")
	
	/**
	 * @Resource 約== @Autowired + @Qualifier
	 * 預設情況下,@Resource將先按照byName裝配方式進行精準裝配,如果裝配失敗,將回退到byType裝配方式
	 * 
	 * 如果你指定了name="userDaoImpl3" ,那麼將嚴格按照byName的裝配方式,不會回退
	 */
	@Resource
	private IUserDao userDaoImpl;
	
	@MyLog(value="使用者管理",type=LogEnum.ADD)
	@Override
	public UserBean saveUserBean(UserBean user) {
		// TODO Auto-generated method stub
		return userDaoImpl.addUserBean(user);;
	}

	@MyLog(value="使用者管理",type=LogEnum.ADD)
	@Override
	public int addBatchUserBean(List<UserBean> users) {
		// TODO Auto-generated method stub
		return userDaoImpl.addBatchUserBean(users);
	}

	@MyLog(value="使用者管理",type=LogEnum.UPDATE)
	@Override
	public int updateUserBean(UserBean user) {
		// TODO Auto-generated method stub
		return userDaoImpl.updateUserBean(user);
	}

	@MyLog(value="使用者管理",type=LogEnum.DELETE)
	@Override
	public int deleteUserBean(UserBean user) {
		// TODO Auto-generated method stub
		return userDaoImpl.deleteUserBean(user);
	}

	@MyLog(value="使用者管理",type=LogEnum.DELETE)
	@Override
	public int deleteBatchUserBean(int[] ids) {
		// TODO Auto-generated method stub
		return userDaoImpl.deleteBatchUserBean(ids);
	}

	@MyLog(value="使用者管理",type=LogEnum.DELETE)
	@Override
	public int deleteUserBean(Integer id) {
		// TODO Auto-generated method stub
		return userDaoImpl.deleteUserBean(id);
	}

	@Override
	public UserBean getUserBeanById(Integer id) {
		// TODO Auto-generated method stub
		return userDaoImpl.getUserBeanById(id);
	}

	@Override
	public Map<String, Object> queryUserBeanById(Integer id) {
		// TODO Auto-generated method stub
		return userDaoImpl.queryUserBeanById(id);
	}

	@Override
	public List<Map<String, Object>> findUserBeanMapByObject(UserBean user) {
		// TODO Auto-generated method stub
		return userDaoImpl.findUserBeanMapByObject(user);
	}

	@Override
	public UserBean findUserBeanByLoginNameAndPwd(String loginName, String pwd) {
		// TODO Auto-generated method stub
		return userDaoImpl.findUserBeanByLoginNameAndPwd(loginName, pwd);
	}

	@Override
	public List<UserBean> findUserBeanByObject(UserBean user) {
		// TODO Auto-generated method stub
		return userDaoImpl.findUserBeanByObject(user);
	}

	@Override
	public List<UserBean> findUserBeanByMap(Map map) {
		// TODO Auto-generated method stub
		return userDaoImpl.findUserBeanByMap(map);
	}

	@Override
	public PageBean findUserBeanList2PageBean(PageBean page, UserBean user) {
		// TODO Auto-generated method stub
		int totalRows = userDaoImpl.countUserBeanList2PageBean(user);
		List<?> datas = null;
		if(totalRows > 0) {
			datas = userDaoImpl.findUserBeanList(page, user);
		}
		page.setTotalRows(totalRows);	
		page.setData(datas);
		return page;
	}

	

}

這個註解是自己寫的,如下:

@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {

	/**
	 * value 代表著 具體的操作模組
	 * @return
	 */
	String value() default ""; 
	
	/**
	 * 操作型別 0- 新增,1-修改,2-刪除
	 * @return
	 */
	LogEnum type();
}

LogEnum列舉也是自己寫的,如下:

public enum LogEnum {

	ADD(0),UPDATE(1),DELETE(2);
	
	private int value;
	
	private LogEnum(int value) {
		this.value = value;
	}

	public int getValue() {
		return value;
	}
	
}