1)AOP(Aspect Oriented Programming):面向切面编程,是一种可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的技术.
2)关注点代码与核心业务代码,看下面代码一看就知道区别了...
// 保存一个用户 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); ..【关注点代码】 } } 关注点:指重复执行的代码.关注点代码与业务代码分离的好处?
关注点代码写一次即可:开发者只需要关注业务代码即可;运行时期,执行核心业务代码的时候动态植入关注点代码.
如何分离?
过程式/对象式/代理模式分离.
3)AOP功能:让关注点代码与核心业务代码分离.
4)切面:关注点形成的类,就叫切面(类).
面向切面编程:就是指对很多功能都有重复的代码进行抽取,再在运行的时候往业务方法上动态的植入"切面类代码".
5)切入点:执行目标对象方法,动态植入切面代码.
可以通过切入点表达式,指定拦截哪些类的哪些方法,给指定的类在运行的时候动态植入切面类代码.
1)先引入aop相关jar文件(aspectj aop优秀组件). spring-aop-3.2.5.RELEASE.jar 【spring3.2源码】 aopalliance.jar 【spring2.5源码/lib/aopalliance】 aspectjweaver.jar 【spring2.5源码/lib/aspectj】或【aspectj-1.8.2\lib】 aspectjrt.jar 【spring2.5源码/lib/aspectj】或【aspectj-1.8.2\lib】 注意:用到spring2.5版本的jar文件,如果用jdk1.7可能会有问题.需要升级aspectj组件,即使用aspectj-1.8.2版本(或其他版本)中提供的jar文件. 2) bean.xml中引入aop名称空间.
3)开启aop注解. 4) 使用注解.
@Aspect 指定一个类为切面类 @Pointcut("execution(* com.bighuan.e_aop_anno.*.*(..))") 指定切入点表达式 @Before("pointCut_()") 前置通知: 目标方法之前执行 @After("pointCut_()") 后置通知:目标方法之后执行(始终执行) @AfterReturning("pointCut_()") 返回后通知: 执行方法结束前执行(异常不执行) @AfterThrowing("pointCut_()") 异常通知: 出现异常时候执行 @Around("pointCut_()") 环绕通知: 环绕目标方法执行
1)最最重要的事:在bean.xml中开启注解扫描及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: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.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.bighuan.e_aop_anno"></context:component-scan> <!-- 开启aop注解方式 :如果目标对象有实现接口就用JDK代理;否则就用cglib代理--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>2)接口
public interface IUserDao { void save(); }3)目标对象,并将目标对象加入IOC容器. @Component// 加入IOC容器 public class UserDao implements IUserDao { @Override public void save() { System.out.println("***保存用户数据到数据库"); } }4)切面类:用Aspect将Aop类声明为切面类,同时将该类加入IOC容器 @Component @Aspect// 指定当前类为切面类 public class Aop { // @Pointcut("execution(* com.bighuan.e_aop_anno.UserDao.save(..))") @Pointcut("execution(* com.bighuan.e_aop_anno.*.*(..))") public void pointCut() { // 方法名随便 } // 指定切入点表达式:拦截那些方法,即为那些类生成代理对象 // @Before("execution(* com.bighuan.e_aop_anno.UserDao.save(..))") // @Before("execution(* com.bighuan.e_aop_anno.UserDao.*(..))") // public void begin(){ // System.out.println("*****开启事务*****"); // } // @After("execution(* com.bighuan.e_aop_anno.UserDao.*(..))") // public void commit(){ // System.out.println("********提交事务******"); // } // 前置通知:在执行目标方法之前执行 @Before("pointCut()") public void begin() { System.out.println("*****开启事务*****"); } // 后置/最终通知:在执行目标方法之后执行,无论是否出现异常最终都会执行 @After("pointCut()") public void after() { System.out.println("********提交事务******"); } // 执行方法结束前执行 @AfterReturning("pointCut()") // 出现异常不执行 public void afterReturning() { System.out.println("Aop.afterReturning()"); } // 在目标方法执行异常时执行 @AfterThrowing("pointCut()") public void afterThrowing() { System.out.println("Aop.afterThrowing()"); } // 环绕通知 @Around("pointCut()") public void around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("环绕前>>>>>>>"); Object returnValue = pjp.proceed();// 执行目标方法 System.out.println("环绕后<<<<<<<<<<"); } } 5)测试: private ApplicationContext ac = new ClassPathXmlApplicationContext( "com/bighuan/e_aop_anno/bean.xml"); //目标对象有实现接口,Spring会自动选择"JDK"代理,即动态代理 @Test public void testAop() throws Exception { IUserDao userDao = (IUserDao) ac.getBean("userDao"); System.out.println(userDao.getClass()); userDao.save(); } 结果: class $Proxy12 环绕前>>>>>>> *****开启事务***** ***保存用户数据到数据库 环绕后<<<<<<<<<< ********提交事务****** Aop.afterReturning()从结果可以看出,这是JDK代理即动态代理.6)OrderDao不实现接口,同样把他加入IOC容器
@Component //加入IOC容器 public class OrderDao { public void save() { System.out.println("***保存用户数据到数据库111111111111"); //int i=1/0; } }7)测试2: //目标对象没有实现接口,Spring会自动选择"Cglib"代理,即子类代理 @Test public void testCglib() throws Exception { OrderDao orderDao = (OrderDao) ac.getBean("orderDao"); System.out.println(orderDao.getClass()); orderDao.save(); }结果:
class com.bighuan.e_aop_anno.OrderDao$$EnhancerByCGLIB$$c7d2931b 环绕前>>>>>>> *****开启事务***** ***保存用户数据到数据库111111111111 环绕后<<<<<<<<<< ********提交事务****** Aop.afterReturning()不实现接口的话,就是Cglib代理了.1)依然使用上面的IUserDao,UserDao,OrderDao,将注解全部去掉.环境也是同一个(即引入同样的jar包)
2)切面类:Aop.java
//切面类 public class Aop { public void pointCut(){ //方法名随便 } //指定切入点表达式:拦截那些方法,即为那些类生成代理对象 // @Before("execution(* com.bighuan.e_aop_anno.UserDao.save(..))") // @Before("execution(* com.bighuan.e_aop_anno.UserDao.*(..))") // public void begin(){ // System.out.println("*****开启事务*****"); // } // @After("execution(* com.bighuan.e_aop_anno.UserDao.*(..))") // public void commit(){ // System.out.println("********提交事务******"); // } //前置通知:在执行目标方法之前执行 public void begin(){ System.out.println("*****开启事务*****"); } public void after(){ System.out.println("********提交事务******"); } public void afterReturning(){ System.out.println("Aop.afterReturning()"); } public void afterThrowing(){ System.out.println("Aop.afterThrowing()"); } public void around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("环绕前>>>>>>>"); Object returnValue = pjp.proceed();//执行目标方法 System.out.println("环绕后<<<<<<<"); } } 3)bean.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: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.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"> <!-- dao实例 --> <bean id="userDao" class="com.bighuan.f_aop_xml.UserDao"></bean> <bean id="orderDao" class="com.bighuan.f_aop_xml.OrderDao"></bean> <!-- 切面类 --> <bean id="aop" class="com.bighuan.f_aop_xml.Aop"></bean> <!-- Aop配置 --> <aop:config> <!-- 定义一个切入点表达式,拦截哪些方法 --> <aop:pointcut expression="execution(* com.bighuan.f_aop_xml.*.*(..))" id="pt" /> <!-- 切面类 --> <aop:aspect ref="aop"> <!-- 前置通知:在目标方法调用前执行 --> <aop:before method="begin" pointcut-ref="pt" /> <!-- 后置通知:在目标方法调用后执行 --> <aop:after method="after" pointcut="execution(* com.bighuan.f_aop_xml.*.*(..))" /> <!-- 返回后通知 --> <aop:after-returning method="afterReturning" pointcut-ref="pt" /> <!--异常通知 --> <aop:after-throwing method="afterThrowing" pointcut-ref="pt" /> <!-- 环绕通知 --> <aop:around method="around" pointcut-ref="pt" /> </aop:aspect> </aop:config> </beans>4)测试代码与上面的一样.XML方式更容易维护,我倾向于这种方式...