Spring学习之AOP

Posted 小蘇

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring学习之AOP相关的知识,希望对你有一定的参考价值。

一、操作术语

  连接点(Joinpoint):类里面可以被增强的方法,这些方法称为连接点。

  切入点(Pointcut):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义

  通知/增强(Advice):所谓通知是指拦截到Joinpoint后要做的事情就是通知,通知分为前置通知、后置通知、异常通知、最终通知和环绕通知(切面要完成功能)。

  切面(Aspect):切入点和通知的结合,即把增强应用到具体的方法上面,这个过程称为切面。

  目标对象(Target):代理的目标对象(要增强的类)。

  织入(Weaving):把增强(Advice)应用到目标(Target)的过程。

  代理(Proxy):一个类被aop织入增强后,就产生一个代理结果类

 

二、实现策略

  1、JDK动态代理

  使用动态代理可以为一个或多个接口在运行期动态生成实现对象,生成的对象中实现接口的方法时可以添加增强代码,从而实现AOP。缺点是只能针对接口进行代理,另外由于动态代理是通过反射实现的,有时可能要考虑反射调用的开销。

  2、字节码生成(CGLib 动态代理)

  动态字节码生成技术是指在运行时动态生成指定类的一个子类对象,并覆盖其中特定方法,覆盖方法时可以添加增强代码,从而实现AOP。其常用工具是cglib。

  3、定制的类加载器

  当需要对类的所有对象都添加增强,动态代理和字节码生成本质上都需要动态构造代理对象,即最终被增强的对象是由AOP框架生成,不是开发者new出来的。解决的办法就是实现自定义的类加载器,在一个类被加载时对其进行增强。JBoss就是采用这种方式实现AOP功能。

  4、代码生成

  利用工具在已有代码基础上生成新的代码,其中可以添加任何横切代码来实现AOP。

  5、语言扩展

  可以对构造方法和属性的赋值操作进行增强,AspectJ是采用这种方式实现AOP的一个常见Java语言扩展。

 

三、编程式增强(无配置文件)

  1、定义接口和实现类

package com.sgl.aop;

public interface Person {
    void eat(String name);
}
package com.sgl.aop;

public class Student implements Person{

    @Override
    public void eat(String name) {
        System.out.println("student eat "+name);
    }
}

  2、编写前置增强和后置增强

package com.sgl.aop;

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

public class BeforeAndAfterAdvice implements MethodBeforeAdvice,AfterReturningAdvice{

    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("after");
    }

    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("before");
    }

}

  3、环绕增强

package com.sgl.aop;

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

public class AroundAdvice implements MethodInterceptor{

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        before();
        Object result = invocation.proceed();
        after();
        return result;
    }

    public void before(){
        System.out.println("before");
    }
    
    public void after(){
        System.out.println("after");
    }
}

  4、单元测试

package com.sgl.aop;

import org.junit.Test;
import org.springframework.aop.framework.ProxyFactory;

public class TestAop {
    
    @Test
    public void aopTest(){
        //创建代理工厂
        ProxyFactory factory = new ProxyFactory();
        //添加代理目标对象
        factory.setTarget(new Student());
        //添加通知
        factory.addAdvice(new BeforeAndAfterAdvice());
//        factory.addAdvice(new AroundAdvice());
        //获取代理对象
        Person person = (Person) factory.getProxy();
        person.eat("chicken");
    }
}

四、声明式增强(配置文件)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

    <context:component-scan base-package="com.sgl.aop.xml1" />
    
    <bean id="proxyObject" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="targetName" value="student" />
        <property name="interceptorNames" value="beforeAndAfterAdvice" />
        <property name="proxyInterfaces" value="com.sgl.aop.xml1.Person" />
    </bean>

</beans>

  在代理目标和增强类添加@Component注解,创建bean

  单元测试:

package com.sgl.aop.xml1;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestAop {
    @Test
    public void test1(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Person person = context.getBean("proxyObject",Person.class);
        person.eat("chicken");
    }
}

五、Spring的AspectJ自动代理

  1、基于xml配置文件

