Spring之AOP(面向切面编程)
2017-11-05 17:23
816 查看
一、概念:
Aop编程:
关注点代码与业务代码分离!(jdk/cglib代理)
关注点:
重复执行的代码, 也叫关注点代码!
切面:
关注点代码形成的类,就叫做切面
SpringAop编程,也叫面向切面编程!
Aop: Aspect Object Programming 面向切面编程!
哪些是切面?:事务、权限控制、日志…
切入点表达式:
拦截指定方法,给给指定方法所在的类,生成代理对象!
Spring在初始化容器的时候,会根据切入点表达式的规则,会符合拦截规则的方法所在的类生成代理对象!
二、使用Aop注解方式开发步骤:
1. 引入Aop相关jar文件
(aspectj是在spring之前,面向切面开发的公用组件)
aopalliance.jar 【spring-framework-2.5.6\lib\aopalliance】
aspectjrt.jar 【spring-framework-2.5.6\lib\aspectj】
aspectjweaver.jar 【spring-framework-2.5.6\lib\aspectj】
spring-
4000
aop-3.2.5.RELEASE.jar 【Spring3.2源码】
2.引入Aop名称空间
3. 开启Aop注解
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
4. 使用Aop相关注解
@Aspect
指定一个类为切面类
(切面类也需要实例化)
(切面类中的方法,也叫做通知)
@Before 前置通知
【在执行目标对象方法之前执行】
@After 后置通知
【在执行目标对象方法之后执行】
@AfterReturning 返回后通知 【在执行目标对象方法结束后执行, 出现异常不执行】
@AfterThrowing 异常通知 【在执行目标对象方法出现异常时候执行】
@Around 环绕通知 【环绕目标方法执行】
@Pointcut 定义一个切入点表达式变量 (后面使用这个切入点表达式的时候,直接引用方法名即可!)
如:
Spring生成代理对象的过程?
1. 创建容器对象的时候,根据"切入点表达式"拦截的类,生成代理对象。
2. 如果目标对象有实现接口,使用jdk代理!
3. 如果目标对象没有实现接口,使用cglib代理!
4. 从容器获取代理后的对象。
5. 执行代理对象的方法,在运行时期动态植入"切面"类中的"通知"!
四、使用AopXML方式开发步骤:
1.引入Aop 相关jar文件
2. bean.xml 引入Aop名称空间
3. Aop配置
五、切入点表达式语法详解
切入点表达式:
拦截指定的类,生成代理对象!
expression="execution(* cn.itcast.g_execution.UserDao.save(..))"
execution(
modifiers-pattern? 拦截的方法的访问修饰符(? 表示0或多,可以省略!)
ret-type-pattern 方法返回类型,必须指定(*)
declaring-type-pattern? 拦截的方法所在的类
name-pattern(param-pattern) 拦截的方法(以及方法的参数列表)
throws-pattern?) 方法声明的异常
总结:拦截,一定要指定到方法!
六、Aop编程之注解方式代码案例:
testApp():
testApp2():(注意:UserDao实现IUserDao要取消,cglib代理目标类不能实现接口!)
testApp3():(切入点表达式未拦截OrderDao类,所以切面类没有起作用,仅仅输出OrderDao中的save()方法!)
七、Aop编程之xml方式代码案例:
tespApp():
testApp2():(注意:UserDao实现IUserDao要取消,cglib代理目标类不能实现接口!)
testApp3():(切入点表达式未拦截OrderDao类,所以切面类没有起作用,仅仅输出OrderDao中的save()方法!)
参考资料:传智播客袁杰老师SpringAop讲解
Aop编程:
关注点代码与业务代码分离!(jdk/cglib代理)
关注点:
重复执行的代码, 也叫关注点代码!
public void add(User user) { Session session = null; Transaction trans = null; try { session = HibernateSessionFactoryUtils.getSession(); 【关注点代码】 trans = session.beginTransaction();【关注点代码】 session.save(user); // 业务代码 trans.commit();【关注点代码】 } catch (Exception e) { e.printStackTrace(); if(trans != null){ trans.rollback(); } } finally{ HibernateSessionFactoryUtils.closeSession(session); } }
切面:
关注点代码形成的类,就叫做切面
SpringAop编程,也叫面向切面编程!
Aop: Aspect Object Programming 面向切面编程!
哪些是切面?:事务、权限控制、日志…
切入点表达式:
拦截指定方法,给给指定方法所在的类,生成代理对象!
Spring在初始化容器的时候,会根据切入点表达式的规则,会符合拦截规则的方法所在的类生成代理对象!
二、使用Aop注解方式开发步骤:
1. 引入Aop相关jar文件
(aspectj是在spring之前,面向切面开发的公用组件)
aopalliance.jar 【spring-framework-2.5.6\lib\aopalliance】
aspectjrt.jar 【spring-framework-2.5.6\lib\aspectj】
aspectjweaver.jar 【spring-framework-2.5.6\lib\aspectj】
spring-
4000
aop-3.2.5.RELEASE.jar 【Spring3.2源码】
2.引入Aop名称空间
<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" 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/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="cn.itcast.e_aop_anno"></context:component-scan> <!-- 开启aop注解 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
3. 开启Aop注解
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
4. 使用Aop相关注解
@Aspect
指定一个类为切面类
(切面类也需要实例化)
(切面类中的方法,也叫做通知)
@Before 前置通知
【在执行目标对象方法之前执行】
@After 后置通知
【在执行目标对象方法之后执行】
@AfterReturning 返回后通知 【在执行目标对象方法结束后执行, 出现异常不执行】
@AfterThrowing 异常通知 【在执行目标对象方法出现异常时候执行】
@Around 环绕通知 【环绕目标方法执行】
@Pointcut 定义一个切入点表达式变量 (后面使用这个切入点表达式的时候,直接引用方法名即可!)
如:
public class TransactionAop { // 定义一个切入点表达式变量(后面使用这个切入点表达式的时候,直接引用方法名即可) @Pointcut("execution(* spring_ioc.g_aop_anno.UserDao.*(..))") public void pointcut_(){ } //【前置通知】 // 在执行业务方法,之前执行 @Before("pointcut_()") public void beginTransaction() { System.out.println("[前置通知]开启事务.."); }
Spring生成代理对象的过程?
1. 创建容器对象的时候,根据"切入点表达式"拦截的类,生成代理对象。
2. 如果目标对象有实现接口,使用jdk代理!
3. 如果目标对象没有实现接口,使用cglib代理!
4. 从容器获取代理后的对象。
5. 执行代理对象的方法,在运行时期动态植入"切面"类中的"通知"!
四、使用AopXML方式开发步骤:
1.引入Aop 相关jar文件
2. bean.xml 引入Aop名称空间
3. Aop配置
<?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" 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/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"> <!-- UserDao实例加入Spring的IOC容器 --> <bean id = "userDao" class = "spring_ioc.h_aop_xml.UserDao"></bean> <!-- 切面类实例加入容器 --> <bean id = "transactionAop" class = "spring_ioc.h_aop_xml.TransactionAop"></bean> <!-- OrderDao实例加入容器 --> <bean id = "orderDao" class = "spring_ioc.h_aop_xml.OrderDao"></bean> <!-- AOP的相关配置 --> <aop:config> <!-- 切入点表达式的定义 --> <aop:pointcut expression="execution(* spring_ioc.h_aop_xml.UserDao.*(..))" id="pt"/> <!-- 切面配置 --> <aop:aspect ref = "transactionAop"> <!-- 环绕通知配置 --> <aop:around method="arroud" pointcut-ref = "pt"/> <!-- 前置通知的配置:在目标方法之前执行 --> <aop:before method="beginTransaction" pointcut-ref = "pt"/> <!-- 后置通知的配置:在目标方法之后执行 --> <aop:after method="commit" pointcut-ref = "pt"/> <!-- 返回后通知的配置:在执行目标方法结束后执行,出现异常不会执行 --> <aop:after-returning method="afterReturing" pointcut-ref = "pt"/> <!-- 异常通知的配置:在执行目标方法的时候出现异常执行 --> <aop:after-throwing method="afterThrowing" pointcut-ref = "pt"/> </aop:aspect> </aop:config> </beans>
五、切入点表达式语法详解
切入点表达式:
拦截指定的类,生成代理对象!
expression="execution(* cn.itcast.g_execution.UserDao.save(..))"
execution(
modifiers-pattern? 拦截的方法的访问修饰符(? 表示0或多,可以省略!)
ret-type-pattern 方法返回类型,必须指定(*)
declaring-type-pattern? 拦截的方法所在的类
name-pattern(param-pattern) 拦截的方法(以及方法的参数列表)
throws-pattern?) 方法声明的异常
总结:拦截,一定要指定到方法!
<!-- 切入点表达式定义 -->
<!-- 1. 拦截指定的方法(通用)-->
<!--<aop:pointcut expression="execution(* cn.itcast.g_execution.UserDao.save(..))" id="pt"/>-->
<!-- 2. 拦截指定的类下所有的方法 -->
<!--<aop:pointcut expression="execution(* cn.itcast.g_execution.UserDao.*(..))" id="pt"/>-->
<!-- 3. 拦截指定包下所有的类的所有方法 -->
<!--<aop:pointcut expression="execution(* cn.itcast.g_execution.*.*(..))" id="pt"/>-->
<!-- 3. 拦截指定包,以及其子包下所有类的所有方法 -->
<!--<aop:pointcut expression="execution(* cn..*.*(..))" id="pt"/>-->
<!-- 5. 拦截所有的public方法 -->
<!--<aop:pointcut expression="execution(public * *(..))" id="pt"/>-->
<!-- 6. 拦截所有的包含save方法 -->
<!--<aop:pointcut expression="execution(* *save*(..))" id="pt"/>-->
<!-- 7. 拦截UserDao.save()方法与OrderDao.save() -->
<!--<aop:pointcut expression="execution(* cn..UserDao.save(..)) || execution(* cn..OrderDao.save(..))" id="pt"/>-->
<!--<aop:pointcut expression="execution(* cn..UserDao.save(..)) or execution(* cn..OrderDao.save(..))" id="pt"/>-->
<!-- 8. 不拦截UserDao.save()方法 -->
<!--<aop:pointcut expression="!execution(* cn..UserDao.save(..))" id="pt"/>-->
<!--<aop:pointcut expression=" not execution(* cn..UserDao.save(..))" id="pt"/>-->
<!-- 9. 拦截UserDao.save()同时拦截OrderDao.save() -->
<!-- 注意: 这种很少用,一般都是或者的关系即: || 、 or-->
<!--<aop:pointcut expression="execution(* cn..UserDao.save(..)) and execution(* cn..OrderDao.save(..))" id="pt"/>-->
<aop:pointcut expression="execution(* cn..UserDao.save(..)) && execution(* cn..OrderDao.save(..))" id="pt"/>
六、Aop编程之注解方式代码案例:
package spring_ioc.g_aop_anno; public interface IUserDao { void save(); }
package spring_ioc.g_aop_anno; import org.springframework.stereotype.Repository; @Repository // 把对象加入ioc容器 public class UserDao implements IUserDao{ public void save() { System.out.println("保存..."); } }
package spring_ioc.g_aop_anno;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
// 重复代码
@Component("aop")
@Aspect// 指定一个类为切面类
public class TransactionAop { // 定义一个切入点表达式变量(后面使用这个切入点表达式的时候,直接引用方法名即可) @Pointcut("execution(* spring_ioc.g_aop_anno.UserDao.*(..))") public void pointcut_(){ } //【前置通知】 // 在执行业务方法,之前执行 @Before("pointcut_()") public void beginTransaction() { System.out.println("[前置通知]开启事务.."); }
//【后置通知】
// 在执行业务方法,之后执行
@After("pointcut_()")
public void commit() {
System.out.println("[后置通知] 提交事务..");
}
// 【返回后通知】 在执行目标方法结束后执行, 出现异常不会执行
@AfterReturning("pointcut_()")
public void afterReturing(){
System.out.println("[返回后通知]");
}
// 【异常通知】 在执行目标方法的时候出现异常执行
@AfterThrowing("pointcut_()")
public void afterThrowing(){
System.out.println("[异常通知]");
}
// 【环绕通知】 会环绕目标方法执行
@Around("pointcut_()")
public void arroud(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("[环绕前:]");
pjp.proceed(); // 执行目标方法
System.out.println("[环绕后:]");
}
}
package spring_ioc.g_aop_anno; import org.springframework.stereotype.Repository; @Repository public class OrderDao { public void save(){ System.out.println("订单已保存!"); } }
<?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" 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/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="spring_ioc.g_aop_anno"></context:component-scan> <!-- 开启aop注解 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
package spring_ioc.g_aop_anno; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class App { private ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml",getClass()); // jdk代理(注意:目标类UserDao实现IUserDao接口!) @Test public void testApp() throws Exception { //从Spring的IOC容器中获取对象,使用接口接收! IUserDao userDao = (IUserDao)ac.getBean("userDao"); System.out.println(userDao.getClass()); userDao.save(); /*//使用接口接收会报错:java.lang.ClassCastException: $P bd37 roxy13 cannot be cast to spring_ioc.g_aop_anno.UserDao UserDao userDao = (UserDao)ac.getBean("userDao"); System.out.println(userDao.getClass()); userDao.save();*/ /*总结:在Spring的AOP编程中,符合切入点表达式的目标类,如果目标类有实现接口,从容器中获取目标对象的时候一定要通过接口接收否则会报错! * */ } //cglib代理(注意:目标类UserDao未实现IUserDao接口!) @Test public void testApp2(){ UserDao userDao = (UserDao) ac.getBean("userDao"); System.out.println(userDao.getClass()); userDao.save(); } //测试:没有被切入点表达式拦截的类(OrderDao)是不会生成代理对象的! @Test public void testApp3(){ OrderDao orderDao = (OrderDao)ac.getBean("orderDao"); orderDao.save(); } }
testApp():
testApp2():(注意:UserDao实现IUserDao要取消,cglib代理目标类不能实现接口!)
testApp3():(切入点表达式未拦截OrderDao类,所以切面类没有起作用,仅仅输出OrderDao中的save()方法!)
七、Aop编程之xml方式代码案例:
package spring_ioc.h_aop_xml; public interface IUserDao { void save(); }
package spring_ioc.h_aop_xml; public class UserDao implements IUserDao{ public void save() { System.out.println("保存..."); } }
package spring_ioc.h_aop_xml;
import org.aspectj.lang.ProceedingJoinPoint;
//切面类
public class TransactionAop {
public void beginTransaction() {
System.out.println("[前置通知]开启事务..");
}
public void commit() {
System.out.println("[后置通知] 提交事务..");
}
public void afterReturing(){
System.out.println("[返回后通知]");
}
public void afterThrowing(){
System.out.println("[异常通知]");
}
public void arroud(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("[环绕前:]");
pjp.proceed(); // 执行目标方法
System.out.println("[环绕后:]");
}
}
package spring_ioc.h_aop_xml; public class OrderDao { public void save(){ System.out.println("订单已保存!"); } }
<?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"
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/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">
<!-- UserDao实例加入Spring的IOC容器 -->
<bean id = "userDao" class = "spring_ioc.h_aop_xml.UserDao"></bean>
<!-- 切面类实例加入容器 -->
<bean id = "transactionAop" class = "spring_ioc.h_aop_xml.TransactionAop"></bean>
<!-- OrderDao实例加入容器 -->
<bean id = "orderDao" class = "spring_ioc.h_aop_xml.OrderDao"></bean>
<!-- AOP的相关配置 -->
<aop:config>
<!-- 切入点表达式的定义 -->
<aop:pointcut expression="execution(* spring_ioc.h_aop_xml.UserDao.*(..))" id="pt"/>
<!-- 切面配置 -->
<aop:aspect ref = "transactionAop">
<!-- 环绕通知配置 -->
<aop:around method="arroud" pointcut-ref = "pt"/>
<!-- 前置通知的配置:在目标方法之前执行 -->
<aop:before method="beginTransaction" pointcut-ref = "pt"/>
<!-- 后置通知的配置:在目标方法之后执行 -->
<aop:after method="commit" pointcut-ref = "pt"/>
<!-- 返回后通知的配置:在执行目标方法结束后执行,出现异常不会执行 -->
<aop:after-returning method="afterReturing" pointcut-ref = "pt"/>
<!-- 异常通知的配置:在执行目标方法的时候出现异常执行 -->
<aop:after-throwing method="afterThrowing" pointcut-ref = "pt"/>
</aop:aspect>
</aop:config>
</beans>
package spring_ioc.h_aop_xml; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class App { private ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml",getClass()); // jdk代理(注意:目标类UserDao实现IUserDao接口!) @Test public void testApp() throws Exception { //从Spring的IOC容器中获取对象,使用接口接收! IUserDao userDao = (IUserDao)ac.getBean("userDao"); System.out.println(userDao.getClass()); userDao.save(); /*//使用接口接收会报错:java.lang.ClassCastException: $Proxy13 cannot be cast to spring_ioc.g_aop_anno.UserDao UserDao userDao = (UserDao)ac.getBean("userDao"); System.out.println(userDao.getClass()); userDao.save();*/ /*总结:在Spring的AOP编程中,符合切入点表达式的目标类,如果目标类有实现接口,从容器中获取目标对象的时候一定要通过接口接收否则会报错! * */ } //cglib代理(注意:目标类UserDao未实现IUserDao接口!) @Test public void testApp2(){ UserDao userDao = (UserDao) ac.getBean("userDao"); System.out.println(userDao.getClass()); userDao.save(); } //测试:没有被切入点表达式拦截的类(OrderDao)是不会生成代理对象的! @Test public void testApp3(){ OrderDao orderDao = (OrderDao)ac.getBean("orderDao"); orderDao.save(); } }
tespApp():
testApp2():(注意:UserDao实现IUserDao要取消,cglib代理目标类不能实现接口!)
testApp3():(切入点表达式未拦截OrderDao类,所以切面类没有起作用,仅仅输出OrderDao中的save()方法!)
参考资料:传智播客袁杰老师SpringAop讲解
相关文章推荐
- Spring----AOP面向切面编程例子解析(附源码)
- Spring之面向切面编程(AOP)四种通知
- Spring AOP(Spring面向切面编程)
- Spring(4)——面向切面编程(AOP模块)
- Spring-aop面向切面编程
- Spring基础、IOC(控制反转)、AOP(面向切面编程)、Log4j、注解配置
- Spring详解篇之 AOP面向切面编程
- Spring之AOP面向切面编程实现(一)
- Spring的控制反转(IoC)和面向切面编程(AOP)的概念浅析。
- Spring -- aop(面向切面编程),前置&后置&环绕&抛异常通知,引入通知,自动代理
- Spring 学习(二) spring 面向切面编程(AOP)
- Spring 的AOP(面向切面编程)
- Spring学习笔记(AOP面向切面编程)
- Spring实践:面向切面编程(AOP)
- spring的IOC(控制反转)与AOP(面向切面编程)
- SpringAOP 面向切面编程
- Spring 的面向切面编程(AOP)
- Spring面向切面编程(AOP)
- Spring之AOP(面向切面编程)
- Spring基础、IOC(控制反转)、AOP(面向切面编程)、Log4j、注解配置