Spring框架-AOP(动态代理)


参照: https://blog.csdn.net/qq_22583741/article/details/79589910

项目:E:\myProject\SpringTest

讲的比较清楚

AOP

解耦和,将业务代码与非业务代码分离

Aspect切面

1.定义Aspect

1概述:

结构

Advice类型

AOP术语

Spring中的AOP

Pointcut表达式

Pointcut示例

2演示(springTest)

idea创建spring+webapp工程

2.1手动方式(动态代理,不涉及配置文件)

让jdk动态代理 对“装饰者” 设计模式简化。前提是:必须有接口

1.目标类:接口+实现类

接口类:userService.java文件
package com.java.dao;
//目标类,包括接口和其实现类
public interface UserService {
    public void addUser();
    public void updateUser();
    public void deleteUser();
}
实现类:userServiceImp.java文件
package com.java.dao.impl;

import com.java.dao.UserService;

public class UserServiceImpl implements UserService {
    @Override
    public void addUser() {
        System.out.println("addUser");
    }

    @Override
    public void updateUser() {
        System.out.println("updateUser");
    }

    @Override
    public void deleteUser() {
        System.out.println("deleteUser");
    }
}

2.切面类

用于存通知

切面类:MyAspect.java
package com.java.dao;

//切面类:用于存放通知。MyAspect
public class MyAspect  {
    public void before(){
        System.out.println("before");
    }

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

}

3.工厂类

在工厂类中生成代理

工厂类:MyBeanFactory.java
package com.java.dao;

import com.java.dao.impl.UserServiceImpl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//工厂类,生成代理
public class MyBeanFactory {
    public static UserService createService(){
        //1.目标类
        final UserService userService = new UserServiceImpl();
        //2.切面类
        final MyAspect myAspect = new MyAspect();
        //3.代理类
        /* 3 代理类:将目标类(切入点)和 切面类(通知) 结合 --> 切面
         *  Proxy.newProxyInstance
         *      参数1:loader ,类加载器,动态代理类 运行时创建,任何类都需要类加载器将其加载到内存。
         *          一般情况:当前类.class.getClassLoader();
         *                  目标类实例.getClass().get...
         *      参数2:Class[] interfaces 代理类需要实现的所有接口
         *          方式1:目标类实例.getClass().getInterfaces()  ;注意:只能获得自己接口,不能获得父元素接口
         *          方式2:new Class[]{UserService.class}
         *          例如:jdbc 驱动  --> DriverManager  获得接口 Connection
         *      参数3:InvocationHandler  处理类,接口,必须进行实现类,一般采用匿名内部
         *          提供 invoke 方法,代理类的每一个方法执行时,都将调用一次invoke
         *              参数31:Object proxy :代理对象
         *              参数32:Method method : 代理对象当前执行的方法的描述对象(反射)
         *                  执行方法名:method.getName()
         *                  执行方法:method.invoke(对象,实际参数)
         *              参数33:Object[] args :方法实际参数
         *
         */
        UserService proxService = (UserService) Proxy.newProxyInstance(MyBeanFactory.class.getClassLoader(), userService.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //前执行
                myAspect.before();

                //执行目标类的方法
                Object obj = method.invoke(userService,args);

                //后执行
                myAspect.after();

                return obj;
            }
        });
        return proxService;
    }
}

4.测试

package com.java.dao.test;

import com.java.dao.MyBeanFactory;
import com.java.dao.impl.UserServiceImpl;

//测试类
public class Test1 {
    public static void main(String[] args){
        UserService userService = MyBeanFactory.createService();
        userService.addUser();
        userService.updateUser();
        userService.deleteUser();

    }
}

2.1.2cglib字节码增强

1.没有接口,只有实现类

2.采用字节码增强框架 cglib,在运行时 创建目标类的子类,从而对目标类进行增强。

工厂类:MyBeanFactory2.java
package com.java.dao;
//工厂类2
//cglib字节码增强,实现动态代理