package com.sgl.aop.xml;

/**
 * 被代理的目标类
 */
public class Math{
    //
    public int add(int n1,int n2){
        int result=n1+n2;
        System.out.println(n1+"+"+n2+"="+result);
        return result;
    }
    
    //
    public int sub(int n1,int n2){
        int result=n1-n2;
        System.out.println(n1+"-"+n2+"="+result);
        return result;
    }
    
    //
    public int mut(int n1,int n2){
        int result=n1*n2;
        System.out.println(n1+"X"+n2+"="+result);
        return result;
    }
    
    //
    public int div(int n1,int n2){
        int result=n1/n2;
        System.out.println(n1+"/"+n2+"="+result);
        return result;
    }
}
package com.sgl.aop.xml;

public class Advices{

    public void before(){
        System.out.println("----------前置通知----------");
    }

    public void after(){
        System.out.println("----------最终通知----------");
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

    <!-- 被代理对象 -->
    <bean id="math" class="com.sgl.aop.xml.Math" />
    
    <!-- 通知 -->
    <bean id="advice" class="com.sgl.aop.xml.Advices" />
    
    <!-- aop 配置 -->
    <aop:config>
        <!--切面 -->
        <aop:aspect ref="advice">
            <!-- 切点 -->
            <aop:pointcut expression="execution(* com.sgl.aop.xml.*.*(..))" id="pointcut"/>
            <!--连接通知方法与切点 -->
            <aop:before method="before" pointcut-ref="pointcut"/>
            <aop:after method="after" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>

</beans>
package com.sgl.aop.xml;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext1.xml");
        Math math = context.getBean("math",Math.class);
        int n1 = 100, n2 = 5;
        math.add(n1, n2);
        math.sub(n1, n2);
        math.mut(n1, n2);
        math.div(n1, n2);
    }
}

  2、基于注解

package com.sgl.aop.annotion;

import org.springframework.stereotype.Component;

/**
 * 被代理的目标类
 */
@Component
public class Math{
    //
    public int add(int n1,int n2){
        int result=n1+n2;
        System.out.println(n1+"+"+n2+"="+result);
        return result;
    }
    
    //
    public int sub(int n1,int n2){
        int result=n1-n2;
        System.out.println(n1+"-"+n2+"="+result);
        return result;
    }
    
    //
    public int mut(int n1,int n2){
        int result=n1*n2;
        System.out.println(n1+"X"+n2+"="+result);
        return result;
    }
    
    //
    public int div(int n1,int n2){
        int result=n1/n2;
        System.out.println(n1+"/"+n2+"="+result);
        return result;
    }
}
package com.sgl.aop.annotion;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class Advices{

    @Before(value="execution(* com.sgl.aop.annotion.*.add*(..))")
    public void before(JoinPoint joinPoint){
        System.out.println("----------前置通知----------");
        System.out.println(joinPoint.getSignature().getName());
    }

    @After(value="execution(* com.sgl.aop.annotion.*.*(..))")
    public void after(){
        System.out.println("----------最终通知----------");
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

    <context:component-scan base-package="com.sgl.aop.annotion" />

    <!-- 开启aop注解扫描 -->
    <aop:aspectj-autoproxy proxy-target-class="true" />

</beans>
package com.sgl.aop.annotion;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext2.xml");
        Math math = context.getBean("math",Math.class);
        int n1 = 100, n2 = 5;
        math.add(n1, n2);
        math.sub(n1, n2);
        math.mut(n1, n2);
        math.div(n1, n2);
    }
}

  3、定义pointcut,复用切点

package com.sgl.aop.annotion1;

import org.aspectj.lang.JoinPoint;
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
@Aspect
public class Advices{

    //切点
    @Pointcut(value="execution(* com.sgl.aop.annotion1.*.a*(..))")
    public void pointcut(){
        System.out.println("nihao");
    };
    
    @Before(value="pointcut()")
    public void before(JoinPoint joinPoint){
        System.out.println("----------前置通知----------");
        System.out.println(joinPoint.getSignature().getName());
    }

