一、概念:
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编程之注解方式代码案例:
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():
testApp2():(注意:UserDao实现IUserDao要取消,cglib代理目标类不能实现接口!)
testApp3():(切入点表达式未拦截OrderDao类,所以切面类没有起作用,仅仅输出OrderDao中的save()方法!)
七、Aop编程之xml方式代码案例:
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():
testApp2():(注意:UserDao实现IUserDao要取消,cglib代理目标类不能实现接口!)
testApp3():(切入点表达式未拦截OrderDao类,所以切面类没有起作用,仅仅输出OrderDao中的save()方法!)
参考资料:传智播客袁杰老师SpringAop讲解