民工心事 发表于 2024-10-25 06:56:54

spring (Aop) day 1024

ok了家人们,继续学习spring ,这段知识点有点绕,建议搭配b站的视频去学,passion!!!
https://i-blog.csdnimg.cn/direct/69fc55ceb92c4ef0abb639db92bec81f.png
八.AOP-面向切面编程

8.1 动态署理

8.1.1 概述

   什么是署理?在现实生活中,署理很常见。比如,我们必要去   某个地方,必要自己开车已往,但是公交车师傅的出现替代了   我们自己开车的这个步骤,我们只必要乘坐公交车就可以到达   我们必要到达的目标地。这就是一种署理方式。      用自己的话总结一下:署理就是原本必要自己去完成的工作可      以由一个新的替代者来完成,这就是署理的一种表现。 在      Java   中就是在不侵入原来代码的情况下完成功能的增强。             Java    中常见署理分类:静态署理,基于    JDK    的动态署理,基于         CGlib    的动态署理             8.1.2 静态署理

         举个例子:现实中明星都是有经纪人的,经纪人的作用就是帮            明星来处置惩罚业务,节目组要找明星来表演,那么肯定不是和明            星直接接洽的,而是明星的经纪人出头和节目组洽谈,而且只            有当业务条件满足时,明星才会出头表演,那么这个经纪人就            是明星的署理人,他可以帮助明星来筛选演出业务,条件达不            到要求的表演就过滤了,只有达到要求了,才会被经纪人接收            然后明星就会出场表演,也就是拦截了外界对明星的直接访            }   问。这也是   JAVA   中署理对象的作用,产生一个署理对象用来            拦截外界对真实业务的访问。                     可以看出,经纪人会将外界给明星的信息进行拦截、处置惩罚,这               就是我们常说的署理模式。                            静态署理的实现方式                        

[*]                   经纪人和明星在一些行为上有共同点,共同的目标,以是                           定义一个共有接口:          start          接口          (          参加节目          )          。
[*]                              明星实现接口的行为,因为真正出力的是明星。
                     

[*]       经纪人要代表明星,就必要和明星有同样的行为,同时持         有明星的引用    (    经纪人能找到明星    )    。
[*]            调用方要找明星之前,先找到经纪人。由经纪人出头进行            谈判,比如参加节目前片酬,比如参加节目后发的微博。       
静态署理的代码实现


[*]接口 
public interface FindWife {
    public void findWife(String message);
}

[*] 署理类
public class BuyHouseProxy implements BuyHouse{
//定义一个 有买房需求的
private BuyHouse buyHouse;
//在创建代理对象的时候 得有委托人
public BuyHouseProxy(BuyHouse buyHouse) {
    this.buyHouse = buyHouse;
}
    @Override
    public void buyHouse() {
    //代理人 帮助 委托人
    System.out.println("我是中介,帮您买房,先付款...");
      buyHouse.buyHouse();//最终 委托人买方法
    System.out.println("将信息偷偷滴卖给装修的人...");
    }
}

[*]署理类
package com.cjx.service;
//买房的中介 代理
public class BuyHouseProxy implements BuyHouse{

    //定义一个 有买房需求的
    private BuyHouse buyHouse;

    public BuyHouseProxy(BuyHouse buyHouse) {
      this.buyHouse = buyHouse;
    }

    @Override
    public void buyHouse() {
      //代理人 帮助 委托人
      System.out.println("我是中介,帮您买房,先付款...");
      buyHouse.buyHouse();//最终 委托人买方法
      System.out.println("将信息偷偷滴卖给装修的人...");
    }
}
package com.cjx.service;
//婚介中心
public class FindWifeProxy implements FindWife{
    //定义一个 有找媳妇需求的人
    private FindWife findWife;

    public FindWifeProxy(FindWife findWife) {
      this.findWife = findWife;
    }

    @Override
    public void findWife(String message) {
      System.out.println("我们这里是百合网,交会员费,提供优质服务,按照您的条件找");
      findWife.findWife(message);
      System.out.println("祝你幸福...");
      System.out.println("又把信息卖给了婚纱摄影");
    }
}


