AOP的相关概念
这里面的名词解释,可以结合上一章的内容(模拟Hibernate的事务处理和查看工资)来进行理解。
1. 切面
日志、安全性、权限类以及事务类,总之和业务逻辑没有关系的都可以做切面
2. 通知
切面中的方法。例如:事务中的beginTransaction和commit方法
3. 切入点
只有符合切入点,才能把通知和目标方法结合起来。例如:代理的判断语句
if("admin".equals(this.privilege.getAccess())){
//调用目标类的目标方法
method.invoke(this.iSalaryManage, args);
}else{
System.out.println("您没有权限");
}
4. 连接点
客户端调用的方法
proxy.showSalary();//代理对象的代理方法
AOP做到了代码的重用
springAOp切面编程的例子
结合上一节的“模拟Hibernate的事务处理”来进行拓展。因此,这里只粘贴部分代码。
Xml文件
使用AOP切面编程
1. 导入命名空间
2. 引入目标类
3. 定义切面类,过滤切入点。代理类的生成交给spring来完成
4. 设置通知方法
<?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-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<!--
使用AOP切面编程
1. 导入命名空间
2. 引入目标类
3. 定义切面类,过滤切入点
4. 设置通知方法
-->
<bean id="personDao" class="com.lzl.test.aop_proxy.PersonDaoImpl"></bean>
<bean id="transaction" class="com.lzl.test.aop_proxy.Transaction"></bean>
<aop:config>
<!-- 切面类
expression(切面表达式)
com.lzl.test.aop_proxy.personDaoImpl下面的所有方法
-->
<aop:pointcut expression="execution(* com.lzl.test.aop_proxy.PersonDaoImpl.*(..))" id="perform"/>
<!--
切面
-->
<aop:aspect ref="transaction">
<!-- 前置通知
* 在目标方法执行之前
-->
<aop:before method="beginTransaction" pointcut-ref="perform"/>
<!-- 后置通知
* 在目标方法执行之后执行
* 通过returning来获取返回的结果
* 代理方法发生异常时,不执行该方法
-->
<aop:after-returning method="commit" pointcut-ref="perform" returning="val"></aop:after-returning>
<!--
最终通知
* 目标方法执行之后
* 无论是否发生异常,都会执行。类似于finally
* 常用于资源的关闭等操作
-->
<aop:after method="finallyMethod" pointcut-ref="perform"/>
<!--
异常通知
* 目标方法发生异常时,触发该方法。
* throwing抛异常的参数
-->
<aop:after-throwing method="throwEx" throwing="ex"
pointcut-ref="perform"></aop:after-throwing>
<!-- 环绕通知
* 在目标方法执行之前执行
* 可以控制那些对象执行目标方法
-->
<aop:around pointcut-ref="perform" method="arroundAdvice"></aop:around>
</aop:aspect>
</aop:config>
</beans>
切入点类
public class Transaction {
/**
* 前置通知
* @param joinPoint
*/
public void beginTransaction(JoinPoint joinPoint){
joinPoint.getArgs(); //获取方法参数
String methodName = joinPoint.getSignature().getName();
System.out.println("=begin transaction...="+methodName);
}
/**
* 后置通知
* @param val
*/
public void commit(Object val){
//List<Person> list = (List<Person>) val;
//System.out.println("=commit...="+list.size());
System.out.println("=commit...=");
}
/**
* 最终通知
*/
public void finallyMethod(){
System.out.println("finally Method...");
}
/**
* 异常通知
* @param ex
*/
public void throwEx(Throwable ex){
System.out.println(ex.getMessage());
}
public void arroundAdvice(ProceedingJoinPoint pjp) throws Throwable{
String methodName = pjp.getSignature().getName();
if("savePerson".equals(methodName)){
pjp.proceed();
}
}
}
测试类
public class PersonTest extends SpringHelper{
static{
path="com/lzl/test/aop_proxy/applicationContext.xml";
}
@Test
public void test(){
PersonDao personDao = (PersonDao) fileResource.getBean("personDao");
personDao.selectPerson();
}
}
说明
如果目标类实现了接口,spring就用JDKproxy代理,如果没有实现接口,spring就使用cglib代理。
用SpringAOP重构查看工资的代码
代码的实现参照上一节。这里只粘贴修改的代码
<?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-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<bean id="salaryManage" class="com.lzl.test.spring.salary.SalaryManageImpl"></bean>
<bean id="logger" class="com.lzl.test.spring.salary.Logger"></bean>
<bean id="privilege" class="com.lzl.test.spring.salary.Privilege"></bean>
<bean id="security" class="com.lzl.test.spring.salary.Security"></bean>
<!--
一个切入点有多个切面
通知方法执行的顺序,按照其配置的先后顺序来执行的。
-->
<aop:config>
<!-- 定义的目标类及方法 -->
<aop:pointcut expression="execution(* com.lzl.test.spring.salary.SalaryManageImpl.*(..))" id="perform"/>
<!-- 切面 -->
<aop:aspect ref="logger">
<!-- 前置通知
日志系统
-->
<aop:before method="startLogger" pointcut-ref="perform"/>
</aop:aspect>
<aop:aspect ref="security">
<!-- 前置通知
安全系统
-->
<aop:before method="startSecurity" pointcut-ref="perform"/>
</aop:aspect>
<aop:aspect ref="privilege">
<!-- 前置通知
权限验证
-->
<aop:before method="openPrivilege" pointcut-ref="perform"/>
</aop:aspect>
</aop:config>
</beans>
测试类
public class SalaryTest extends SpringHelper{
static{
path="com/lzl/test/spring/salary/applicationContext.xml";
}
@Test
public void test(){
ISalaryManage sm = (ISalaryManage) fileResource.getBean("salaryManage");
sm.showSalary();
}
}
AOP注解
使用注解的方式实现AOP切面编程。(仅供了解)
这个例子还是MVC的那个例子。在PersonDaoImpl中添加了事务的处理。这里只粘贴部分代码。
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-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<!--
开启注解解析器
-->
<context:component-scan base-package="com.lzl.test.annotation.mvc.aop"></context:component-scan>
<!-- 开启AOP的注解 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
事务类(切面类)
@Component("transaction")
@Aspect //定义为切面类
public class Transaction {
//切入点
@Pointcut("execution(* com.lzl.test.annotation.mvc.aop.PersonDaoImpl.*(..))")
private void aa(){} //方法签名 返回值必须是void 方法的修饰符最好是private
@Before("aa()")
public void beginTransaction(){
System.out.println("=开启事务...=");
}
@AfterReturning("aa()")
public void commit(){
System.out.println("=关闭事务..=");
}
}