Spring--AOP自动代理

Spring AOP的自动创建代理可分为三种:

  • BeanNameAutoProxyCreator 根据Bean名称创建代理

  • DefaultAdvisorAutoProxyCreator 根据Advisor本身包含信息创建代理

  • AnnotationAwareAspectJAutoProxyCreator基于Bean中的AspectJ注解进行自动代理

1、BeanNameAutoProxyCreator 根据Bean名称创建代理

1、新建一个学生接口

public interface StudentDao {
   public void find();
   public void update();
   public void save();
   public void delete();
}

2、实现StudentDao接口


public class StudentDaoImpl implements StudentDao {
    public void find() {
        System.out.println("查询学生");
    }
    public void update() {
        System.out.println("修改学生");
    }
    public void save() {
        System.out.println("保存学生");
    }
    public void delete() {
        System.out.println("删除学生");
    }
}

3、再创建一个类CustomerDao:

public class CustomerDao {
    public void find(){
        System.out.println("查询客户");
    }
    public void save(){
        System.out.println("保存客户");
    }
    public void update(){
        System.out.println("修改客户");
    }
    public void delete(){
        System.out.println("删除客户");
    }
}

4、创建一个前置增强类MybeforeAdvice实现MethodBeforeAdvice接口,重写方法

import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;

public class MybeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("这是前置增强(通知)");
    }
}

5、再写一个环绕增强类吧。。MyAroundAdvice实现MethodInterceptor

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;


public class MyAroundAdvice implements MethodInterceptor {

    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("环绕前增强");
        //执行目标方法
        Object obj = invocation.proceed();
        System.out.println("环绕后增强");
        return obj;
    }
}

6、配置文件

    
    
    
    
    
    

    
    	
        
        

    

7、测试


    @Test
    public void demo1(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext4.xml");

        StudentDao studentDao = (StudentDao) applicationContext.getBean("studentDao");
        CustomerDao customerDao = (CustomerDao) applicationContext.getBean("customerDao");

        studentDao.save();
        studentDao.find();
        studentDao.delete();
        studentDao.update();
        customerDao.save();
        customerDao.find();
        customerDao.delete();
        customerDao.update();
    }

DefaultAdvisorAutoProxyCreator 根据Advisor本身包含信息创建代理(根据切面信息)

只需改变配置文件


    
    
    
    
    
        
    
    
    
    	
        
        
        
    
    

AnnotationAwareAspectJAutoProxyCreator基于Bean中的AspectJ注解进行自动代理

需要jar:

  • spring-aop
  • com.springsource.org.aopalliance
  • spring-aspects
  • com.springsource.org.aspectj.wever
    环境准备


        
        
        

@AspectJ提供不同的通知类型

  • @Before 前置通知,相当于BeforeAdvice
  • @AfterReturning 后置通知,相当于AfterReturningAdvice
  • @Around 环绕通知,相当于MethodInterceptor
  • @AfterThrowing 异常抛出通知,相当于ThrowAdvice
  • @After最终final通知,不管是否异常,该通知都会执行
  • @DeclareParents 引介通知,相当于IntroductionInterceptor

在通知中通过value属性定义切点
通过execution函数,可以定义切点的方法切入

  • 语法 execution(<访问修饰符>?<返回值类型><方法名>(<参数>)<异常>)
    • 例如匹配所有类public方法 execution(public * * (…))
    • 匹配指定包下所有类方法 execution(* com.dao.*( . . ))不包含子包
    • execution(* com.dao…*(…)) …*表示包、子孙包下所有类
    • 匹配指定类所有方法 execution(* com.service.UserService.*(…))
    • 匹配实现特定接口所有类方法 execution(* com.dao.UserDao+.*(…))
    • 匹配所有save开头的方法 execution(* save*(…))

代码演示
1、创建ProductDao

public class ProductDao {
    public void save(){
        System.out.println("保存商品");
    }
    public void update(){
        System.out.println("修改商品");
    }
    public void delete(){
        System.out.println("删除商品");
    }
    public String findOne(){
        System.out.println("查询一个商品");
        return "hello";
    }
    public void findAll(){
        System.out.println("查询所有商品");
        //int i = 1/0;//发生异常通知
    }
}

2、配置文件配置信息,完成自动代理、目标类、切面的定义



        
        

        
        
        
        

3、创建增强类


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

/**
 *切面类
 */
