首页 > WEB开发 > 后台开发 > Spring AOP
2014
10-05

Spring AOP

四、AOP高级应用

1、处理异常

项目中的异常处理确实是件头疼的事,使用Spring AOP将大大减轻异常处理工作,在遇到异常时,你只需抛出,然后集中到一处进行处理(切面)。

业务场景:开发时的三层架构中,将Dao层的异常抛给Service层,在Service层继续抛出,最后在一个异常处理的切面中进行处理。

10. Spring AOP10131

1)PersonDaoImpl、PersonServiceImpl

public class PersonDaoImpl implements PersonDao {
	public void save() throws Exception {
		throw new RuntimeException("在调用personDao的savePerson方法时出错");
	}
}

public class PersonServiceImpl implements PersonService{
	private PersonDao personDao;

	public void setPersonDao(PersonDao personDao){
		this.personDao = personDao;
	}

	public void savePerson() throws Exception{
		personDao.save();
	}
}

2)切面:GlobalExceptionProcess

public class GlobalExceptionProcess {
	public void exceptionAdvice(Exception ex){
		System.out.println(ex.getMessage());
	}
}

3)applicationContext.xml中IoC容器配置、声明切面

<bean id="personDao" class="org.flyne.dao.impl.PersonDaoImpl"></bean>
<bean id="personService" class="org.flyne.service.impl.PersonServiceImpl">
	<property name="personDao" ref="personDao"/>
</bean>

<bean id="exceptionProcess" class="org.flyne.aspect.GlobalExceptionProcess"/>

<aop:config>
	<aop:pointcut
		expression="execution(* org.flyne.service.impl.*.*(..))"
		id="serviceImpl"/>

	<aop:aspect ref="exceptionProcess">
		<aop:after-throwing method="exceptionAdvice" pointcut-ref="serviceImpl" throwing="ex"/>
	</aop:aspect>
</aop:config>

4)测试

@Test(expected = Exception.class)
public void test() throws Exception{
	ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
	PersonService personService = (PersonService)ctx.getBean("personService");
	personService.savePerson();
}

5)结论:通过控制台的输出结果可知,在切面的异常通知中成功捕获了Service层抛出的异常。

2、权限控制

权限控制也是项目中需要考虑的问题之一。有些操作(方法)只有具有某一权限才能执行。

业务场景:在执行Action中的方法时,会检查用户是否有权限执行该操作。此时可以定义一个权限检查的切面(PrivilegeCheckAspect),在调用Action中的方法时,进入切面的环绕通知方法,检查用户是否有权限执行,有则放行,没有不执行并在控制台打印”没有权限”(仅仅是模拟)。

本文仅提供一个解决思路,还可以进行扩展。下面是代码中一些类的说明:

① @PrivilegeInfo注解:该注解可以加在Action中需要进行权限判断的方法上。PrivilegeInfoParse中提供了对该注解进行解析的方法(反射注解)。

② Privilege类:权限类,该类仅封装了权限名。

③ PrivilegeCheckAspect类:AOP中的切面,提供环绕通知方法。将用户的权限保存在一个List<Privilege>中(实际开发时权限会存放在数据库中),当目标方法被拦截时,会对比目标方法所需权限和用户权限决定是否放行。

1)权限相关类:Privilege类、@PrivilegeInfo注解、PrivilegeInfoParse类

public class Privilege {
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}
-------------------------------------
//加在方法上,表示执行该方法需要拥有的权限
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PrivilegeInfo {
	String name() default "";
}
-------------------------------------
public class PrivilegeInfoParse {
	//如果方法上存在@PrivilegeInfo注解,返回name属性值,不存在返回null
	public static String parse(Class targetClass , String methodName) throws Exception{
		Method targetMethod = targetClass.getMethod(methodName);
		if(targetMethod.isAnnotationPresent(PrivilegeInfo.class)){
			PrivilegeInfo privilege = targetMethod.getAnnotation(PrivilegeInfo.class);
			String privilegeName = privilege.name();
			return privilegeName;
		}
		return null;
	}
}

2)PrivilegeCheckAspect类

public class PrivilegeCheckAspect {
	//保存用户的权限,调用Action中的方法时传入
	private static List<Privilege> privileges = new ArrayList<Privilege>();
	public static List<Privilege> getPrivileges() {
		return privileges;
	}

	public Object privilegeCheck(ProceedingJoinPoint joinPoint) throws Throwable{
		Class targetClass = joinPoint.getTarget().getClass();
		String methodName = joinPoint.getSignature().getName();

		String privilegeName = PrivilegeInfoParse.parse(targetClass, methodName);

		boolean flag = false;//是否有权限执行Action中方法的标志
		if(privilegeName == null){//如果方法上没加注解就直接放行
			flag = true;
		}else{//方法上加了注解表明该方法需要有相应的权限才能执行
			for(Privilege privilege : privileges){
				if(privilegeName.equals(privilege.getName())){
					flag = true;
					break;
				}
			}
		}
		if(flag){
			return joinPoint.proceed();
		}
		System.out.println("没有权限");
		return "error";
	}
}

3)PersonAction类

public class PersonAction {
	private PersonService personService;
	public void setPersonService(PersonService personService){
		this.personService = personService;
	}

	//需要有update权限才能执行
	@PrivilegeInfo(name="update")
	public void update(){
		personService.updatePerson();
	}

	//需要有find权限才能执行
	@PrivilegeInfo(name="find")
	public void findPerson(){
		personService.findPerson();
	}
}

4)applicationContext.xml中的相关配置:

<bean id="personAction" class="org.flyne.action.PersonAction">
	<property name="personService" ref="personService"></property>
</bean>

<bean id="privilegeCheck" class="org.flyne.aspect.PrivilegeCheckAspect"/>
<aop:config>
	<aop:pointcut
		 expression="execution(* org.flyne.action.*.*(..))"
		 id="actionPointCut"/>
	<aop:aspect ref="privilegeCheck">
		<aop:around method="privilegeCheck" pointcut-ref="actionPointCut" />
	</aop:aspect>
</aop:config>

5)测试类:PrivilegeCheckTest.java

public class PrivilegeCheckTest {
	//privileges存放用户拥有的权限
	private static List<Privilege> privileges;
	private static ApplicationContext ctx;

	@BeforeClass
	public static void init(){
		privileges = PrivilegeCheckAspect.getPrivileges();
		Privilege privilege = new Privilege();
		//假设用户只有查看的权限
		privilege.setName("find");
		//假设用户只有查看的权限
		privileges.add(privilege);

		ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
	}

	//测试更新操作(会在控制台打印"没有权限")
	@Test
	public void testUpdate(){
		PersonAction action = (PersonAction)ctx.getBean("personAction");
		action.update();
	}

	//测试查找操作
	@Test
	public void testFind(){
		PersonAction action = (PersonAction)ctx.getBean("personAction");
		action.findPerson();
	}
}

说明:因Service、Dao层编码写法比较固定,相关代码没有列出。


留下一个回复

你的email不会被公开。