SpringAop

AopOfSpring

本章内容:

  • 面向切面编程的基本原理

  • 通过POJO创建切面

  • 使用@AspectJ注解

  • 为AspectJ切面注入依赖

 

4.1什么是切面编程呢

切面能帮助我们模块化横切关注点,对于许多需要相同同的方法去增强的bean来说,一个个来将增强代码写入其中,显然是不可取的, 假如有一百个类需要增强,那么就得写一百次增强方法,再加上修改,这不得逆天,所以AOP面向切面编程出现了

4.1.1 定义AOP术语

不多说,上图

Spring切面可以有5种类型得通知:

  • 前置通知:在目标方法调用之前调用通知

  • 后置通知:在目标方法调用之后调用通知

  • 返回通知:在目标方法成功执行后调用通知

  • 异常通知:在目标方法抛出异常后调用通知

  • 环绕通知(重点):通知包裹了被通知得方法,在被通知得方法调用之前和调用之后执行自定义得行为

4.1.2 Spring对Aop的支持

Spring提供了四种类型的AOP支持:

  • 基于代理的经典SpringAOP(太过笨重复杂,不做介绍)

  • 纯POJO切面

  • @AspectJ注解驱动的切面

  • 注入式是AspectJ切面(适用于各个版本)

前三种都是SpringAOP的变体,需要构建在JDK动态代理上,所以Spring对AOP的支持局限于方法拦截

spring在运行时通知对象

当Spring在调用bean的方法前,并不是调用目标类的方法,而是执行代理对象的方法.即当代理类拦截到方法调用时,在调用目标方法之前,会执行切面逻辑

 

4.2 通过切点来选择连接点

这边主要会介绍有关于切点的编写

如下

package concert;
//注意包名以及类名
public interface Performance {
    
    public void perform();
}

当我们使用AspectJ切点表达式选择Performance的perform()方法时

 

我们也可以使用within()指示器来限制匹配,使用方法如下

execution(* concert.Performance.perform(..)) && within(concert
*)

&&符号表示的是and操作符,即必须是concert所在包下的任意类的方法

当然我们也可以使用'||'操作符来标识(or)关系,而使用'!'操作符来标识非(not)操作

在XML中'&'有特殊含义,我们可以是用and,or,not代替符号

 

4.2.2 在切点中选择bean

//执行concert.Performance类中perform方法,但是bean的id为woodstock
execution(* concert.Performance.perform(..)) and bean('woodstock')
  
  //执行concert.Performance类中perform方法,但是bean的id不为woodstock
execution(* concert.Performance.perform(..)) and !bean('woodstock')

 

4.3 使用注解创建切面

//切面类
@Aspect
public class Audience {
        
    //来看演出需要静音手机
    @Before("execution(* concert.Performance.perform(..))")
    public void silenceCellphones() {
        System.out.println("Silenciong cell phones");
    }
    
    //静音手机
    @Before("execution(* concert.Performance.perform(..))")
    public void takeSeats() {
        System.out.println("takingSeats");
    }
    
    //表演的成功有掌声
    @AfterReturning("execution(* concert.Performance.perform(..))")
    public void applause() {
        System.out.println("CLAP CLAP");
    }
    
    //表演出现了异常 会要求退票
    @AfterThrowing("execution(* concert.Performance.perform(..))")
    public void demanRefund() {
        System.out.println("Demanding a refund");
    }
}

Spring使用AspectJ注解来声明通知方法

注解通知
@After通知方法会在目标返回或抛出异常后调用
@AfterReturning通知方法会在目标方法返回后调用
@AfrterThrowing通知方法会在目标方法抛出异常后调用
@Around通知方法会将目标方法封装起来
@Before通知方法会在目标方法调用之前执行

从上面的代码我们发现,一个切点表达式重复了四次,这可不是什么好玩的事情,我们可以做一些改造

@Aspect
public class Audience {
    
    //此方法不需要有任何执行,仅仅是一个表达式,让@PointCut去依附,仅仅是一个标识
    @Pointcut("execution(* concert.Performance.perform(..))")
    public void performance() {
    }
        
    //来看演出需要静音手机
    @Before("performance()")
    public void silenceCellphones() {
        System.out.println("Silenciong cell phones");
    }
    
    //静音手机
    @Before("performance()")
    public void takeSeats() {
        System.out.println("takingSeats");
    }
    
    //表演的成功有掌声
    @AfterReturning("performance())")
    public void applause() {
        System.out.println("CLAP CLAP");
    }
    
    //表演出现了异常 会要求退票
    @AfterThrowing("performance())")
    public void demanRefund() {
        System.out.println("Demanding a refund");
    }
}

当然Audience切面也可以像其他bean一样

@Bean
public Audience audience(){
    return new Audience();
}

在前面说过,我们调用的方法其实是个代理对象,即我们应该启动AspectJ自动代理

@ComponentScan
@Configuration
//启动AspectJ代理
@EnableAspectJAutoProxy
public class JavaConfig {
    @Bean
    public Audience audience(){
        return new Audience();
    }
}

也可以在XML声明中

<aop:aspectj-autoproxy />

 

带有参数得AOP

Aspect类

@Aspect
public class Audience {
    
    //此方法不需要有任何执行,仅仅是一个表达式,让@PointCut去依附,仅仅是一个标识
    @Pointcut("execution(** concert.ServiceTest.service3(Integer)) && args(param1)")
    public void performance(Integer param1) {
    }
        
