三、AOP编程
关注点代码:除了业务代码以外的代码.比如开启事务,关闭事务,异常处理
核心业务代码:保存用户这一句才是重点.
例子如下:
// 保存一个用户
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); ////..【关注点代码】
}
}
为了提高代码效率,会让核心代码和业务代码分离.实现的方法就是代理模式.
名词解释:
AOP面向切面编程:
让关注点代码与业务代码分离(面向切面编程就是面向重复代码编程)
关注点:
很多重复代码形成的类和方法
切面:
关注点形成的类。(公共的类)
面向切面编程与面向接口编程:
面向切面编程比面向接口编程更加抽象,面向切面编程公有属性和行为,抽象为面向切面。
面向接口编程,各个对象的公共方法抽取出来,放到父类和接口当中,接口当中定义类的公用行为。
面向切面编程很多重复的功能,抽象出来,比如工厂类
应用:
面向切面编程用在后期优化的时候使用。
切入点(指定拦截的是哪个点,拦截哪些方法):
执行目标对象方法,动态植入切面代码。
可以通过切入点表达式,指定拦截哪些类的哪些方法。给指定的类在运行的时候植入切面类代码
Aop代理实现了两种,一种是jdk代理,一种是cglib代理.
Aop实现的其实是通过,切面类的拦截,从而进行动态代理,使得多个重复的代码和方法,抽象出来,增强代码的强壮性和代码的优化性能.
1、注解方式实现AOP编程
步骤:
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.2lib】
aspectjrt.jar 【spring2.5源码/lib/aspectj】或【aspectj-1.8.2lib】
注意: 用到spring2.5版本的jar文件,如果用jdk1.7可能会有问题。
需要升级aspectj组件,即使用aspectj-1.8.2版本中提供jar文件提供。
2) bean.xml中引入aop名称空间
1 <context:component-scan base-package="com.liuyang.annotation.proxy"></context:component-scan>
3) 开启aop注解
1 <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
4) 使用注解
@Aspect 指定一个类为切面类
@Pointcut("execution(* cn.itcast.e_aop_anno.*.*(..))") 指定切入点表达式
@Before("pointCut_()") 前置通知: 目标方法之前执行
@After("pointCut_()") 后置通知:目标方法之后执行(始终执行)
@AfterReturning("pointCut_()") 返回后通知: 执行方法结束前执行(异常不执行)
@AfterThrowing("pointCut_()") 异常通知: 出现异常时候执行
@Around("pointCut_()") 环绕通知: 环绕目标方法执行
事例:
(1)建立UserDao
1 public interface UserDao { 2 3 public void save(); 4 5 }
(2)建立MyUserDaoAop
1 @Component 2 public class MyUserDaoAop implements UserDao { 3 4 @Override 5 public void save() { 6 System.out.println("******核心业务的实现*****"); 7 } 8 9 }
(3)建立AOP
1 @Component 2 // 指定当前类为切面类 3 @Aspect 4 public class Aop { 5 6 // 指定切入点表单式: 拦截哪些方法; 即为哪些类生成代理对象 7 @Pointcut("execution(* com.liuyang.annotation.proxy.*.*(..))") 8 public void pointCut_() { 9 } 10 11 // 前置通知 : 在执行目标方法之前执行 12 @Before("pointCut_()") 13 public void begin() { 14 System.out.println("开始事务/异常"); 15 } 16 17 // 后置/最终通知:在执行目标方法之后执行 【无论是否出现异常最终都会执行】 18 @After("pointCut_()") 19 public void after() { 20 System.out.println("提交事务/关闭"); 21 } 22 23 // 返回后通知: 在调用目标方法结束后执行 【出现异常不执行】 24 @AfterReturning("pointCut_()") 25 public void afterReturning() { 26 System.out.println("afterReturning()"); 27 } 28 29 // 异常通知: 当目标方法执行异常时候执行此关注点代码 30 @AfterThrowing("pointCut_()") 31 public void afterThrowing() { 32 System.out.println("afterThrowing()"); 33 } 34 35 // 环绕通知:环绕目标方式执行 36 @Around("pointCut_()") 37 public void around(ProceedingJoinPoint pjp) throws Throwable { 38 System.out.println("环绕前...."); 39 pjp.proceed(); // 执行目标方法 40 System.out.println("环绕后...."); 41 } 42 43 }
(4)建立bean.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:aop="http://www.springframework.org/schema/aop" 6 xsi:schemaLocation=" 7 http://www.springframework.org/schema/beans 8 http://www.springframework.org/schema/beans/spring-beans.xsd 9 http://www.springframework.org/schema/context 10 http://www.springframework.org/schema/context/spring-context.xsd 11 http://www.springframework.org/schema/aop 12 http://www.springframework.org/schema/aop/spring-aop.xsd"> 13 14 <!-- 开启注解扫描 --> 15 <context:component-scan base-package="com.liuyang.annotation.proxy"></context:component-scan> 16 17 <!-- 开启AOP注解模式 ,默认false采用jdk代理 --> 18 <aop:aspectj-autoproxy></aop:aspectj-autoproxy> 19 </beans>
(5)建立非接口模式的代理
1 @Component 2 // 加入容器 3 @Scope("prototype") 4 public class OrderDao { 5 6 public void save() { 7 System.out.println("-----核心业务:保存2222!!!------"); 8 } 9 }
(6)建立测试
1 public class TestAop { 2 3 // 目标对象有实现接口,spring会自动选择“JDK代理” 4 @Test 5 public void testApp() { 6 ApplicationContext aContext = new ClassPathXmlApplicationContext( 7 "com/liuyang/annotation/proxy/bean.xml"); 8 UserDao userDao = (UserDao) aContext.getBean("myUserDaoAop"); 9 System.out.println(userDao.getClass());// $Proxy001 10 userDao.save(); 11 } 12 13 // 目标对象没有实现接口, spring会用“cglib代理” 14 @Test 15 public void testCglib() { 16 ApplicationContext aContext = new ClassPathXmlApplicationContext( 17 "com/liuyang/annotation/proxy/bean.xml"); 18 OrderDao orderDao = (OrderDao) aContext.getBean("orderDao"); 19 System.out.println(orderDao.getClass()); 20 orderDao.save(); 21 } 22 23 @Deprecated 24 // 共性问题:如果目标对象有实现接口,在从容器中获取目标对象的时候,只能通过接口接收对象。 25 public void testApp2() { 26 ApplicationContext aContext = new ClassPathXmlApplicationContext( 27 "com/liuyang/annotation/proxy/bean.xml"); 28 // 错误代码: 只能用接口接收 29 UserDao userDao = (UserDao) aContext.getBean("myUserDaoAop"); 30 System.out.println(userDao.getClass());// $Proxy001 31 userDao.save(); 32 } 33 34 @Test 35 public void testGetObj() throws Exception { 36 ApplicationContext aContext = new ClassPathXmlApplicationContext( 37 "com/liuyang/annotation/proxy/bean.xml"); 38 OrderDao orderDao1 = (OrderDao) aContext.getBean("orderDao"); 39 OrderDao orderDao2 = (OrderDao) aContext.getBean("orderDao"); 40 41 System.out.println(orderDao1); 42 System.out.println(orderDao2); 43 44 } 45 46 }