[*]被署理类
package com.cjx.service;
//消费者 委托人
public class Customer implements BuyHouse,FindWife{

    @Override
    public void buyHouse() {
      System.out.println("看房源...");
      System.out.println("谈价格...");
      System.out.println("过户...");
    }

    @Override
    public void findWife(String message) {
      System.out.println("要求是:"+message);
      System.out.println("主要是暖被窝...");
    }
}


[*]测试
package com.cjx.test;

import com.cjx.service.*;
import org.junit.Test;

public class DemoTest {
    @Test
    public void test01(){
      //没有代理的时候 自己买方房
      Customer customer = new Customer();
      customer.buyHouse();
      customer.findWife("xx");
      System.out.println("-------------------");

      //找中介 代理人
      BuyHouseProxy buyHouseProxy = new BuyHouseProxy(customer);
      buyHouseProxy.buyHouse();

      System.out.println("-------------------");

      //找媳妇 代理人
      FindWifeProxy findWifeProxy = new FindWifeProxy(customer);
      findWifeProxy.findWife("迪丽热巴..。");

      System.out.println("-------------------");

//      动态代理
      BuyHouse proxy = (BuyHouse) JDKProxy.getProxy(customer);
      proxy.buyHouse();
    }
}
   从实现上可以看到 署理 的主要作用是 方法增强,它可以在不   “惊动”被署理类的情况下修改被署理类的行为。这有助于体系   解耦。我们这里署理类和被署理类都是自己亲身敲好的,即在   步伐运行前署理类的.class文件就已经存在了的形式叫做静态   署理。值得留意的是,署理类和被署理类应该共同实现一个接   口,或者是共同继承某个类。      静态署理的缺点      

[*]         我们必要在运行前手动创建署理类,这意味着如果有很多            署理的话会很繁琐,一个被署理就必要一个署理;
[*]               其次署理类 和 被署理类      必须实现同样的接口,万一接口               有变更,署理、被署理类都得修改,容易出题目;
8.1.3 动态署理

   动态署理 与 静态署理 最大的区别就是不消我们创建那么多   类,敲那么多代码。在步伐运行时,运用反射机制动态创建而   成。      JDK    中为我们提供了    Proxy    类来实现动态署理,其中最紧张的      方法是    Proxy.newProxyInstance   :只必要我们传入相干信      息,它就可以返回我们必要的署理对象。             Object obj = Proxy.newProxyInstance(ClassLoader         loader,Class<?>[] interfaces, InvocationHandler h)                  上面的方法可以实现动态的拦截被署理类的方法,也就是说我            们无需定义署理类,由该方法自动产生可以实当署理的对象。               package com.cjx.service;

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

