栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SSM框架学习笔记之Spring的AOP

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SSM框架学习笔记之Spring的AOP

Spring的AOP简介

1.什么是AOP?
  AOP是AspectOrientedProgramming的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
  AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发效率。

2.AOP的作用及其优势

(1)作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强
(2)优势:减少重复代码,提高开发效率,并且便于维护。

3.AOP的底层实现
  实际上,AOP的底层是通过Spring提供的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态地生成代理对象,代理对象方法执行时进行增强功能的介入,再去调用目标方法,从而完成功能的增强。

4.AOP的动态代理技术

常用的动态代理技术:
(1)JDK代理:基于接口的动态代理技术
  JDKProxy动态代理是针对对象做代理,要求原始对象具有接口实现,并对接口方法进行增强,在aop动态代理的时候发现这个类是实现接口的类,就选择JDK的动态代理,从ioc中获取这个动态代理的实现类就需要按照接口.class去获取。

(2)cglib代理:基于父类的动态代理技术

  • CGLIB(Code Generation Library),Code生成类库
  • CGLIB动态代理不限定是否具有接口,可以对任意操作进行增强
  • CGLIB动态代理无需要原始被代理对象,动态创建出新的代理对象

    5.模拟AOP动态代理
    JDK代理
代码示例:
//Target接口

public interface TargetInterface {

    public void save();
}

代码示例:
//实现接口

public class TargetImpl implements TargetInterface {
    @Override
    public void save() {
        System.out.println("save方法执行------");
    }
}

代码示例:
//Advice.java

public class Advice {

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

    public void afterRunning(){
        System.out.println("后置增强------");
    }
}

代码示例:
//动态代理生成类
public class ProxyTest {

    public static void main(String[] args) {

        //创建目标对象
        final TargetImpl target = new TargetImpl();

        //增强对象
        Advice advice = new Advice();
        //返回值就是动态生成的代理对象
        TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(
                target.getClass().getClassLoader(), //目标对象的类加载器
                target.getClass().getInterfaces(),//目标对象相同的接口字节码对象数组
                new InvocationHandler() {
                    //调用代理对象的任何方法,实质执行的都是invoke方法
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //前置增强
                        advice.before();
                        //执行目标方法
                        Object invoke = method.invoke(target, args);
                        //后置增强
                        advice.afterRunning();
                        return invoke;
                         }
                    }
                );

        //调用代理对象的方法
        proxy.save();
    }
}

测试结果:

cglib代理

代码示例:
public class ProxyTest {
    public static void main(String[] args) {
        //创建目标对象
        final com.xc.proxy.jdk.impl.TargetImpl target = new TargetImpl();

        //增强对象
        com.xc.proxy.jdk.Advice advice = new Advice();
        //返回值就是动态生成的代理对象,基于cglib
        //1、创建增强器
        Enhancer enhancer = new Enhancer();
        //2、设置父类(目标)
        enhancer.setSuperclass(TargetImpl.class);
        //3、设置回调
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                //执行前置
                advice.before();
                //执行目标
                method.invoke(target,args);
                //执行后置
                advice.afterRunning();
                return null;
            }
        });
        //4、创建代理对象
       TargetImpl proxy = (TargetImpl) enhancer.create();
       proxy.save();
    }
}

测试截图:

6.AOP的相关概念
  Spring的AOP实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。

  AOP的常用术语:

  • Target(目标对象):代理的目标对象
  • Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
  • Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点是指方法,因为spring只支持方法类型的连接点
  • Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义
  • Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知
  • Aspect(切面):是切入点和通知(引介)的结合
  • Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译器织入和类装载期织入

7.开发明确的事项

(1)需要编写的内容

  • 编写核心业务代码(目标类的目标方法)
  • 编写切面类,切面类中有通知(增强功能方法)
  • 在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合

(2)AOP技术实现的内容
  Spring框架监控切入点方法的执行,一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。

(3)AOP底层使用哪种代理方式?
  在spring中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式

基于XML的AOP开发

1.开发步骤

(1)导入AOP相关坐标
(2)创建目标接口和目标类(内部有接口)
(3)创建切面类(内部有增强方法)
(4)将目标类和切面类的对象创建权交给spring
(5)在applicationContext.xml中配置织入关系
(6)测试代码

代码示例:
//(1)pom.xml导入AOP相关坐标

        
            org.springframework
            spring-context
            5.3.10
        
        
            org.aspectj
            aspectjweaver
            1.8.4
        
代码示例:
//(2)创建目标接口和目标类(内部有接口)

//创建目标接口TargetInterface.java
public interface TargetInterface {

    public void save();
}

//创建目标类Target
public class Target implements TargetInterface {
    @Override
    public void save() {
        System.out.println("save方法执行------");
    }
}

代码示例:
//(3)创建切面类MyAsoect(内部有增强方法)

