Spring之AOP(面向切面编程)

一、概念:

         Aop编程:

                   关注点代码与业务代码分离!(jdk/cglib代理)

         关注点:

                   重复执行的代码, 也叫关注点代码!

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); 
		} 
   } 

              切面:

                   关注点代码形成的类,就叫做切面

                   SpringAop编程,也叫面向切面编程!

                   Aop: Aspect Object Programming 面向切面编程!     

    

                   哪些是切面?:事务、权限控制、日志…


         切入点表达式:

                   拦截指定方法,给给指定方法所在的类,生成代理对象!

                   Spring在初始化容器的时候,会根据切入点表达式的规则,会符合拦截规则的方法所在的类生成代理对象!


二、使用Aop注解方式开发步骤:

1. 引入Aop相关jar文件

     (aspectj是在spring之前,面向切面开发的公用组件)

aopalliance.jar              spring-framework-2.5.6\lib\aopalliance

aspectjrt.jar                 spring-framework-2.5.6\lib\aspectj

aspectjweaver.jar            spring-framework-2.5.6\lib\aspectj

spring-aop-3.2.5.RELEASE.jar  Spring3.2源码】

2.引入Aop名称空间


	
	
	
	
	
	
    

3. 开启Aop注解

<aop:aspectj-autoproxy>aop:aspectj-autoproxy>

4. 使用Aop相关注解

@Aspect        指定一个类为切面类

                                (切面类也需要实例化)

                                (切面类中的方法,也叫做通知)

@Before           前置通知  【在执行目标对象方法之前执行】

@After            后置通知  【在执行目标对象方法之后执行】

@AfterReturning   返回后通知 【在执行目标对象方法结束后执行, 出现异常不执行】

@AfterThrowing    异常通知  【在执行目标对象方法出现异常时候执行】

@Around           环绕通知  【环绕目标方法执行】

@Pointcut         定义一个切入点表达式变量  (后面使用这个切入点表达式的时候,直接引用方法名即可!)

如:

public class TransactionAop {
	
	// 定义一个切入点表达式变量  (后面使用这个切入点表达式的时候,直接引用方法名即可)
	@Pointcut("execution(* spring_ioc.g_aop_anno.UserDao.*(..))")
	public void pointcut_(){
	}

	//【前置通知】
	// 在执行业务方法,之前执行
	@Before("pointcut_()")
	public void beginTransaction() {
		System.out.println("[前置通知]  开启事务..");
	}

Spring生成代理对象的过程?

            1. 创建容器对象的时候,根据"切入点表达式"拦截的类,生成代理对象。

            2. 如果目标对象有实现接口,使用jdk代理!

            3. 如果目标对象没有实现接口,使用cglib代理!

            4. 从容器获取代理后的对象。

            5. 执行代理对象的方法,在运行时期动态植入"切面"类中的"通知"!


四、使用AopXML方式开发步骤:

         1.引入Aop 相关jar文件

         2. bean.xml  引入Aop名称空间

         3. Aop配置



	
	
	
	
	
	
	
	
	
	
	
		
		
	
		
		
			
			
			
			
			
			
			
			
			
			
			
			
			
			
			
		
	
	
    
  

五、 切入点表达式语法详解

切入点表达式:

         拦截指定的类,生成代理对象!

expression="execution(* cn.itcast.g_execution.UserDao.save(..))"

execution(

modifiers-pattern?                拦截的方法的访问修饰符(? 表示0或多,可以省略!)

ret-type-pattern                  方法返回类型,必须指定(*)

declaring-type-pattern?           拦截的方法所在的类

name-pattern(param-pattern)       拦截的方法(以及方法的参数列表)

throws-pattern?)   方法声明的异常

总结:拦截,一定要指定到方法!

		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		

六、Aop编程之注解方式代码案例:

Spring之AOP(面向切面编程)_第1张图片

package spring_ioc.g_aop_anno;
public interface IUserDao {
	void save();
}

package spring_ioc.g_aop_anno;
import org.springframework.stereotype.Repository;
@Repository	// 把对象加入ioc容器
public class UserDao implements IUserDao{
	public void save() {
		System.out.println("保存...");
	}
}

package spring_ioc.g_aop_anno;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

// 重复代码
@Component("aop")
@Aspect      // 指定一个类为切面类
public class TransactionAop {
	
	// 定义一个切入点表达式变量  (后面使用这个切入点表达式的时候,直接引用方法名即可)
	@Pointcut("execution(* spring_ioc.g_aop_anno.UserDao.*(..))")
	public void pointcut_(){
	}

	//【前置通知】
	// 在执行业务方法,之前执行
	@Before("pointcut_()")
	public void beginTransaction() {
		System.out.println("[前置通知]  开启事务..");
	}
	
	//【后置通知】
	// 在执行业务方法,之后执行
	@After("pointcut_()")
	public void commit() {
		System.out.println("[后置通知] 提交事务..");
	}
	
	// 【返回后通知】   在执行目标方法结束后执行, 出现异常不会执行
	@AfterReturning("pointcut_()")
	public void afterReturing(){
		System.out.println("[返回后通知]");
	}
	
	// 【异常通知】   在执行目标方法的时候出现异常执行
	@AfterThrowing("pointcut_()")
	public void afterThrowing(){
		System.out.println("[异常通知]");
	}
	
