spring03 AOP、各種通知、一/多個切面例子
一、 springAOP
1、作用: 使得事務、日誌、安全性框架、許可權、目標方法之間完全是鬆耦合的
2、組成
1、切面: 事務、日誌、安全性框架、許可權等都是切面
2、通知: 切面中的方法就是通知
3、目標類
4、切入點 只有符合切入點,才能讓通知和目標方法結合在一起
5、織入: 形成代理物件的方法的過程
注:代理物件的方法就是通知和目標方法的結合體
3、相對於動態代理,省略了寫攔截器的過程
4、切入點表示式詳解
5、springAOP的具體載入步驟:
1、當spring容器啟動的時候,載入了spring的配置檔案
2、為配置檔案中所有的bean建立物件
3、spring容器會解析aop:config的配置
解析切入點表示式,用切入點表示式和納入spring容器中的bean做匹配
如果匹配成功,則會為該bean建立代理物件,代理物件的方法=目標方法+通知
如果匹配不成功,不會建立代理物件
4、在客戶端利用context.getBean獲取物件時,如果該物件有代理物件則返回代理物件,
如果無代理物件,則返回目標物件
5、如果目標類沒有實現介面,則spring容器會採用cglib的方式產生代理物件,
如果實現了介面,會採用jdk的方式
6、各種通知詳解
1、前置通知
1、在目標方法執行之前執行
2、無論目標方法是否丟擲異常,都執行,因為在執行前置通知的時候,目標方法還沒有執行,還沒有遇到異常
例:
------------------------------------------------------------------------------
<aop:before method="beginTransaction" pointcut-ref="perform"/>
// 前置通知
public void beginTransaction(){
System.out.println("before inform");
this.transaction = sessionFactory.getCurrentSession().beginTransaction();
}
------------------------------------------------------------------------------
2、後置通知
1、在目標方法執行之後執行
2、當目標方法遇到異常,後置通知將不再執行
3、後置通知可以接受目標方法的返回值,但是必須注意:
後置通知的引數的型別為Object,且名稱和配置檔案中returning="var"的值是一致的
例:
------------------------------------------------------------------------------
不帶引數的
<aop:after-returning method="commit" pointcut-ref="perform" />
// 後置通知
public void commit(){
System.out.println("after return inform");
this.transaction.commit();
}
// 不帶返回值的目標方法
public void savePerson(Person person) {
sessionFactory.getCurrentSession().save(person);
}
帶引數的
<aop:after-returning method="commit" pointcut-ref="perform" returning="var"/>
public void commit(Object var){
System.out.println("after return inform");
System.out.println(var);
this.transaction.commit();
}
// 帶返回值的目標方法
public String savePerson(Person person) {
//int i = 1/0;
System.out.println("目標方法");
return "返回值";
}
------------------------------------------------------------------------------
3、最終通知:
1、在目標方法執行之後執行
2、無論目標方法是否丟擲異常,都執行,因為相當於finally
例:
------------------------------------------------------------------------------
<aop:after method="fianllyMethod" pointcut-ref="perform"/>
public void fianllyMethod(){
System.out.println("finally inform");
}
------------------------------------------------------------------------------
4、異常通知
1、接受目標方法丟擲的異常資訊
2、步驟
在異常通知方法中有一個引數Throwable ex
在配置檔案中
<aop:after-throwing method="throwingMethod" pointcut-ref="perform" throwing="ex"/>
注: throwing也要和切面中的異常通知的型別為Throable的引數名稱保持一致
例:
-------------------------------------------------------------------------------
<aop:after-throwing method="throwingMethod" pointcut-ref="perform" throwing="ex"/>
public void throwingMethod(Throwable ex){
System.out.println(ex.getMessage());
}
// 目標類
public void savePerson(Person person) {
int i = 1/0;
sessionFactory.getCurrentSession().save(person);
}
-------------------------------------------------------------------------------
5、環繞通知
1、環繞通知可以控制目標方法的執行
方式: 在環繞通知中加一個引數:
org.aspectj.lang.JoinPoint包中的ProceedingJoinPoint joinPoint
然後在環繞通知中顯示呼叫ProceedingJoinPoint的proceed()方法,,則執行目標方法
2、同時,各種通知若加JoinPoint引數(org.aspectj.lang.JoinPoint包),還可以獲取目標方法的資訊
例:
-------------------------------------------------------------------------------
不呼叫目標方法:
<aop:around method="aroundMethod" pointcut-ref="perform" />
public void aroundMethod(){
System.out.println("around inform");
}
// 目標方法 注: 此時未呼叫目標方法
public String savePerson(Person person) {
System.out.println("目標方法");
return "返回值";
}
呼叫目標方法:
<aop:around method="aroundMethod" pointcut-ref="perform" />
public void aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable{
joinPoint.proceed(); // 顯示的呼叫目標方法的執行,可以控制目標方法是否執行
System.out.println("around inform with targetMethod");
System.out.println("目標方法的方法名:"+joinPoint.getSignature().getName());
}
// 目標方法: 注: 此時呼叫了目標方法
public String savePerson(Person person) {
System.out.println("目標方法");
return "返回值";
}
-------------------------------------------------------------------------------
7、例1: 一個切面
--------------------------------------------------------------------------------
public class Person implements Serializable {
private Long pid;
private String pname;
private String psex;
}
// 介面
public interface PersonDao {
public void savePerson(Person person);
}
// 目標類
public class PersonDaoImpl extends HibernateUtil implements PersonDao {
@Override
public void savePerson(Person person) {
sessionFactory.getCurrentSession().save(person);
}
}
// 切面
public class MyTransaction extends HibernateUtil{
private Transaction transaction;
// 通知
public void beginTransaction(){
this.transaction = sessionFactory.getCurrentSession().beginTransaction();
}
// 通知
public void commit(){
this.transaction.commit();
}
}
配置檔案:
<!-- AOP 匯入目標類,匯入切面 -->
<bean id="personDao" class="cn.itcast.spring.aop.PersonDaoImpl"></bean>
<bean id="myTransaction" class="cn.itcast.spring.aop.MyTransaction"></bean>
<!-- AOP 的配置 -->
<aop:config>
<!-- 切入點表示式 裡面含有目標方法 -->
<aop:pointcut expression="execution(* cn.itcast.spring.aop.PersonDaoImpl.*(..))" id="perform"/>
<!-- 宣告切面 -->
<aop:aspect ref="myTransaction">
<!-- 宣告通知及其觸發時間 -->
<aop:before method="beginTransaction" pointcut-ref="perform"/>
<aop:after-returning method="commit" pointcut-ref="perform"/>
</aop:aspect>
</aop:config>
測試:
public class PersonTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext(
"cn/itcast/spring/aop/applicationContext.xml");
// 注: 此處得到的是代理物件
PersonDao personDao = (PersonDao) context.getBean("personDao");
Person person = new Person();
person.setPname("aaa");
person.setPsex("男");
personDao.savePerson(person);
}
}
--------------------------------------------------------------------------------
例1: 多個切面
--------------------------------------------------------------------------------
目標類:
public class SalaryManagerImpl {
public void show(){
System.out.println("目標方法:檢視工資");
}
}
切面1:
public class Logger {
public void logger(){
System.out.println("logger");
}
}
切面2:
public class Security {
public void security(){
System.out.println("security");
}
}
切面3:
public class Privilege {
private String access;
// set/get屬性
public void hasAccess(ProceedingJoinPoint joinPoint) throws Throwable{
if(this.access.equals("admin")){
joinPoint.proceed();
}else{
System.out.println("對不起,您沒有許可權");
}
}
}
配置檔案:
<bean id="salaryManager" class="cn.itcast.spring.aop.aspeces.SalaryManagerImpl"></bean>
<bean id="logger" class="cn.itcast.spring.aop.aspeces.Logger"></bean>
<bean id="security" class="cn.itcast.spring.aop.aspeces.Security"></bean>
<bean id="privilege" class="cn.itcast.spring.aop.aspeces.Privilege">
<property name="access" value="admin1"></property>
</bean>
<aop:config>
<aop:pointcut expression="execution(
* cn.itcast.spring.aop.aspeces.SalaryManagerImpl.*(..))" id="perform"/>
<aop:aspect ref="logger">
<aop:before method="logger" pointcut-ref="perform"/>
</aop:aspect>
<aop:aspect ref="security">
<aop:before method="security" pointcut-ref="perform"/>
</aop:aspect>
<aop:aspect ref="privilege">
<aop:around method="hasAccess" pointcut-ref="perform"/>
</aop:aspect>
</aop:config>
測試:
public class SalaryTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("
cn/itcast/spring/aop/aspeces/applicationContext.xml");
SalaryManagerImpl proxy = (SalaryManagerImpl) context.getBean("salaryManager");
proxy.show();
}
}
--------------------------------------------------------------------------------
8、例:統一的錯誤異常處理
--------------------------------------------------------------------------------
public class PersonAction {
private PersonService personService = new PersonServiceImpl();
// set/get屬性
public String savePerson() throws Exception{
this.personService.savePerson();
return null;
}
}
public interface PersonService {
public void savePerson() throws Exception;
}
public class PersonServiceImpl implements PersonService {
PersonDao personDao = new PersonDaoImpl();
// set/get屬性
@Override
public void savePerson() throws Exception {
this.personDao.savePerson();
throw new RuntimeException("service層異常");
}
}
public interface PersonDao {
public void savePerson() throws Exception;
}
public class PersonDaoImpl implements PersonDao {
@Override
public void savePerson() throws Exception{
System.out.println("save person daoImpl");
throw new RuntimeException("dao層異常");
}
}
public class MyException {
public void myException(Throwable ex){
System.out.println(ex.getMessage());
}
}
配置檔案:
<bean id="personDao" class="cn.itcast.dao.impl.PersonDaoImpl"></bean>
<bean id="personService" class="cn.itcast.service.impl.PersonServiceImpl">
<property name="personDao">
<ref bean="personDao"/>
</property>
</bean>
<bean id="personAction" class="cn.itcast.action.PersonAction">
<property name="personService">
<ref bean="personService"/>
</property>
</bean>
<bean id="myException" class="cn.itcast.myException.MyException"></bean>
<aop:config>
<aop:pointcut expression="execution(* cn.itcast.service.impl.*.*(..))" id="perform"/>
<aop:aspect ref="myException">
<aop:after-throwing method="myException" pointcut-ref="perform" throwing="ex"/>
</aop:aspect>
</aop:config>
測試:
public class UnionExceptionTest {
@Test
public void test() throws Exception {
ApplicationContext context = new ClassPathXmlApplicationContext("
applicationContext.xml");
PersonAction personAction = (PersonAction) context.getBean("personAction");
personAction.savePerson();
}
}
--------------------------------------------------------------------------------
9、 springAOP註解的形式
--------------------------------------------------------------------------------
public class Person implements Serializable {
private Long pid;
private String pname;
private String psex;
// set/get屬性
}
public interface PersonDao {
public void savePerson(Person person);
}
@Repository("personDao")
public class PersonDaoImpl extends HibernateUtil implements PersonDao {
@Override
public void savePerson(Person person) {
sessionFactory.getCurrentSession().save(person);
}
}
/**
@Aspect相當於:
<aop:config></aop:config>
*/
// 切面
@Component("myTransaction")
@Aspect
public class MyTransaction extends HibernateUtil{
private Transaction transaction;
/**
@Pointcut("execution(* cn.itcast.spring.aop.annotation.PersonDaoImpl.*(..))")相當於
<aop:pointcut expression="execution(* cn.itcast.spring.aop.annotation.PersonDaoImpl.*(..))" id="perform"/>
*/
@Pointcut("execution(* cn.itcast.spring.aop.annotation.PersonDaoImpl.*(..))")
private void perform(){} // 方法簽名,返回值必須是void,並且方法的修飾符最好是private
// 前置通知
/**
@Before("perform()")相當於:
<aop:before method="beginTransaction" pointcut-ref="perform"/>
*/
@Before("perform()")
public void beginTransaction(){
this.transaction = sessionFactory.getCurrentSession().beginTransaction();
}
// 後置通知
/**
@AfterReturning(value="perform()",returning="var")相當於:
<aop:after-returning method="commit" pointcut-ref="perform" returning="var"/>
*/
@AfterReturning(value="perform()",returning="var")
public void commit(Object var){
this.transaction.commit();
}
}
配置檔案:
<!--
把目標類和切面納入到spring容器中管理,啟動類掃描機制
-->
<context:component-scan base-package="cn.itcast.spring.aop.annotation"/>
<!-- 啟動AOP的註解解析器 然後再在切面中寫註解來代替配置資訊 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
測試:
public class PersonTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("
cn/itcast/spring/aop/annotation/applicationContext.xml");
PersonDao personDao = (PersonDao) context.getBean("personDao");
Person person = new Person();
person.setPname("bbb");
person.setPsex("男");
personDao.savePerson(person);
}
}
--------------------------------------------------------------------------------