public class MyAsoect {

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

代码示例:
//(4)在applicationContext.xml中将目标类和切面类的对象创建权交给spring


    

    
    

代码示例:
//(5)在applicationContext.xml中配置织入关系

    
    
    
        
            
            
        
    
代码示例:
(6)测试代码

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {

    @Autowired
   private TargetInterface target;

    @Test
    public void test1(){
        target.save();
    }
}

测试截图:

2.XML配置AOP详解
(1)切面表达式的写法

表达式语法:
execution([修饰符] 返回值类型 包名.类名.方法名(参数))

  • 访问修饰符可以省略
  • 返回值类型、包名、类名、方法名可以使用星号*代表任意
  • 包名与类名之间一个.代表当前包下的类,两个点…表示当前包及其子包下的类
  • 参数列表可以使用两个点…表示任意个数,任意类型的参数列表

(2)通知的类型

通知的配置语法:

代码示例:
//MyAspect.java

public class MyAspect {

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

    public void afterRunning(){
        System.out.println("后置增强------");
    }

    //ProceedingJoinPoint:正在执行的连接点叫切点
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕前增强------");
        //切点方法
        Object proceed = pjp.proceed();
        System.out.println("环绕后增强------");
        return proceed;
    }

    public void afterThrowing(){
        System.out.println("异常抛出增强------");
    }

    public void after(){
        System.out.println("最终增强------");
    }

}

代码示例:
//applicationContext.xml

    
    

    
    

    
    
    
        
            
            
            
            
            
            
        
    

测试截图:
异常抛出前

异常抛出后

(3)切点表达式的抽取
  当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用pointcut-ref属性代替pointcut属性来引用抽取后的切点表达式

代码示例:
//applicationContext.xml
 
    
        
            
            
            
            
            
        
    

测试截图:

基于注解的AOP开发

1.开发步骤
(1)创建目标接口和目标类(内部有接口)
(2)创建切面类(内部有增强方法)
(3)将目标类和切面类的对象创建权交给spring
(4)在切面类中使用注解配置织入关系
(5)在配置文件中开启组件扫描和AOP的自动代理
(6)测试代码

代码示例:
//(1)创建目标接口和目标类(内部有接口)

//TargetInterface.java
public interface TargetInterface {

    public void save();
}

//Target.java
public class Target implements TargetInterface {
    @Override
    public void save() {
        System.out.println("save方法执行------");
    }
}


代码示例:
(2)创建切面类(内部有增强方法)

//MyAspect .java
public class MyAspect {

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

    public void afterRunning(){
        System.out.println("后置增强------");
    }

    //ProceedingJoinPoint:正在执行的连接点叫切点
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕前增强------");
        //切点方法
        Object proceed = pjp.proceed();
        System.out.println("环绕后增强------");
        return proceed;
    }

    public void afterThrowing(){
        System.out.println("异常抛出增强------");
    }

    public void after(){
        System.out.println("最终增强------");
    }

代码示例:
(3)将目标类和切面类的对象创建权交给spring
@Component("myAspect")
public class MyAspect {
代码示例:
//(4)在切面类中使用注解配置织入关系
@Aspect//代表标注当前MyAspect是一个切面类
public class MyAspect {

代码示例:
//(5)在applicationContext-anno.xml配置文件中开启组件扫描和AOP的自动代理

    
    

    
    
代码示例:
//AnnoTest.java

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-anno.xml")
public class AnnoTest {

    @Autowired
   private TargetInterface target;

    @Test
    public void test1(){
        target.save();
    }
}

测试截图:

2.注解配置AOP详解
(1)注解通知的类型

通知配置的语法:@通知注解(“切点表达式”)


(2)切点表达式的抽取
  同xml配置aop一样,我们可以将切点表达式抽取。抽取方法是在切面内定义方法,在该方法上使用@Pointcut注解定义切点表达式,然后在增强注解中进行引用。

代码示例:
//AnnoTest.java

@Component("myAspect")
@Aspect//代表标注当前MyAspect是一个切面类
public class MyAspect {

    //定义一个切点表达式
    @Pointcut("execution(* com.xc.anno.*.*(..))")
    public void pointcut(){}
    
    //配置前置增强
    @Before("pointcut()")
    public void before(){
        System.out.println("前置增强------");
    }
    
    @AfterReturning("MyAspect.pointcut()")//这种写法也可以
    public void afterRunning(){
        System.out.println("后置增强------");
    }
    
    @Around("pointcut()")
    //ProceedingJoinPoint:正在执行的连接点叫切点
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕前增强------");
        //切点方法
        Object proceed = pjp.proceed();
        System.out.println("环绕后增强------");
        return proceed;
    }

    public void afterThrowing(){
        System.out.println("异常抛出增强------");
    }

    @After("pointcut()")
    public void after(){
        System.out.println("最终增强------");
    }
    
}

测试截图:

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/351723.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号