首页 > 代码库 > spring中的AOP 以及各种通知
spring中的AOP 以及各种通知
理解了前面动态代理对象的原理之后,其实还是有很多不足之处,因为如果在项目中有20多个类,每个类有100多个方法都需要判断是不是要开事务,那么方法调用那里会相当麻烦。
spring中的AOP很好地解决了这个问题,通过 execution表达式 指定哪些包中的那些类 哪些方法 用到事务
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.itcast.spring.sh..*.*(String,?,Integer)) cn.itcast.spring.sh包及子包中所有的类的有三个参数
第一个参数为String,第二个参数为任意类型,
第三个参数为Integer类型的方法
springAOP的具体加载步骤:
1、当spring容器启动的时候,加载了spring的配置文件
2、为配置文件中所有的bean创建对象
3、spring容器会解析aop:config的配置
1、解析切入点表达式,用切入点表达式和纳入spring容器中的bean做匹配
如果匹配成功,则会为该bean创建代理对象,代理对象的方法=目标方法+通知
如果匹配不成功,不会创建代理对象
4、在客户端利用context.getBean获取对象时,如果该对象有代理对象则返回代理对象,如果代理对象,则返回目标对象
说明:如果目标类没有实现接口,则spring容器会采用cglib的方式产生代理对象,如果实现了接口,会采用jdk的方式
实例:
引入包
aspectjrt.jar
aspectjweaver.jar
Person.java
1 package cn.itcast.sh.aop; 2 3 4 import java.io.Serializable; 5 6 7 /** 8 * 对象的序列化的作用:让对象在网络上传输,以二进制的形式传输 9 * @author Think 10 * Serializable标示接口 11 */ 12 public class Person implements Serializable{ 13 private Long pid; 14 private String pname; 15 16 public Person(){} 17 18 public Person(String pname){ 19 this.pname = pname; 20 } 21 22 public Long getPid() { 23 return pid; 24 } 25 public void setPid(Long pid) { 26 this.pid = pid; 27 } 28 public String getPname() { 29 return pname; 30 } 31 public void setPname(String pname) { 32 this.pname = pname; 33 } 34 public String getPsex() { 35 return psex; 36 } 37 public void setPsex(String psex) { 38 this.psex = psex; 39 } 40 private String psex; 41 }
PersonDao.java
1 package cn.itcast.sh.aop; 2 3 public interface PersonDao { 4 public void savePerson(Person person); 5 }
PersonDaoImpl.java
1 package cn.itcast.sh.aop; 2 3 import org.hibernate.Session; 4 5 import cn.itcast.hibernate.sh.utils.HiberanteUtils; 6 7 public class PersonDaoImpl extends HiberanteUtils implements PersonDao{ 8 9 @Override 10 public void savePerson(Person person) { 11 Session session=sessionFactory.getCurrentSession(); 12 session.save(person); 13 } 14 15 }
定义切面 MyTransaction.java
1 package cn.itcast.sh.aop; 2 3 import org.hibernate.Transaction; 4 5 import cn.itcast.hibernate.sh.utils.HiberanteUtils; 6 7 public class MyTransaction extends HiberanteUtils{ 8 9 private Transaction tran; 10 public void beginTransaction() 11 { 12 tran=sessionFactory.getCurrentSession().beginTransaction(); 13 } 14 public void commit() 15 { 16 tran.commit(); 17 } 18 }
hibernate.cfg.xml
1 <?xml version=‘1.0‘ encoding=‘utf-8‘?> 2 <!DOCTYPE hibernate-configuration PUBLIC 3 "-//Hibernate/Hibernate Configuration DTD 3.0//EN" 4 "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> 5 <hibernate-configuration> 6 <!-- 7 一个session-factory只能连接一个数据库 8 --> 9 <session-factory> 10 <!-- 11 数据库的用户名 12 --> 13 <property name="connection.username">root</property> 14 15 <property name="connection.driver_class"> 16 com.mysql.jdbc.Driver 17 </property> 18 19 <!-- 20 密码 21 --> 22 <property name="connection.password">friends</property> 23 <!-- 24 url 25 --> 26 <property name="connection.url"> 27 jdbc:mysql://localhost:3306/hibernate_basic 28 </property> 29 <!-- 30 作用:根据持久化类和映射文件生成表 31 validate 32 create-drop 33 create 34 update 35 --> 36 <property name="hbm2ddl.auto">update</property> 37 38 <property name="hibernate.dialect"> 39 org.hibernate.dialect.MySQLInnoDBDialect 40 </property> 41 <!-- 用于配置当前线程用的 --> 42 <property name="current_session_context_class">thread</property> 43 <!-- 44 显示hibernate内部生成的sql语句 45 --> 46 <property name="show_sql">true</property> 47 <property name="format_sql">true</property> 48 <mapping resource="cn/itcast/sh/aop/Person.hbm.xml" /> 49 50 </session-factory> 51 </hibernate-configuration>
重点applicationContext-aop.xml配置spring提供的切面
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:aop="http://www.springframework.org/schema/aop" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd 7 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> 8 <!-- 9 导入目标类,导入切面 10 --> 11 <bean id="personDao" class="cn.itcast.sh.aop.PersonDaoImpl"></bean> 12 13 <bean id="myTransaction" class="cn.itcast.sh.aop.MyTransaction"></bean> 14 15 <!-- 16 aop的配置 17 --> 18 <aop:config> 19 <!-- 20 切入点表达式 21 --> 22 <aop:pointcut expression="execution(* cn.itcast.sh.aop.PersonDaoImpl.*(..))" id="perform"/> 23 <aop:aspect ref="myTransaction"> 24 25 <aop:before method="beginTransaction" pointcut-ref="perform"/> 26 <aop:after-returning method="commit" pointcut-ref="perform"/> 27 28 <!-- <aop:after method="finallyMethod" pointcut-ref="perform"/> --> 29 <!-- <aop:after-throwing method="throwingMethod" pointcut-ref="perform" throwing="ex"/> --> 30 <!-- <aop:around method="aroundMethod" pointcut-ref="perform"/> --> 31 </aop:aspect> 32 </aop:config> 33 </beans>
测试类
1 package cn.itcast.sh.aop; 2 3 import org.junit.Test; 4 import org.springframework.context.ApplicationContext; 5 import org.springframework.context.support.ClassPathXmlApplicationContext; 6 7 public class test { 8 @Test 9 public void testAop() 10 { 11 Person p=new Person(); 12 p.setPname("new comer"); 13 p.setPsex("boy"); 14 15 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-aop.xml"); 16 PersonDao dao=(PersonDao) context.getBean("personDao"); 17 dao.savePerson(p); 18 } 19 20 }
示例二: 返回值通过 applicationContext.xml中在通知后面添加参数来配置返回值
例如后置通知中 配置 returinn属性
通知:
1、前置通知
1、在目标方法执行之前执行
2、无论目标方法是否抛出异常,都执行,因为在执行前置通知的时候,目标方法还没有执行,还没有遇到异常
2、后置通知
1、在目标方法执行之后执行
2、当目标方法遇到异常,后置通知将不再执行
3、后置通知可以接受目标方法的返回值,但是必须注意:aop:after-returning标签
后置通知的参数的名称和配置文件中returning="var"的值是一致的
3、最终通知:
1、在目标方法执行之后执行
2、无论目标方法是否抛出异常,都执行,因为相当于finally
4、异常通知
1、接受目标方法抛出的异常信息
2、步骤
在异常通知方法中有一个参数Throwable ex
在配置文件中
<aop:after-throwing method="throwingMethod" pointcut-ref="perform" throwing="ex"/>
5、环绕通知 用于判断条件是否让目标方法执行
1、如果不在环绕通知中调用ProceedingJoinPoint的proceed,目标方法不会执行
2、环绕通知可以控制目标方法的执行
PersonDaoImpl.java
1 package cn.itcast.spring.aop.sh; 2 3 import java.lang.reflect.Method; 4 5 import org.junit.Test; 6 7 import cn.itcast.spring.aop.annotation.sh.HibernateUtils; 8 import cn.itcast.spring.aop.annotation.sh.Person; 9 10 public class PersonDaoImpl extends HibernateUtils{ 11 public String savePerson(Person person) { 12 // TODO Auto-generated method stub 13 //int a = 1/0; 14 //sessionFactory.getCurrentSession().save(person); 15 System.out.println("aaaa"); 16 return "aaaa"; 17 } 18 }
用到的类和方法和上面相同,只不过改切面的文件(MyTransaction.java)和 applicationContext-aop.xml文件
1 package cn.itcast.spring.aop.sh; 2 3 import org.aspectj.lang.JoinPoint; 4 import org.aspectj.lang.ProceedingJoinPoint; 5 import org.hibernate.Transaction; 6 7 import cn.itcast.spring.aop.annotation.sh.HibernateUtils; 8 9 public class MyTransaction extends HibernateUtils{ 10 private Transaction transaction; 11 /** 12 * 通过该参数可以获取目标方法的一些信息 13 *在每个通知中都可以加入 JoinPoint joinpoint参数 14 * @param joinpoint 15 */ 16 public void beginTransaction(JoinPoint joinpoint){ 17 System.out.println("bbb"); 18 this.transaction = sessionFactory.getCurrentSession().beginTransaction(); 19 } 20 //var是目标方法的返回值,以上的savePerson函数返回值 aaaa 21 public void commit(Object var){ //保持与 application-aop.xml 中的aop:after-returning元素中 returning中的属性值一致 22 System.out.println(var); 23 this.transaction.commit(); 24 } 25 26 public void finallyMethod(){ 27 System.out.println("finally method"); 28 } 29 30 /** 31 * 异常通知 32 * @param ex 33 */ 34 public void throwingMethod(Throwable ex){ //保持与 application-aop.xml 中的aop:after-returning元素中 throwing中的属性值一致 35 System.out.println(ex.getMessage()); 36 } 37 //JoinPoint是 ProceedingJoinPoint的父类 38 public void aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable{ 39 System.out.println(joinPoint.getSignature().getName()); 40 joinPoint.proceed();//调用目标方法 41 System.out.println("aaaasfdasfd"); 42 } 43 }
applicationContext-aop.xm
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:aop="http://www.springframework.org/schema/aop" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd 7 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> 8 <!-- 9 导入目标类,导入切面 10 --> 11 <bean id="personDao" class="cn.itcast.spring.aop.sh.PersonDaoImpl"></bean> 12 13 <bean id="myTransaction" class="cn.itcast.spring.aop.sh.MyTransaction"></bean> 14 15 <!-- 16 aop的配置 17 --> 18 <aop:config> 19 <!-- 20 切入点表达式 21 --> 22 <aop:pointcut expression="execution(* cn.itcast.spring.aop.sh.PersonDaoImpl.*(..))" id="perform"/> 23 <aop:aspect ref="myTransaction"> 24 <!-- 25 前置通知 <aop:before method="beginTransaction" pointcut-ref="perform"/> 26 后置通知 <aop:after-returning method="commit" pointcut-ref="perform" returning="var"/> 27 --><!--最终通知 异常通知 环绕通知 用于判断是否执行目标对象的--> 28 <aop:after method="finallyMethod" pointcut-ref="perform"/> 29 <aop:after-throwing method="throwingMethod" pointcut-ref="perform" throwing="ex"/> 30 <aop:around method="aroundMethod" pointcut-ref="perform"/> 31 </aop:aspect> 32 </aop:config> 33 </beans>
测试
1 package cn.itcast.spring.aop.sh; 2 3 import org.junit.Test; 4 import org.springframework.context.ApplicationContext; 5 import org.springframework.context.support.ClassPathXmlApplicationContext; 6 7 import cn.itcast.spring.aop.annotation.sh.Person; 8 import cn.itcast.spring.aop.annotation.sh.PersonDaoImpl; 9 10 public class PersonTest { 11 @Test 12 public void test(){ 13 ApplicationContext context = new ClassPathXmlApplicationContext("cn/itcast/spring/aop/sh/applicationContext.xml"); 14 PersonDaoImpl personDao = (PersonDaoImpl)context.getBean("personDao"); 15 Person person = new Person(); 16 person.setPname("aaas"); 17 personDao.savePerson(person); 18 } 19 }