import com.java.dao.impl.UserServiceImpl;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class MyBeanFactory2 {
    public static UserServiceImpl createService(){
        //1.目标类
        final UserServiceImpl userService = new UserServiceImpl();
        //2.切面类
        final MyAspect myAspect = new MyAspect();
        //3.代理类,采用cglib,底层创建目标类的子类
        //3.1 核心类
        Enhancer enhancer = new Enhancer();
        //3.2确定父类
        enhancer.setSuperclass(userService.getClass());
        /* 3.3 设置回调函数 , 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 {
                //前
                myAspect.before();

                //执行目标类的方法
                Object obj = method.invoke(userService,args);

                //*执行代理类的父类,执行目标类(目标类和代理类 父子关系)
                //效果是执行了两遍目标类的方法
                methodProxy.invokeSuper(proxy,args);

                //后
                myAspect.after();
                return obj;
            }
        });
        //3.4创建代理
        UserServiceImpl proxService = (UserServiceImpl)enhancer.create();
        return proxService;
    }
}
测试
package com.java.dao.test;

import com.java.dao.MyBeanFactory2;
import com.java.dao.impl.UserServiceImpl;

//测试类
public class Test1 {
    public static void main(String[] args){
        UserServiceImpl userService2 = MyBeanFactory2.createService();
        userService2.addUser();
        userService2.updateUser();
        userService2.deleteUser();
    }
}

2.2半自动(涉及配置文件)

让spring创建代理对象,从spring容器中手动获取代理对象

2.2.1目标类

目标类:UserService.java
package com.java.dao;
//目标类,包括接口和其实现类
public interface UserService {
    public void addUser();
    public void updateUser();
    public void deleteUser();
}

2.2.2切面类

切面类:MyAspectHalfAuto.java
package com.java.dao.impl;
//半自动切面类
/*
切面类中确定通知,需要实现不同接口,接口就是防范,从而就确定方法名称。
采用“环绕通知” MethodInterceptor(org.aopalliance.intercept包中的)
 */

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

public class MyAspectHalfAuto implements MethodInterceptor {


    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        //前运行
        System.out.println("前3");
        //手动执行目标方法
        Object obj = methodInvocation.proceed();
        //后运行
        System.out.println("后3");
        return obj;
    }

}

2.2.3Spring配置文件

配置文件:application-context.xml
<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.java.dao"/>
    <!--1.创建目标类-->
    <bean id="userService" class="com.java.dao.impl.UserServiceImpl"></bean>
    <!--2.创建切面类-->
    <bean id="myAspect" class="com.java.dao.impl.MyAspectHalfAuto"></bean>

    <!--创建代理类
     * 使用工厂bean FactoryBean ,底层调用 getObject() 返回特殊bean
        * ProxyFactoryBean 用于创建代理工厂bean,生成特殊代理对象
            interfaces : 确定接口们
                通过<array>可以设置多个值
                只有一个值时,value=""
            target : 确定目标类
            interceptorNames : 通知 切面类的名称,类型String[],如果设置一个值 value=""
            optimize :强制使用cglib
                <property name="optimize" value="true"></property>
        底层机制
            如果目标类有接口,采用jdk动态代理
            如果没有接口,采用cglib 字节码增强
            如果声明 optimize = true ,无论是否有接口,都采用cglib
    -->
    <bean id="proxyServiceId" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="interfaces" value="com.java.dao.UserService"/>
        <property name="target" ref="userService"/>
        <property name="interceptorNames" value="myAspect"/>
    </bean>
</beans>

2.2.4测试

package com.java.dao.test;

import com.java.dao.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

//半自动测试类
public class HalfAutoTest {
    public static void main(String[] args){
        String xmlPath = "com/resources/application-context.xml";
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
        UserService userService = (UserService) applicationContext.getBean("proxyServiceId");
        userService.addUser();
        userService.updateUser();
        userService.deleteUser();
    }
}

2.3全自动

1.从spring容器获得目标类,如果配置aop,spring将自动生成代理。

2.使用的目标类和切面类与半自动一样。

3.在工程lib中加入aspectjweaver-1.8.7.jar,并添加如libraries中

2.3.1Spring配置文件

aop配置文件:spring-aop.xml
<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--1.创建目标类-->
    <bean id="userService" class="com.java.dao.impl.UserServiceImpl"></bean>
    <!--2.创建切面类(通知)-->
    <bean id="myAspect" class="com.java.dao.impl.MyAspectHalfAuto"></bean>
    <!--
    3.aop编程
    3.1 导入命名空间
    3.2 使用 <aop:config>进行配置
    proxy-target-class="true" 声明时使用cglib代理
    <aop:pointcut> 切入点 ,从目标对象获得具体方法
    <aop:advisor> 特殊的切面,只有一个通知 和 一个切入点
    advice-ref 通知引用
    pointcut-ref 切入点引用
    3.3 切入点(PointCut)表达式
    execution(* com.itheima.c_spring_aop.*.*(..))
    选择方法         返回值任意   包             类名任意   方法名任意   参数任意
  -->
    <aop:config proxy-target-class="true">
        <aop:pointcut id="myPointCut" expression="execution(* com.java.dao.*.*(..))"></aop:pointcut>
        <aop:advisor advice-ref="myAspect" pointcut-ref="myPointCut"></aop:advisor>
    </aop:config>
</beans>

2.3.2测试

package com.java.dao.test;

import com.java.dao.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

//全自动测试
public class AopTest {
    public static void main(String[] args){
        String xmlPath = "com/resources/spring-aop.xml";
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.addUser();
        userService.updateUser();
        userService.deleteUser();
    }
}

2.3.3注解实现aop

参考:https://www.cnblogs.com/caijh/p/7154691.html

注解方式实现aop主要分为如下几个步骤

​ 1.在切面类(为切点服务的类)前用@Aspect注释修饰,声明为一个切面类。

2.用@Pointcut注释声明一个切点,目的是为了告诉切面,谁是它的服务对象。(此注释修饰的方法的方法体为空,不需要写功能比如 public void say(){};就可以了,方法名可以被候命的具体服务功能所以引用,它可以被理解为切点对象的一个代理对象方法)

3.在对应的方法前用对应的通知类型注释修饰,将对应的方法声明称一个切面功能,为了切点而服务

4.在spring配置文件中开启aop注释自动代理。如:aop:aspectj-autoproxy/

这样讲可能还是很抽象,那么,废话不多说,我们代码说话,代码如下:

目标类:UserServiceImpl
package com.java.dao.impl;

import com.java.dao.UserService;
import org.springframework.stereotype.Component;

@Component("userService")
public class UserServiceImpl implements UserService {
    @Override
    public void addUser() {
        System.out.println("addUser");
    }

    @Override
    public void updateUser() {
        System.out.println("updateUser");
    }

    @Override
    public void deleteUser() {
        System.out.println("deleteUser");
    }
}
切面类:MyAspectAnnotationDemo.java
package com.java.dao.impl;

//切面类

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
注解方式声明aop
 1.用@Aspect注解将类声明为切面
 (如果用@Component("")注解注释为一个bean对象,那么就要在spring配置文件中开启注解扫描,
 <context:component-scan base-package="com.java.dao"/>
 否则要在spring配置文件中声明一个bean对象)
 2.在切面需要实现相应方法的前面加上相应的注释,也就是通知类型。
 3.此处有环绕通知,环绕通知方法一定要有ProceedingJoinPoint类型的参数传入,然后执行对应的proceed()方法,环绕才能实现。
 */
@Component("myAspectAnnotation")
@Aspect
public class MyAspectAnnotationDemo {

    //定义切入点
    @Pointcut("execution(* com.java.dao.*.*(..))")
    public void pointcutFunc(){}
    /**
     30      * 前置通知(注解中的pointcutFunc()方法,其实就是上面定义pointcut切点注解所修饰的方法名,那只是个代理对象,不需要写具体方法,
     31      * 相当于xml声明切面的id名,如下,相当于id="embark",用于供其他通知类型引用)
     32      * <aop:config>
     33         <aop:aspect ref="mistrel">
     34             <!-- 定义切点 -->
     35             <aop:pointcut expression="execution(* com.java.dao.*.*(..))" id="embark"/>
     36             <!-- 声明前置通知 (在切点方法被执行前调用) -->
     37             <aop:before method="beforSay" pointcut-ref="embark"/>
     38             <!-- 声明后置通知 (在切点方法被执行后调用) -->
     39             <aop:after method="afterSay" pointcut-ref="embark"/>
     40         </aop:aspect>
     41        </aop:config>
     42      */
    
    @Before("pointcutFunc()")
    public void before(){
        System.out.println("before");
    }

    @After("pointcutFunc()")
    public void after(){
        System.out.println("after");
    }

    //环绕通知。注意要有ProceedingJoinPoint参数传入。
    @Around("pointcutFunc()")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕前");
        proceedingJoinPoint.proceed();//执行方法
        System.out.println("环绕后");
    }

}
配置类:spring-aop.xml
<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!--*******************************下面是启用注解实现aop配置*********************-->
    <context:component-scan base-package="com.java.dao"/>
    <aop:aspectj-autoproxy/>
</beans>

测试类:AnnotationTest.java

package com.java.dao.test;
//注解测试类

import com.java.dao.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AnnotationTest {
    public static void main(String[] args) {
//        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("com/resources/spring-aop.xml");
//        context.start();
        ApplicationContext context = new ClassPathXmlApplicationContext("com/resources/spring-aop.xml");//与上面的效果一样

        UserService userService = (UserService) context.getBean("userService");
        userService.addUser();
        userService.updateUser();
        userService.deleteUser();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值