public class JDKProxy {
      //获取代理对象的方法 参数:被代理对象 委托人
//传入被代理人 返回一个代理对象
      public static Object getProxy(Object obj) {
            Object proxy = null;
            proxy = Proxy.newProxyInstance(
                  obj.getClass().getClassLoader(),
//类加载器,将生成的代理类加载到内存中
                  obj.getClass().getInterfaces(),
// 生成的代理类需要实现的接口的字节码
//invoke:此invoke方法并非是反射的invoke方法,此方法属于动态代理,
//调用代理类的哪个方法,invoke就表示那个方法
            new InvocationHandler() {
                /*
                proxy:代理对象
                method:委托人需要增强的方法
                args:要实现功能的参数
                Object:被增强方法的返回值
                */
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws
                        Throwable {
                  Object result = null;
//这个方法来自哪个接口定义的方法
                  Class<?> clazz =
                            method.getDeclaringClass();
                  if (clazz ==
                            BuyHouse.class) {
//我们应该根据委托人不同的需求,执行不同的功能增强
                        String methodName =
                              method.getName();
                        if
                        (methodName.equalsIgnoreCase("buyHouse")) {
//对买房的操作进行增强
                            System.out.println("我是中介,帮您买房,先付款...");
                            result =
                                    method.invoke(obj, args);
                            System.out.println("将信息偷偷滴卖给装修的人...");
                        }
                  }
                  if (clazz == FindWife.class) {
                        //我们应该根据委托人不同的需求,执行不同的功能增强
                        String methodName = method.getName();
                        if (methodName.equalsIgnoreCase("findWife")) {
                            System.out.println("我们这里是百合网,交会员费,提供优质服务,按照您的条件找");
                            result = method.invoke(obj, args);
                            System.out.println("又把信息卖给了婚纱摄影");
                        }
                  }
                  return result;
                }
                }
            );
            return proxy;
      }
    }

         明星与经纪人的关系先容了静态署理,欠好之处在于一个经纪               人只能署理一个明星,一旦明星有变更,或者想要署理其他明               星时,必要修改、创建经纪人,大量使用这种静态署理,会使               我们体系内的类的规模增大,并且不易维护;                            而动态署理模式,大大减少类的创建、修改本钱。此外动态代                  理还符合      AOP (       面向切面编程       )      头脑,在很多场所都有使用。                                 通过讲授可以得出结论      :      署理模式主要用于扩展原功能又不侵                     入(修改)源代码。      (      增强功能      )                           8.1.4 cglib署理

               JDK         的动态署理已经很好的帮我们完成了署理工作,那么,                        CGlib         动态署理又有什么好用的特征呢?                                           我们可以看出,静态署理和基于          JDK          的动态署理的被署理类都                           必要实现接口,那么,如果我的类没有实现接口,但是,我想                           给它设置署理,那么,应该怎么实现呢?                                                这时候,就轮到         CGlib         出场了,         CGlib         可以署理没有接口的类。                              我们必要在         pom         文件中引入         CGlib         的         jar         包。                                       <!--https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.9</version>
</dependency>                     cglib            动态署理其实就是把原有对象传进去进行方法拦截,拦                                 截到之后进行逻辑增强。                                             8.1.5 署理总结

                         使用署理技能就是为了帮我们在不入侵原有代码的情况下增强                                    业务逻辑!                                                               你完全可以使用静态署理一个一个去定义署理类,但是如许的                                       话太过于繁琐,而且有些情况下你不知道未来会有什么接口                                       (比如咱们的            Mybatis            ,你现在有个            UserMapper.java            ,以后                                       还可能有更多其他的            Mapper            接口,这些都是不确定的),所                                       以就是用动态署理去给他们天生署理对象吧                                                                  有接口就用               JDK               动态署理,没有接口就用               CGLIB               动态署理                                                         8.2 AOP概述

                               OP                为                Aspect Oriented Programming                的缩写,意为:面向切                                             面编程,通过                运行期动态                实现步伐功能的统一维护的一种技                                             术。                AOP                是                OOP                的延续,是软件开发中的一个热门,也是                                             Spring                框架中的一个紧张内容。                                                                              AOP               弥补了               OOP               的不足,基于               OOP               底子上进行横向开发;                                                OOP               步伐开发以类为主题模子,一切围绕对象进行,通过构                                                建模子来完成任务;               AOP               步伐的开发主要关注               OOP               开发中的                                                共性功能,一切围绕共性功能进行。                                                                                 AOP                  通过接纳横向抽取机制,已经完全代替了传统纵向继承体                                                   系重复性代码的编写方式                                                                                        AOP                   的底层封装了动态署理,我们可以理解为                   AOP                   是动态署理                                                      的一种简单写法,进行功能的增强!                                                                                             简单理解:                  AOP                  就是实现业务代码和逻辑代码的解耦,但在运                                                         行时又可以织入在一起。                                                                                                                            场景                         名称                                                                          场景描述                                                                          变乱控制                                                                         批量的为业务对象,增加变乱处置惩罚的功能,不消                                                                        每个对象都去办变乱处置惩罚                                                                          记录跟踪                                                                         批量的为业务对象,增加日记记录的功能,不消                                                                        在每个方法执行进行日记记录                                                                          权限控制                                                                         批量的为业务对象,增加权限查抄,不消每个业                                                                        务对象都做安全查抄                                                                         异常处置惩罚                                                                         批量的为业务对象,增加异常情况的处置惩罚功能,                                                                        不消在每个方法执行进行异常处置惩罚                                                                          参数校验                                                                         批量的为业务对象,增加各种入参的查抄,不消                                                                        在方法内部进行检                                                                          等等                        .....                  8.3 AOP 相干概念

                  