    //来看演出需要静音手机
    @Before("performance(param1)")
    public void silenceCellphones(Integer param1) {
        
        System.out.println("Silenciong cell phones" + "  "+param1);
    }
    }

JavaConfig配置类

@ComponentScan
@Configuration
@EnableAspectJAutoProxy
public class JavaConfig {
    @Bean
    public Audience audience() {
        return new Audience();
    }
    
}
​

Service类

@Service
public class ServiceTest {
​
    public void service2(Integer param1, String param2) {
        System.out.println("I am services 2 and I have param:"+param1+"     "+param2);
    }
​
}

Test类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=JavaConfig.class)
public class JavaAopTest {
    @Autowired
    private ServiceTest st;
    
    @Test
    public void test() {
        st.service3(6);
    }
}

XML配置可自行尝试,示例事务管理配置(不是以上得使用)

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <!-- 传播行为 -->
        <tx:method name="save*" propagation="REQUIRED"/>
        <tx:method name="delete*" propagation="REQUIRED"/>
        <tx:method name="insert*" propagation="REQUIRED"/>
        <tx:method name="update*" propagation="REQUIRED"/>
        <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
        <tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
        <tx:method name="select*" propagation="SUPPORTS" read-only="true"/>
    </tx:attributes>
</tx:advice>
<!-- aop -->
<aop:config>
    <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.pjh.atcrowdfunding..*Service.*(..))"/>
</aop:config>

 

接下来到了个重要环节即AOP底层实现,AOP得底层实现是使用了JDK动态代理,JDK动态代理需要被增强类有接口

下面来看下如何实现JDK的动态代理

Advice增加类

public class AdviceClass {
    public void advice1() {
        System.out.println("这是增强办法1111");
    }
    public void advice2() {
        System.out.println("这是增强办法22222222222");
    }
    public void advice3() {
        System.out.println("这是增强办法333333333333333333");
    }
}

目标类接口

/**
 * 目标类接口
 * @author Administrator
 *
 */
public interface ItemsService {
    
    public void addItems(int i, int b)throws Exception;
    public void updateItems()throws Exception;
    public void deleteItems()throws Exception;
}

目标类

public class ItemsServiceImpl implements ItemsService {
​
    @Override
    public void addItems(int i, int b) throws Exception {
        System.out.println("this is a:  addItems"+i+"this is b    "+b);
    }
​
    @Override
    public void updateItems() throws Exception {
        System.out.println("this is a:  updateItems");
        
    }
​
    @Override
    public void deleteItems() throws Exception {
        System.out.println("this is a:  deleteItems");
        
    }
​
}

 

当然,有了目标类和增强类以后,我们需要将两者结合起来

代理工厂类

public class BeanFactory {
    public static ItemsService createService() {
        // 目标类
        final ItemsService itemsService = new ItemsServiceImpl();
        // 切面类
        final AdviceClass advice = new AdviceClass();
        /**
         * 调用Proxy.newProxyInstance((ClassLoader loader, 类<?>[] interfaces, InvocationHandler h))生成我们的代理类
         * 参数:
         * loader  类加载器,动态代理类运行时创建,任何类的运行都需要类加载器
         * interfaces  目标类的接口,jdk代理需要接口才可以实现
         * h      处理类,接口,必须实现的,每一次执行一次方法,通过代理调用一次invoke
         *          (Object proxy, Method method, Object[] args)
         *          ivoke参数:
         *              proxy:代理对象类
         *              method:调用的方法
         *              args:调用方法的实现类   
         */
        ItemsService itemsServiceProxy = (ItemsService) Proxy.newProxyInstance(BeanFactory.class.getClassLoader(),
                itemsService.getClass().getInterfaces(), new InvocationHandler() {
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        advice.advice1();
                        Object obj = method.invoke(itemsService, args);
                        advice.advice2();
                        advice.advice3();
                        return obj;
                    }
                });
        return itemsServiceProxy;
​
    }
}

 

 

CGlib代理

和JDK动态代理不同,CGlib代理不需要实现接口,但是需要一个父接口,但我们都知道,Java默认继承Object类

这里只对BeanFactory做了更改

BeanFactory

public class BeanFactory {
    public static ItemsService createService() {
        // 目标类
        final ItemsServiceImpl itemsService = new ItemsServiceImpl();
        // 切面类
        final AdviceClass advice = new AdviceClass();
        // .代理类 ,采用cglib,底层创建目标类的子类
        Enhancer enhancer = new Enhancer();
        //确定父类
        enhancer.setSuperclass(itemsService.getClass());
        /* 设置回调函数 , MethodInterceptor接口 等效 jdk InvocationHandler接口
         *  intercept() 等效 jdk  invoke()
         *      参数1、参数2、参数3:以invoke一样
         *      参数4:methodProxy 方法的代理
         */
        enhancer.setCallback(new MethodInterceptor() {
            
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            
                advice.advice1();
                advice.advice2();
                
                //执行目标类的方法
                Object obj = method.invoke(itemsService, args);
                // * 执行代理类的父类 ,执行目标类 (目标类和代理类 父子关系)
                methodProxy.invokeSuper(proxy, args);
                
                //后
                advice.advice3();
                
                return obj;
                
            }
        });     
        //3.4 创建代理
        ItemsServiceImpl proxService = (ItemsServiceImpl) enhancer.create();
                
                return proxService;
        
        
​
    }
}
​

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值