    @After(value="pointcut()")
    public void after(){
        System.out.println("----------最终通知----------");
    }
    
    @Around(value="execution(* com.sgl.aop.annotion1.*.s*(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        System.out.println("----------环绕前置----------");
        Object result = joinPoint.proceed();
        System.out.println("----------环绕后置----------");
        return result;
    }
    
    //返回结果通知
    @AfterReturning(pointcut="execution(* com.sgl.aop.annotion1.*.m*(..))",returning="result")
    public void afterReturning(JoinPoint jp,Object result){
        System.out.println(jp.getSignature().getName());
        System.out.println("结果是:"+result);
        System.out.println("----------返回结果----------");
    }
    
  //异常后通知
    @AfterThrowing(pointcut="execution(* com.sgl.aop.annotion1.*.d*(..))",throwing="exp")
    public void afterThrowing(JoinPoint jp,Exception exp){
        System.out.println(jp.getSignature().getName());
        System.out.println("异常消息:"+exp.getMessage());
        System.out.println("----------异常通知----------");
    }
}
package com.sgl.aop.annotion1;

import org.springframework.stereotype.Component;

/**
 * 被代理的目标类
 */
@Component
public class Math{
    //
    public int add(int n1,int n2){
        int result=n1+n2;
        System.out.println(n1+"+"+n2+"="+result);
        return result;
    }
    
    //
    public int sub(int n1,int n2){
        int result=n1-n2;
        System.out.println(n1+"-"+n2+"="+result);
        return result;
    }
    
    //
    public int mut(int n1,int n2){
        int result=n1*n2;
        System.out.println(n1+"X"+n2+"="+result);
        return result;
    }
    
    //
    public int div(int n1,int n2){
        int result=n1/n2;
        System.out.println(n1+"/"+n2+"="+result);
        return result;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

    <context:component-scan base-package="com.sgl.aop.annotion1" />

    <!-- 开启aop注解扫描 -->
    <aop:aspectj-autoproxy proxy-target-class="true" /><!-- true表示代理目标类,默认为false,代理接口 -->

</beans>
package com.sgl.aop.annotion1;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext3.xml");
        Math math = context.getBean("math",Math.class);
        int n1 = 100, n2 = 5;
        math.add(n1, n2);
        math.sub(n1, n2);
        math.mut(n1, n2);
        math.div(n1, n2);
    }
}

  4、自定义配置文件类来代替applicationContext.xml配置文件

package com.sgl.aop.noxml;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

/**
 * 相当于spring配置文件的类
 * @author [email protected]
 * @date 2018年4月13日
 */
@Configuration/*用于表示当前类为容器的配置类,类似<beans/>*/
@ComponentScan(basePackages={"com.sgl.aop.noxml"})/*扫描的范围,相当于xml的配置<context:component-scan/>*/
@EnableAspectJAutoProxy(proxyTargetClass=true)/*自动代理,相当于<aop:aspectj-autoproxy proxy-target-class="true" />*/
public class ApplicationCfg {
    /**
     * 在配置中声明一个bean,相当于<bean id=getUser class="com.sgl.aop.noxml.User"/>
     * @return
     */
    @Bean
    public User getUser(){
        return new User();
    }
}
package com.sgl.aop.noxml;

public class User {
    public void show(){
        System.out.println("一个用户对象");
    }
}
package com.sgl.aop.noxml;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Test {
    
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationCfg.class);
        Math math = context.getBean("math",Math.class);
        int n1 = 100, n2 = 5;
        math.add(n1, n2);
        math.sub(n1, n2);
        math.mut(n1, n2);
        math.div(n1, n2);
    }
}

 

以上是关于Spring学习之AOP的主要内容,如果未能解决你的问题,请参考以下文章

Spring阶段性学习总结AOP编程入门学习之动态代理实现代码的优化

Spring学习之第一个AOP程序

spring再学习之AOP实操

Spring学习之AOP

spring再学习之AOP准备

Spring源码学习之Aop源码分析