	// 【环绕通知】 会环绕目标方法执行
	@Around("pointcut_()")
	public void arroud(ProceedingJoinPoint pjp) throws Throwable{
		System.out.println("[环绕前:]");
		pjp.proceed();    			   // 执行目标方法
		System.out.println("[环绕后:]");
	}

}

package spring_ioc.g_aop_anno;
import org.springframework.stereotype.Repository;
@Repository
public class OrderDao {
	public void save(){
		System.out.println("订单已保存!");
	}
}



	
	
	
	
	
	
 

package spring_ioc.g_aop_anno;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
	 private ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml",getClass());
	 // jdk代理(注意:目标类UserDao实现IUserDao接口!)
	@Test
	public void testApp() throws Exception {
		//从Spring的IOC容器中获取对象,使用接口接收!
		IUserDao userDao = (IUserDao)ac.getBean("userDao");
		System.out.println(userDao.getClass());
		userDao.save();
		
		/*//使用接口接收会报错:java.lang.ClassCastException: $Proxy13 cannot be cast to spring_ioc.g_aop_anno.UserDao
		UserDao userDao = (UserDao)ac.getBean("userDao");
		System.out.println(userDao.getClass());
		userDao.save();*/
		
		/*总结:在Spring的AOP编程中,符合切入点表达式的目标类,如果目标类有实现接口,从容器中获取目标对象的时候一定要通过接口接收否则会报错!
		 * 
		 */
	}
	
	
	//cglib代理(注意:目标类UserDao未实现IUserDao接口!)
	@Test
	public void testApp2(){
		UserDao userDao = (UserDao) ac.getBean("userDao");
		System.out.println(userDao.getClass());
		userDao.save();
	}
	
	
	//测试:没有被切入点表达式拦截的类(OrderDao)是不会生成代理对象的!
	@Test
	public void testApp3(){
		OrderDao orderDao = (OrderDao)ac.getBean("orderDao");
		orderDao.save();
	}
}

testApp():
Spring之AOP(面向切面编程)_第2张图片

testApp2():(注意:UserDao实现IUserDao要取消,cglib代理目标类不能实现接口!)

Spring之AOP(面向切面编程)_第3张图片

testApp3():(切入点表达式未拦截OrderDao类,所以切面类没有起作用,仅仅输出OrderDao中的save()方法!)

Spring之AOP(面向切面编程)_第4张图片


七、Aop编程之xml方式代码案例:

Spring之AOP(面向切面编程)_第5张图片

package spring_ioc.h_aop_xml;
public interface IUserDao {
	void save();
}

package spring_ioc.h_aop_xml;
public class UserDao implements IUserDao{
	public void save() {
		System.out.println("保存...");
	}
}

package spring_ioc.h_aop_xml;
import org.aspectj.lang.ProceedingJoinPoint;
//切面类
public class TransactionAop {
	public void beginTransaction() {
		System.out.println("[前置通知]  开启事务..");
	}
	
	public void commit() {
		System.out.println("[后置通知] 提交事务..");
	}
	
	public void afterReturing(){
		System.out.println("[返回后通知]");
	}
	
	public void afterThrowing(){
		System.out.println("[异常通知]");
	}
	
	public void arroud(ProceedingJoinPoint pjp) throws Throwable{
		System.out.println("[环绕前:]");
		pjp.proceed();    			   // 执行目标方法
		System.out.println("[环绕后:]");
	}

}

package spring_ioc.h_aop_xml;
public class OrderDao {
	public void save(){
		System.out.println("订单已保存!");
	}
}



	
	
	
	
	
	
	
	
	
	
	
		
		
	
		
		
			
			
			
			
			
			
			
			
			
			
			
			
			
			
			
		
	
	
  

package spring_ioc.h_aop_xml;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
	 private ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml",getClass());
	 // jdk代理(注意:目标类UserDao实现IUserDao接口!)
	@Test
	public void testApp() throws Exception {
		//从Spring的IOC容器中获取对象,使用接口接收!
		IUserDao userDao = (IUserDao)ac.getBean("userDao");
		System.out.println(userDao.getClass());
		userDao.save();
		
		/*//使用接口接收会报错:java.lang.ClassCastException: $Proxy13 cannot be cast to spring_ioc.g_aop_anno.UserDao
		UserDao userDao = (UserDao)ac.getBean("userDao");
		System.out.println(userDao.getClass());
		userDao.save();*/
		
		/*总结:在Spring的AOP编程中,符合切入点表达式的目标类,如果目标类有实现接口,从容器中获取目标对象的时候一定要通过接口接收否则会报错!
		 * 
		 */
	}
	
	
	//cglib代理(注意:目标类UserDao未实现IUserDao接口!)
	@Test
	public void testApp2(){
		UserDao userDao = (UserDao) ac.getBean("userDao");
		System.out.println(userDao.getClass());
		userDao.save();
	}
	
	
	//测试:没有被切入点表达式拦截的类(OrderDao)是不会生成代理对象的!
	@Test
	public void testApp3(){
		OrderDao orderDao = (OrderDao)ac.getBean("orderDao");
		orderDao.save();
	}
}

tespApp():

Spring之AOP(面向切面编程)_第6张图片
testApp2():(注意:UserDao实现IUserDao要取消,cglib代理目标类不能实现接口!)

Spring之AOP(面向切面编程)_第7张图片

testApp3():(切入点表达式未拦截OrderDao类,所以切面类没有起作用,仅仅输出OrderDao中的save()方法!)



参考资料:传智播客袁杰老师SpringAop讲解

你可能感兴趣的:(aop,编程,代理,java,Spring)