[*]                                             Joinpoint(                     连接点                     )                     :                        所谓连接点是指那些被拦截到的点。                                                                  在                     spring                     中                     ,                     这些点指的是方法                     ,                     因为                     spring                     只支持方法范例                                                                  的连接点。
[*]                                                                     Pointcut(                        切入点                        )                        :                         所谓切入点是指我们要对哪些                        Joinpoint                                                                     进行拦截的定义。比方:我们设置                        save                        方法就是切入点
                                                                                                                                                                                             

[*]       Advice(    关照    /    增强    )    :   所谓关照是指拦截到    Joinpoint    之后所         要做的事情就是关照。    关照的范例:前置关照    ,    后置关照    ,    异           常关照    ,    最终关照    ,    环绕关照。    比方:    @Before    前置关照    :    在         切入点运行前执行。
[*]            Target(   目标对象   )   :   署理的目标对象。举例:            UserServiceImpl   ,他就是目标对象,因为它里面的功能需            要被增强。       
[*]       Weaving(    织入    )    :   就是代码运行的时候 业务代码和逻辑代码         织入到一起执行。比方:把    before    方法在    save    方法执行之         前执行了这就是织入。
[*]            Aspect(   切面   )   :      是切入点和关照的结合。切面   =   切入点   +   方位            信息   +   增强逻辑,比方:   AopAdvice   类 是一个切面类       
[*] Proxy(署理): 一个类被AOP织入增强后,就产生一个结果       署理类。比方:    AccountServiceImpl    它的功能在增强的时         候,现实是天生了署理对象增强的。
   8.4 AOP案例

8.4.1 需求分析

   需求:任意业务层接口执行前后获取当前体系时间            分析:            

[*]原始步伐中将共性功能抽取出来独立制作成方法放在特定类中
[*]定义带有共性功能的方法名
[*]将抽取出来的共性功能与对应的方法名之间绑定关系
    8.4.2 情况准备

   

[*]实体类
    package com.cjx.pojo;

import org.springframework.stereotype.Component;

@Component
public class User {
    private int id;
    private String name;
    //省略get set方法
}
   

[*]设置类
    package com.cjx.config;

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

//Spring配置文件
@Configuration
@ComponentScan("com.cjx")
@EnableAspectJAutoProxy
public class SpringConfig {
}
   

