美文网首页
spring aop

spring aop

作者: 寂静的春天1988 | 来源:发表于2020-09-04 12:12 被阅读0次

    aop的专业术语

    Target(目标类):需要被代理的类
    joinpoint(连接点):是指那些可能会被拦截的方法。
    poinCut(切入点):已经被增强的连接点。
    advice(通知/增强):增强的代码
    weaving(织入):把增强advice应用到目标对象,创建新的代理对象的过程。
    proxy:代理类
    aspect(切面):切入点和通知的接口

    使用jdk动态代理实现

    public interface UserService {
        
        public void addUser();
        public void updateUser();
        public void deleteUser();
    
    }
    
    public class UserServiceImpl implements UserService {
    
        @Override
        public void addUser() {
            System.out.println("a_proxy.a_jdk addUser");
        }
    
        @Override
        public void updateUser() {
            System.out.println("a_proxy.a_jdk updateUser");
    
        }
    
        @Override
        public void deleteUser() {
    
            System.out.println("a_proxy.a_jdk deleteUser");
        }
    
    }
    
    
    public class MyAspect {
        
        public void before(){
            System.out.println("前置");
        }
        
        public void after(){
            System.out.println("后置");
        }
    
    }
    
    public class MyBeanFactory {
        
        public static UserService createService(){
            //1 目标类
            final UserService userService = new UserServiceImpl();
            //2切面类
            final MyAspect myAspect = new MyAspect();
            /* 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;
        }
    
    }
    
    
    
    public class TestJDK {
        
        @Test
        public void demo01(){
            UserService userService = MyBeanFactory.createService();
            userService.addUser();
            userService.updateUser();
            userService.deleteUser();
        }
    
    }
    
    

    cglib实现

    public class UserServiceImpl {
    
        public void addUser() {
            System.out.println("a_proxy.b_cglib addUser");
        }
    
        public void updateUser() {
            System.out.println("a_proxy.b_cglib updateUser");
    
        }
    
        public void deleteUser() {
    
            System.out.println("a_proxy.b_cglib deleteUser");
        }
    
    }
    
    public class MyAspect {
        
        public void before(){
            System.out.println("鸡首2");
        }
        
        public void after(){
            System.out.println("牛后2");
        }
    
    }
    
    
    public class MyBeanFactory {
        
        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;
        }
    
    }
    
    
    
    public class TestCglib {
        
        @Test
        public void demo01(){
            UserServiceImpl userService = MyBeanFactory.createService();
            userService.addUser();
            userService.updateUser();
            userService.deleteUser();
        }
    
    }
    

    spring工厂bean代理--半自动

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

    public interface UserService {
        
        public void addUser();
        public void updateUser();
        public void deleteUser();
    
    }
    
    public class UserServiceImpl implements UserService {
    
        @Override
        public void addUser() {
            System.out.println("b_factory_bean addUser");
        }
    
        @Override
        public void updateUser() {
            System.out.println("b_factory_bean updateUser");
    
        }
    
        @Override
        public void deleteUser() {
    
            System.out.println("b_factory_bean deleteUser");
        }
    
    }
    
    /**
     * 切面类中确定通知,需要实现不同接口,接口就是规范,从而就确定方法名称。
     * * 采用“环绕通知” MethodInterceptor
     *
     */
    public class MyAspect implements MethodInterceptor {
    
        @Override
        public Object invoke(MethodInvocation mi) throws Throwable {
            
            System.out.println("前3");
            
            //手动执行目标方法
            Object obj = mi.proceed();
            
            System.out.println("后3");
            return obj;
        }
    }
    

    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"
           xsi:schemaLocation="http://www.springframework.org/schema/beans 
                               http://www.springframework.org/schema/beans/spring-beans.xsd">
        <!-- 1 创建目标类 -->
        <bean id="userServiceId" class="com.itheima.b_factory_bean.UserServiceImpl"></bean>
        <!-- 2 创建切面类 -->
        <bean id="myAspectId" class="com.itheima.b_factory_bean.MyAspect"></bean>
    
        <!-- 3 创建代理类 
            * 使用工厂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.itheima.b_factory_bean.UserService"></property>
            <property name="target" ref="userServiceId"></property>
            <property name="interceptorNames" value="myAspectId"></property>
        </bean>
    </beans>
    
    public class TestFactoryBean {
        
        @Test
        public void demo01(){
            String xmlPath = "com/itheima/b_factory_bean/beans.xml";
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
            
            //获得代理类
            UserService userService = (UserService) applicationContext.getBean("proxyServiceId");
            userService.addUser();
            userService.updateUser();
            userService.deleteUser();
        }
    
    }
    

    spring aop编程

    上面实现了aop的半自动编程,用ProxyFactoryBean让spring来生成代理类。
    但是在发现还是需要手动的获取代理类。
    进一步进化。
    spring利用 implements BeanPostProcessor,在类初始化的时候创建它的代理对象并返回,这样spring容器中存的实际上不是这个类,而是它的代理类,从而实现了aop。

    public interface UserService {
        
        public void addUser();
        public void updateUser();
        public void deleteUser();
    
    }
    
    
    public class UserServiceImpl implements UserService {
    
        @Override
        public void addUser() {
            System.out.println("c_spring_aop addUser");
        }
    
        @Override
        public void updateUser() {
            System.out.println("c_spring_aop updateUser");
    
        }
    
        @Override
        public void deleteUser() {
    
            System.out.println("c_spring_aop deleteUser");
        }
    
    }
    
    /**
     * 切面类中确定通知,需要实现不同接口,接口就是规范,从而就确定方法名称。
     * * 采用“环绕通知” MethodInterceptor
     *
     */
    public class MyAspect implements MethodInterceptor {
    
        @Override
        public Object invoke(MethodInvocation mi) throws Throwable {
            
            System.out.println("前4");
            
            //手动执行目标方法
            Object obj = mi.proceed();
            
            System.out.println("后4");
            return obj;
        }
    }
    
    
    <?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="userServiceId" class="com.itheima.c_spring_aop.UserServiceImpl"></bean>
        <!-- 2 创建切面类(通知) -->
        <bean id="myAspectId" class="com.itheima.c_spring_aop.MyAspect"></bean>
        <!-- 3 aop编程 
            3.1 导入命名空间
            3.2 使用 <aop:config>进行配置
                    proxy-target-class="true" 声明时使用cglib代理
                <aop:pointcut> 切入点 ,从目标对象获得具体方法
                <aop:advisor> 特殊的切面,只有一个通知 和 一个切入点
                    advice-ref 通知引用
                    pointcut-ref 切入点引用
            3.3 切入点表达式
                execution(* com.itheima.c_spring_aop.*.*(..))
                选择方法         返回值任意   包                                             类名任意   方法名任意   参数任意
            
        -->
        <aop:config proxy-target-class="true">
            <aop:pointcut expression="execution(* com.itheima.c_spring_aop.*.*(..))" id="myPointCut"/>
            <aop:advisor advice-ref="myAspectId" pointcut-ref="myPointCut"/>
        </aop:config>
    </beans>
    
    public class TestSpringAOP {
        
        @Test
        public void demo01(){
            String xmlPath = "com/itheima/c_spring_aop/beans.xml";
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
            
            //获得目标类
            UserService userService = (UserService) applicationContext.getBean("userServiceId");
            userService.addUser();
            userService.updateUser();
            userService.deleteUser();
        }
    
    }
    

    最终版

    /**
     * 切面类,含有多个通知
     */
    public class MyAspect {
        
        public void myBefore(JoinPoint joinPoint){
            System.out.println("前置通知 : " + joinPoint.getSignature().getName());
        }
        
        public void myAfterReturning(JoinPoint joinPoint,Object ret){
            System.out.println("后置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret);
        }
        
        public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
            System.out.println("前");
            //手动执行目标方法
            Object obj = joinPoint.proceed();
            
            System.out.println("后");
            return obj;
        }
        
        public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
            System.out.println("抛出异常通知 : " + e.getMessage());
        }
        
        public void myAfter(JoinPoint joinPoint){
            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: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="userServiceId" class="com.itheima.d_aspect.a_xml.UserServiceImpl"></bean>
        <!-- 2 创建切面类(通知) -->
        <bean id="myAspectId" class="com.itheima.d_aspect.a_xml.MyAspect"></bean>
        <!-- 3 aop编程 
            <aop:aspect> 将切面类 声明“切面”,从而获得通知(方法)
                ref 切面类引用
            <aop:pointcut> 声明一个切入点,所有的通知都可以使用。
                expression 切入点表达式
                id 名称,用于其它通知引用
        -->
        <aop:config>
            <aop:aspect ref="myAspectId">
                <aop:pointcut expression="execution(* com.itheima.d_aspect.a_xml.UserServiceImpl.*(..))" id="myPointCut"/>
                
                <!-- 3.1 前置通知 
                    <aop:before method="" pointcut="" pointcut-ref=""/>
                        method : 通知,及方法名
                        pointcut :切入点表达式,此表达式只能当前通知使用。
                        pointcut-ref : 切入点引用,可以与其他通知共享切入点。
                    通知方法格式:public void myBefore(JoinPoint joinPoint){
                        参数1:org.aspectj.lang.JoinPoint  用于描述连接点(目标方法),获得目标方法名等
                    例如:
                <aop:before method="myBefore" pointcut-ref="myPointCut"/>
                -->
                
                <!-- 3.2后置通知  ,目标方法后执行,获得返回值
                    <aop:after-returning method="" pointcut-ref="" returning=""/>
                        returning 通知方法第二个参数的名称
                    通知方法格式:public void myAfterReturning(JoinPoint joinPoint,Object ret){
                        参数1:连接点描述
                        参数2:类型Object,参数名 returning="ret" 配置的
                    例如:
                <aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret" />
                -->
                
                <!-- 3.3 环绕通知 
                    <aop:around method="" pointcut-ref=""/>
                    通知方法格式:public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
                        返回值类型:Object
                        方法名:任意
                        参数:org.aspectj.lang.ProceedingJoinPoint
                        抛出异常
                    执行目标方法:Object obj = joinPoint.proceed();
                    例如:
                <aop:around method="myAround" pointcut-ref="myPointCut"/>
                -->
                <!-- 3.4 抛出异常
                    <aop:after-throwing method="" pointcut-ref="" throwing=""/>
                        throwing :通知方法的第二个参数名称
                    通知方法格式:public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
                        参数1:连接点描述对象
                        参数2:获得异常信息,类型Throwable ,参数名由throwing="e" 配置
                    例如:
                <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/>
                -->
                <!-- 3.5 最终通知 -->           
                <aop:after method="myAfter" pointcut-ref="myPointCut"/>
                
                
                
            </aop:aspect>
        </aop:config>
    </beans>
    

    注解版

    <?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"
           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">
        
        <!-- 1.扫描 注解类 -->
        <context:component-scan base-package="com.itheima.d_aspect.b_anno"></context:component-scan>
        
        <!-- 2.确定 aop注解生效 -->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    
    </beans>
    
    /**
     * 切面类,含有多个通知
     */
    @Component
    @Aspect
    public class MyAspect {
        
        //切入点当前有效
    //  @Before("execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))")
        public void myBefore(JoinPoint joinPoint){
            System.out.println("前置通知 : " + joinPoint.getSignature().getName());
        }
        
        //声明公共切入点
        @Pointcut("execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))")
        private void myPointCut(){
        }
        
    //  @AfterReturning(value="myPointCut()" ,returning="ret")
        public void myAfterReturning(JoinPoint joinPoint,Object ret){
            System.out.println("后置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret);
        }
        
    //  @Around(value = "myPointCut()")
        public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
            System.out.println("前");
            //手动执行目标方法
            Object obj = joinPoint.proceed();
            
            System.out.println("后");
            return obj;
        }
        
    //  @AfterThrowing(value="execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))" ,throwing="e")
        public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
            System.out.println("抛出异常通知 : " + e.getMessage());
        }
        
        @After("myPointCut()")
        public void myAfter(JoinPoint joinPoint){
            System.out.println("最终通知");
        }
    
    }
    
    

    相关文章

      网友评论

          本文标题:spring aop

          本文链接:https://www.haomeiwen.com/subject/wptosktx.html