AOP:Aspect Oriented Programming 面向切面编程
面向切面编程的核心是动态代理设计模式。请先參见动态代理设计模式笔记。
以Hibernate保存一个对象到数据库为例,因为保存数据时须要开启事务,利用面向切面编程思想,将事务的处理分离出来。当作一个切面来处理。
jdk的动态代理的缺点: 1、在拦截器中,切入点的推断是很复杂的 2、尽管实现了切面与目标类的松耦合,可是在拦截器中还得实现结合过程
一.springAOP的原理:
目标类:在目标类的方法调用的前后,我们须要增加自己的逻辑。
切面:包括了全部的封装了自己的逻辑方法的类
切入点:目标类里的须要增加额外逻辑的方法。
通知:切面里的自己的封装自己的逻辑的方法;
比方Hibernate中,目标类是XDaoImpl,切入点是XDao.save(xx)方法,通知是开启事务,以及commit,切面就是封装了开启事务和commit的类;即在save方法运行前。须要開始事务,运行后,须要提交事务!
1、当启动spring容器的时候,
把这两个bean创建对象了 2、解析<aop:config>便签 (1)、解析切入点表达式<aop:pointcut>。切入点针对的是函数,从函数进行切入,把表达式解除出来以后和 spring中的bean进行匹配 (2)、假设匹配成功。则为该bean创建代理对象,在创建代理对象的过程中,把目标方法和通知结合在一起了 假设匹配不成功。则直接报错 (3)、当client调用context.getBean时,获取到的 (1)、假设该对象有代理对象,则返回代理对象 (2)、假设该对象没有代理对象。则返回对象本身 3、<aop:aspect>切面标签:
<bean id="classDao" class="cn.itheima03.spring.aop.xml.ClassesDaoImpl"></bean> <bean id="myTransaction" class="cn.itheima03.spring.aop.xml.MyTransaction"></bean> |
在切面中配置各种通知,这些通知就我们自己须要额外运行的逻辑。有的逻辑在切入点函数运行前运行,用<aop:before>配置。有的须要在切入点方法运行之后运行,使用<aop:after>配置。还有的是运行切入点函数出现异常后运行,等的。。 说明: spring容器内部会自己主动推断: 假设目标类实现了接口。则採用jdkproxy 假设目标类没有实现接口,则採用cglibproxy
二.关于通知(通知就是切面里的方法,又称Advive,是在方法运行前和后须要运行的自己的代码)
前置通知: 1、在目标方法之前运行 2、不管目标方法遇到异常都运行 后置通知: 1、在目标方法之后运行 2、假设目标方法遇到异常,则不运行 3、能够获取连接点的一些信息 终于通知: 1、相当于finally 2、不管目标方法是否遇到异常,都运行 异常通知 1、获取目标方法抛出的异常信息 2、throwing參数的配置才干获取信息 围绕通知 相当于jdkproxy的invoke方法
三.以下使用Spring的AOP来处理Hibernate保存对象。
1.配置文件:applicationContext.xml
< 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.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd" > <!-- 1、目标类 2、切面 3、进行AOP的配置 --> < bean id="classDao" class="cn.itheima03.spring.aop.xml.ClassesDaoImpl" ></bean> < bean id="myTransaction" class="cn.itheima03.spring.aop.xml.MyTransaction" ></bean> < aop:config > <!-- 切入点表达式 expression切入点表达式 id 唯一标示 --> <aop:pointcut expression="execution(* cn.itheima03.spring.aop.xml.ClassesDaoImpl.*(..))" id= "perform" /> <!-- ref 引向切面 切面里包括各种各样的通知,这些通知都是我们自己想要额外实现的东西,比方开启事务等。。--> <aop:aspect ref= "myTransaction"> <!-- 方法运行之前运行 --> <aop:before method= "beginTransaction" pointcut-ref="perform" /> <!-- 后置通知 returning 返回值 要与方法中的參数的名字相相应 --> <aop:after-returning method= "commit" pointcut-ref="perform" returning="val" /> <!-- 终于通知 不管目标方法是否有异常。都运行 --> <aop:after method= "finnalyMethod" pointcut-ref="perform" /> <!-- 异常通知 throwing 获取目标方法抛出的异常信息 --> <aop:after-throwing method= "throwingMethod" pointcut-ref="perform" throwing="ex" /> <!-- 相当于 代理中invoke 方法,能够控制切入点的运行 --> <aop:around method= "aroundMethod" pointcut-ref="perform" /> </aop:aspect> </ aop:config > </ beans > |
关于切入点表达式:切入点表达式匹配的是方法。一个方法的完整声明为:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?) |
问号代表可有可无,没有问号代表一定要有!
以下是一个方法完整声明的演示样例:
切入点表达式演示样例:
- execution(public * *(..)) 全部的公共方法
- execution(* set*(..)) 以set开头的随意方法
- execution(* com.xyz.service.AccountService.*(..))com.xyz.service.AccountService类里的全部的方法
- execution(* com.xyz.service.*.*(..)) com.xyz.service包下的全部类的全部的方法
- execution(* com.xyz.service..*.*(..)) com.xyz.service包及子包中全部的类的全部的方法
- execution(* cn.itheima03.spring..*.*(String,*,Integer))
- execution(* cn.itheima03.*.*.spring.*..*.*(..)) 參数..代表随意类型的随意參数,參数能够是0个
2.java代码:
public interface ClassesDao { public void saveClasses(Classes classes); public void updateClasses(Classes classes); public List<Classes> getClasses(); } =========================================== public class ClassesDaoImpl extends HibernateUtils{ public String saveClasses(Classes classes) { int a = 1/0; sessionFactory.getCurrentSession().save(classes); return "aaaa" ; } public List<Classes> getClasses() { return sessionFactory .getCurrentSession().createQuery("from Classes").list(); } public void updateClasses(Classes classes) { sessionFactory.getCurrentSession().update(classes); } } =========================================== public class HibernateUtils { public static SessionFactory sessionFactory; static { Configuration configuration = new Configuration(); configuration.configure(); sessionFactory = configuration.buildSessionFactory(); } } =========================================== public class MyTransaction extends HibernateUtils{ private Transaction transaction; /** * 前置通知 * JoinPoint 可以调用该API得到连接点的一些信息 */ public void beginTransaction(JoinPoint joinPoint){ System. out.println(joinPoint.getSignature().getName()); this.transaction = sessionFactory.getCurrentSession().beginTransaction(); } /** * 后置通知 * 1、获取目标方法的返回值 */ public void commit(Object val){ System. out.println(val); this.transaction .commit(); } /** * 终于通知 */ public void finnalyMethod(){ System. out.println("finally method" ); } /** * 异常通知 */ public void throwingMethod(Throwable ex){ /** * 输出目标方法的异常信息 */ System. out.println(ex.getMessage()); } /** * 围绕通知 * 1、假设不运行joinPoint.proceed();。目标方法是不运行的 * 2、在目标方法运行的上下文加入内容 */ public void aroundMethod(ProceedingJoinPoint joinPoint){ try { System. out.println("aaaa" ); joinPoint. proceed(); //运行目标方法 System. out.println("bbbb" ); } catch (Throwable e) { e.printStackTrace(); } } } =========================================== /** * 注意的点 * 1、代理对象的方法体的内容就是拦截器 中invoke方法体的内容 * 2、在client,用代理对象调用方法的时候进去了invoke方法 */ public class ClassesDaoTest { @Test public void testSaveClasses(){ ApplicationContext context = new ClassPathXmlApplicationContext("cn/itheima03/spring/aop/xml/applicationContext.xml" ); ClassesDaoImpl classesDao = (ClassesDaoImpl)context.getBean( "classDao" ); Classes classes = new Classes(); classes.setCname( "afds"); classesDao.saveClasses(classes); } } |
四.多切面的样例
假如查看工资须要经过日志管理,安全管理,权限管理后才干查看工资。
目标类:查看工资的类
切面:日志管理,安全管理。权限管理
切入点:查看工资的方法
1.java代码:
/** * 切面 * 日志管理 */ public class Logger { public void interceptor() { System. out.println("logging" ); } } =========================================== /** * 安全管理 */ public class Security { public void interceptor() { System. out.println("security" ); } } =========================================== /** * 权限管理 */ public class Privilege{ private String access; public String getAccess() { return access ; } public void setAccess(String access) { this.access = access; } public void interceptor(ProceedingJoinPoint joinPoint) { if("admin" .equals(this.access)){ try { joinPoint.proceed(); } catch (Throwable e) { e.printStackTrace(); } } else{ System. out.println("对不起,没有权限查看...." ); } } } =========================================== /** * 目标类 */ public class SalaryManagerImpl implements SalaryManager{ @Override public void showSalary() { System. out.println("正在查看工资" ); } } =========================================== public class SalaryTest { @Test public void test(){ ApplicationContext context= new ClassPathXmlApplicationContext("cn/itheima03/spring/aop/multiaspect/applicationContext.xml" ); SalaryManager salarmManager=(SalaryManager) context.getBean( "salaryManager" ); salarmManager.showSalary(); } } =========================================== |
2.配置文件:
< 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.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd" > <!--目标类, 切面类,aop --> < bean id="salaryManager" class="cn.itheima03.spring.aop.multiaspect.SalaryManagerImpl" ></bean> < bean id="logger" class="cn.itheima03.spring.aop.multiaspect.Logger" ></bean> < bean id="security" class="cn.itheima03.spring.aop.multiaspect.Security" ></bean> < bean id="privilege" class="cn.itheima03.spring.aop.multiaspect.Privilege" > <property name= "access" value="admin" ></property> </ bean > < aop:config > <!--切入点 --> <aop:pointcut expression="execution(* cn.itheima03.spring.aop.multiaspect.SalaryManagerImpl.*(..))" id ="sm"/> <!-- 切面 --> <aop:aspect ref= "logger"> <aop:before method= "interceptor" pointcut-ref="sm" /> </aop:aspect> <aop:aspect ref= "security"> <aop:before method= "interceptor" pointcut-ref="sm" /> </aop:aspect> <aop:aspect ref= "privilege"> <!--围绕切入点 --> <aop:around method= "interceptor" pointcut-ref="sm" /> </aop:aspect> </ aop:config > </ beans > |