[*]关照类
    public class AopAdvice {
public void before(){
System.out.println("当前方法执行前初始时间毫
秒值是:"+System.currentTimeMillis());
}
public void after(){
System.out.println("当前方法执行后初始时间毫
秒值是:"+System.currentTimeMillis());
}
}   

[*]业务层接口和实现类
    package com.cjx.service;

import com.cjx.pojo.User;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserServiceImpl implements UserService{
    @Override
    public void save(User user) {
      //System.out.println("当前方法执行前初始时间毫秒值是:"+System.currentTimeMillis());
      System.out.println("保存了用户数据~");
      //System.out.println("当前方法执行后初始时间毫秒值是:"+System.currentTimeMillis());
    }

    @Override
    public void update(User user) {
      //System.out.println("当前方法执行前初始时间毫秒值是:"+System.currentTimeMillis());
      System.out.println("更新了用户数据~");
      //System.out.println("当前方法执行后初始时间毫秒值是:"+System.currentTimeMillis());
    }

    @Override
    public void delete(String id) {
      System.out.println("删除了用户数据~");
    }

    @Override
    public List<User> findAll() {
      System.out.println("查询了用户数据~");
      return null;
    }
}
    @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes =
{SpringConfig.class})
public class DemoTest {
@Autowired
private UserService userService;
@Test
public void test01(){
userService.save(new User());
}
}    8.4.3 AOP实现

   

[*]导入aspect坐标
    <dependency>
<groupId>org.springframework</groupId>
<artifactId>springaspects</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>   

[*]             将共性功能抽取出来制作成独立的关照方法写入关照类                  中。创建一个新的类,将共性功能提取出来制作成独立的                  方法
         /*
通知类
*/
public class AopAdvice {
    public void before(){
      System.out.println("当前方法执行前初始时间毫秒值是:"+System.currentTimeMillis());
    }
    public void after(){
      System.out.println("当前方法执行后初始时间毫秒值是:"+System.currentTimeMillis());
    }
}   

[*]将共性功能抽取的位置定义成切入点,写入关照类中
   /*
通知类
切入点:@Pointcut
你想增强哪个方法,那个方法就当做切入点,切入点在通知
类中这样配置
void
com.lzw.service.UserServiceImpl.save(com.lzw.poj
o.User)
*/
public class AopAdvice {
//配置切入点 是用来找到需要被增强的方法的
    @Pointcut("execution(void
    com.lzw.service.UserServiceImpl.save(com.lzw.poj
    o.User))")
    public void pt(){
    }
}   

[*]绑定原始操作与被抽取的功能之间的关系
   package com.cjx.aop;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import java.util.Date;

/*
通知类
    切入点:@Pointcut
    你想增强哪个方法,那个方法就当做切入点,切入点在通知
    类中这样配置
void
com.lzw.service.UserServiceImpl.
save(com.lzw.pojo.User)
@Component 我这个通知类交给spring管理
@Aspect 当前类为切面类 里面包含 切入点+方位信息
+增强逻辑 目的是spring底层自动完成织入
*/
@Component
@Aspect
public class AopAdvice {
    /*
      before after 使我们抽出来的 逻辑代码
      在aop的术语中 这两个方法 叫做通知方法
      在 pt 切入点 方法执行之前 执行 before功能
    */

    //配置切入点 是用来找到需要被增强的方法的
    @Pointcut("execution(void com.cjx.service.UserServiceImpl.save(com.cjx.pojo.User))")
    public void pt(){

    }

    @Pointcut("execution(void com.cjx.service.UserServiceImpl.update(com.cjx.pojo.User))")
    public void update(){

    }

    @Before("pt()")
    public void before(){
      System.out.println("当前方法执行前初始时间毫秒值是:"+System.currentTimeMillis());
    }

    @After("pt()")
    public void after(){
      System.out.println("当前方法执行后初始时间毫秒值是:"+System.currentTimeMillis());
    }

    @Before("update()")
    public void before01(){
      System.out.println("当前方法执行的时间是:"+new Date());
    }

    @After("update()")
    public void after01(){
      System.out.println("当前方法执行的时间是:"+new Date());
    }
}

   

[*]在Spring设置类上声明开启AOP功能
   package com.cjx.config;

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

//Spring配置文件
@Configuration
@ComponentScan("com.cjx")
@EnableAspectJAutoProxy
public class SpringConfig {
}
   8.4.4 工作流程分析

   

[*]Spring容器启动,加载bean
[*]启动AOP,加载所有的切入点
[*]               bean      工作的过程中,      AOP      内部监听机制不停监听设置的切                     入点是否运行
[*]                        当切入点对应的对象(目标对象)执行对应的切入点方法                        时,创建目标对象的署理对象
         

[*]动态将关照内容织入到署理对象对应的方法中
[*]最终由署理对象完成最终工作
package com.cjx.test;

import com.cjx.config.SpringConfig;
import com.cjx.pojo.User;
import com.cjx.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.sql.PreparedStatement;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class UserTest {

    @Autowired
    private User user;

    @Autowired
    private UserService userService;


    @Test
    public void test01(){

      userService.save(user);
      userService.update(user);
      userService.delete("1");
      userService.findAll();
    }
}
ok了家人们,see you later
https://i-blog.csdnimg.cn/direct/84082e4186d146d1a33d76ba399cd2f8.png

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: spring (Aop) day 1024