@Aspect
public class MyAspectAnno {
    //@Before前置通知:可以在方法中传入JoinPoint对象,用来获得切点信息
    @Before(value = "execution(* com.aspectJ.demo1.ProductDao.save(..))")
    public void before(JoinPoint joinPoint){
        System.out.println("前置通知"+joinPoint);
    }
    //@AfterReturning后置通知:通过return属性可以定义方法返回值,作为参数
    @AfterReturning(value = "execution(* com.aspectJ.demo1.ProductDao.update(..))",returning = "result")
    public void afterReturning(Object result){
        System.out.println("后置通知"+result);
    }
    //@Around环绕通知: around方法的返回值就是目标代理方法执行返回值
    //参数为ProceedingJoinPoint可以调用拦截目标方法执行
    @Around(value = "execution(* com.aspectJ.demo1.ProductDao.delete(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕前通知===");
        Object obj = joinPoint.proceed();//执行目标方法,不调用不执行目标方法(即拦截目标方法)
        System.out.println("环绕后通知===");
        return obj;
    }

    //@AfterThrowing 异常通知:当目标方法发生异常时调用,可输出异常信息
    @AfterThrowing(value = "execution(* com.aspectJ.demo1.ProductDao.findAll(..))",throwing = "e")
    //设置参数,打印异常信息
    public void afterThrowing(Throwable e){
        //目标类发生异常
        System.out.println("异常通知"+e);
    }

    //@After最终通知:无论是否出现异常,最终通知总是会被执行
    @After(value = "execution(* com.aspectJ.demo1.ProductDao.findAll(..))")
    public void after(){
        System.out.println("最终通知");
    }

}

4、测试
jar要求:junit版本要求4以上,并且要与spring-test版本一致

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext5.xml")
public class SpringDemo1 {
    @Resource(name = "productDao")
    private ProductDao productDao;

    @Test
    public void demo1(){
        productDao.update();
        productDao.save();
        productDao.findAll();
        productDao.findOne();
        productDao.delete();
    }
}

总结
在每个通知内定义切点,会造成工作量大,不易维护,对于重复的切点,可以使用@Pointcut进行定义

切点方法

private void 无参数方法,方法名为切入点。当通知多个切点时,可以使用 || 进行连接

使用前:

    //@Before前置通知:可以在方法中传入JoinPoint对象,用来获得切点信息
    @Before(value = "execution(* com.aspectJ.demo1.ProductDao.save(..))")
    public void before(JoinPoint joinPoint){
        System.out.println("前置通知"+joinPoint);
    }

使用后。方便对具有相同通知类型进行统一管理

    @Pointcut("execution(* com.aspectJ.demo1.ProductDao.save(..))")
    public void myPointcut1(){}
    
    //@Before前置通知:可以在方法中传入JoinPoint对象,用来获得切点信息
    @Before(value = "myPointcut1()")
    public void before1(JoinPoint joinPoint){
        System.out.println("前置通知"+joinPoint);
    }

使用aspectJ的xml方式开发

1、新建一个接口CustomerDao

public interface CustomerDao {
    public void save();
    public void delete();
    public void findOne();
    public String update();
    public  void findAll();
}

2、新建CustomerDao实现类CustomerDaoImpl


public class CustomerDaoImpl implements CustomerDao {
    public void save() {
        System.out.println("保存客户");
    }

    public void delete() {
        System.out.println("删除客户");
       // int i=1/0;
    }

    public void findOne() {
        System.out.println("查询单个客户");
    }

    public String update() {
        System.out.println("修改用户");
        return "Spring";
    }

    public void findAll() {
        System.out.println("查询所有用户");
    }
}

3、新建增强(通知)类


import org.aspectj.lang.ProceedingJoinPoint;


public class MyAspectXML {

    public void before(){
        System.out.println("前置增强");
    }
    //获取返回值,参数名与XML配置文件returning属性要相同
    public void afterReturning(Object result){
        System.out.println("后置增强"+result);
    }

    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕前通知===");
        Object obj = joinPoint.proceed();//执行目标方法,不调用不执行目标方法(即拦截目标方法)
        System.out.println("环绕后通知===");
        return obj;
    }

    public void afterThrowing(Throwable e){
        //目标类发生异常
        System.out.println("异常通知"+e.getMessage());
    }
    public void after(){
        System.out.println("最终通知");
    }


}

4、配置文件



       
        
        
        
        

        
        
                
                
                
                
                
                
                
                  
                  
                  
                  
                  
                  
                
        

5、测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:applicationContext6.xml")
public class SpringDemo2 {
    @Resource(name = "customerDao")
    private CustomerDao customerDao;

    @Test
    public void demo1(){
        customerDao.save();
        customerDao.update();
        customerDao.findOne();
        customerDao.delete();
        customerDao.findAll();

    }
}

你可能感兴趣的:(spring)