spring (Aop) day 1024

打印 上一主题 下一主题

主题 1639|帖子 1639|积分 4917

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
ok了家人们,继续学习spring ,这段知识点有点绕,建议搭配b站的视频去学,passion!!!

.AOP-面向切面编程

8.1 动态署理

8.1.1 概述

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

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

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

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


  • 接口 
  1. public interface FindWife {
  2.     public void findWife(String message);
  3. }
复制代码


  •  署理类
  1. public class BuyHouseProxy implements BuyHouse{
  2. //定义一个 有买房需求的
  3. private BuyHouse buyHouse;
  4. //在创建代理对象的时候 得有委托人
  5. public BuyHouseProxy(BuyHouse buyHouse) {
  6.     this.buyHouse = buyHouse;
  7. }
  8.     @Override
  9.     public void buyHouse() {
  10.     //代理人 帮助 委托人
  11.     System.out.println("我是中介,帮您买房,先付款...");
  12.         buyHouse.buyHouse();//最终 委托人买方法
  13.     System.out.println("将信息偷偷滴卖给装修的人...");
  14.     }
  15. }
复制代码


  • 署理类
  1. package com.cjx.service;
  2. //买房的中介 代理
  3. public class BuyHouseProxy implements BuyHouse{
  4.     //定义一个 有买房需求的
  5.     private BuyHouse buyHouse;
  6.     public BuyHouseProxy(BuyHouse buyHouse) {
  7.         this.buyHouse = buyHouse;
  8.     }
  9.     @Override
  10.     public void buyHouse() {
  11.         //代理人 帮助 委托人
  12.         System.out.println("我是中介,帮您买房,先付款...");
  13.         buyHouse.buyHouse();//最终 委托人买方法
  14.         System.out.println("将信息偷偷滴卖给装修的人...");
  15.     }
  16. }
复制代码
  1. package com.cjx.service;
  2. //婚介中心
  3. public class FindWifeProxy implements FindWife{
  4.     //定义一个 有找媳妇需求的人
  5.     private FindWife findWife;
  6.     public FindWifeProxy(FindWife findWife) {
  7.         this.findWife = findWife;
  8.     }
  9.     @Override
  10.     public void findWife(String message) {
  11.         System.out.println("我们这里是百合网,交会员费,提供优质服务,按照您的条件找");
  12.         findWife.findWife(message);
  13.         System.out.println("祝你幸福...");
  14.         System.out.println("又把信息卖给了婚纱摄影");
  15.     }
  16. }
复制代码


  • 被署理类
  1. package com.cjx.service;
  2. //消费者 委托人
  3. public class Customer implements BuyHouse,FindWife{
  4.     @Override
  5.     public void buyHouse() {
  6.         System.out.println("看房源...");
  7.         System.out.println("谈价格...");
  8.         System.out.println("过户...");
  9.     }
  10.     @Override
  11.     public void findWife(String message) {
  12.         System.out.println("要求是:"+message);
  13.         System.out.println("主要是暖被窝...");
  14.     }
  15. }
复制代码


  • 测试
  1. package com.cjx.test;
  2. import com.cjx.service.*;
  3. import org.junit.Test;
  4. public class DemoTest {
  5.     @Test
  6.     public void test01(){
  7.         //没有代理的时候 自己买方房
  8.         Customer customer = new Customer();
  9.         customer.buyHouse();
  10.         customer.findWife("xx");
  11.         System.out.println("-------------------");
  12.         //找中介 代理人
  13.         BuyHouseProxy buyHouseProxy = new BuyHouseProxy(customer);
  14.         buyHouseProxy.buyHouse();
  15.         System.out.println("-------------------");
  16.         //找媳妇 代理人
  17.         FindWifeProxy findWifeProxy = new FindWifeProxy(customer);
  18.         findWifeProxy.findWife("迪丽热巴..。");
  19.         System.out.println("-------------------");
  20. //      动态代理
  21.         BuyHouse proxy = (BuyHouse) JDKProxy.getProxy(customer);
  22.         proxy.buyHouse();
  23.     }
  24. }
复制代码
  从实现上可以看到 署理 的主要作用是 方法增强,它可以在不     “  惊动  ”  被署理类的情况下修改被署理类的行为。这有助于体系     解耦。我们这里署理类和被署理类都是自己亲身敲好的,即在     步伐运行前署理类的  .class  文件就已经存在了的形式叫做静态     署理。值得留意的是,署理类和被署理类应该共同实现一个接     口,或者是共同继承某个类。        静态署理的缺点      

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

   动态署理 与 静态署理 最大的区别就是不消我们创建那么多     类,敲那么多代码。在步伐运行时,运用反射机制动态创建而     成。        JDK    中为我们提供了    Proxy    类来实现动态署理,其中最紧张的        方法是    Proxy.newProxyInstance   :只必要我们传入相干信        息,它就可以返回我们必要的署理对象。             Object obj = Proxy.newProxyInstance(ClassLoader           loader,Class<?>[] interfaces, InvocationHandler h)                  上面的方法可以实现动态的拦截被署理类的方法,也就是说我              们无需定义署理类,由该方法自动产生可以实当署理的对象。               
  1. package com.cjx.service;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. import java.lang.reflect.Proxy;
  5. public class JDKProxy {
  6.         //获取代理对象的方法 参数:被代理对象 委托人
  7. //传入被代理人 返回一个代理对象
  8.         public static Object getProxy(Object obj) {
  9.             Object proxy = null;
  10.             proxy = Proxy.newProxyInstance(
  11.                     obj.getClass().getClassLoader(),
  12. //类加载器,将生成的代理类加载到内存中
  13.                     obj.getClass().getInterfaces(),
  14. // 生成的代理类需要实现的接口的字节码
  15. //invoke:此invoke方法并非是反射的invoke方法,此方法属于动态代理,
  16. //调用代理类的哪个方法,invoke就表示那个方法
  17.             new InvocationHandler() {
  18.                 /*
  19.                 proxy:代理对象
  20.                 method:委托人需要增强的方法
  21.                 args:要实现功能的参数
  22.                 Object:被增强方法的返回值
  23.                 */
  24.                 @Override
  25.                 public Object invoke(Object proxy, Method method, Object[] args) throws
  26.                         Throwable {
  27.                     Object result = null;
  28. //这个方法来自哪个接口定义的方法
  29.                     Class<?> clazz =
  30.                             method.getDeclaringClass();
  31.                     if (clazz ==
  32.                             BuyHouse.class) {
  33. //我们应该根据委托人不同的需求,执行不同的功能增强
  34.                         String methodName =
  35.                                 method.getName();
  36.                         if
  37.                         (methodName.equalsIgnoreCase("buyHouse")) {
  38. //对买房的操作进行增强
  39.                             System.out.println("我是中介,帮您买房,先付款...");
  40.                             result =
  41.                                     method.invoke(obj, args);
  42.                             System.out.println("将信息偷偷滴卖给装修的人...");
  43.                         }
  44.                     }
  45.                     if (clazz == FindWife.class) {
  46.                         //我们应该根据委托人不同的需求,执行不同的功能增强
  47.                         String methodName = method.getName();
  48.                         if (methodName.equalsIgnoreCase("findWife")) {
  49.                             System.out.println("我们这里是百合网,交会员费,提供优质服务,按照您的条件找");
  50.                             result = method.invoke(obj, args);
  51.                             System.out.println("又把信息卖给了婚纱摄影");
  52.                         }
  53.                     }
  54.                     return result;
  55.                 }
  56.                 }
  57.             );
  58.             return proxy;
  59.         }
  60.     }
复制代码
          明星与经纪人的关系先容了静态署理,欠好之处在于一个经纪                 人只能署理一个明星,一旦明星有变更,或者想要署理其他明                 星时,必要修改、创建经纪人,大量使用这种静态署理,会使                 我们体系内的类的规模增大,并且不易维护;                            而动态署理模式,大大减少类的创建、修改本钱。此外动态代                    理还符合        AOP (       面向切面编程       )        头脑,在很多场所都有使用。                                 通过讲授可以得出结论        :        署理模式主要用于扩展原功能又不侵                       入(修改)源代码。        (        增强功能        )                             8.1.4 cglib署理

                 JDK         的动态署理已经很好的帮我们完成了署理工作,那么,                          CGlib         动态署理又有什么好用的特征呢?                                           我们可以看出,静态署理和基于          JDK          的动态署理的被署理类都                             必要实现接口,那么,如果我的类没有实现接口,但是,我想                             给它设置署理,那么,应该怎么实现呢?                                                这时候,就轮到           CGlib           出场了,           CGlib           可以署理没有接口的类。                                我们必要在           pom           文件中引入           CGlib           的           jar           包。                                       
  1. <!--https://mvnrepository.com/artifact/cglib/cglib -->
  2. <dependency>
  3. <groupId>cglib</groupId>
  4. <artifactId>cglib</artifactId>
  5. <version>3.2.9</version>
  6. </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 情况准备

   

  • 实体类
   
  1. package com.cjx.pojo;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class User {
  5.     private int id;
  6.     private String name;
  7.     //省略get set方法
  8. }
复制代码
   

  • 设置类
   
  1. package com.cjx.config;
  2. import org.springframework.context.annotation.ComponentScan;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.context.annotation.EnableAspectJAutoProxy;
  5. //Spring配置文件
  6. @Configuration
  7. @ComponentScan("com.cjx")
  8. @EnableAspectJAutoProxy
  9. public class SpringConfig {
  10. }
复制代码
   

  • 关照类
   
  1. public class AopAdvice {
  2. public void before(){
  3. System.out.println("当前方法执行前初始时间毫
  4. 秒值是:"+System.currentTimeMillis());
  5. }
  6. public void after(){
  7. System.out.println("当前方法执行后初始时间毫
  8. 秒值是:"+System.currentTimeMillis());
  9. }
  10. }
复制代码
   

  • 业务层接口和实现类
   
  1. package com.cjx.service;
  2. import com.cjx.pojo.User;
  3. import org.springframework.stereotype.Service;
  4. import java.util.List;
  5. @Service
  6. public class UserServiceImpl implements UserService{
  7.     @Override
  8.     public void save(User user) {
  9.         //System.out.println("当前方法执行前初始时间毫秒值是:"+System.currentTimeMillis());
  10.         System.out.println("保存了用户数据~");
  11.         //System.out.println("当前方法执行后初始时间毫秒值是:"+System.currentTimeMillis());
  12.     }
  13.     @Override
  14.     public void update(User user) {
  15.         //System.out.println("当前方法执行前初始时间毫秒值是:"+System.currentTimeMillis());
  16.         System.out.println("更新了用户数据~");
  17.         //System.out.println("当前方法执行后初始时间毫秒值是:"+System.currentTimeMillis());
  18.     }
  19.     @Override
  20.     public void delete(String id) {
  21.         System.out.println("删除了用户数据~");
  22.     }
  23.     @Override
  24.     public List<User> findAll() {
  25.         System.out.println("查询了用户数据~");
  26.         return null;
  27.     }
  28. }
复制代码
  
  1. @RunWith(SpringJUnit4ClassRunner.class)
  2. @ContextConfiguration(classes =
  3. {SpringConfig.class})
  4. public class DemoTest {
  5. @Autowired
  6. private UserService userService;
  7. @Test
  8. public void test01(){
  9. userService.save(new User());
  10. }
  11. }
复制代码
   8.4.3 AOP实现

   

  • 导入aspect坐标
   
  1. <dependency>
  2. <groupId>org.springframework</groupId>
  3. <artifactId>springaspects</artifactId>
  4. <version>5.2.10.RELEASE</version>
  5. </dependency>
复制代码
   

  •              将共性功能抽取出来制作成独立的关照方法写入关照类                    中。创建一个新的类,将共性功能提取出来制作成独立的                    方法
        
  1. /*
  2. 通知类
  3. */
  4. public class AopAdvice {
  5.     public void before(){
  6.         System.out.println("当前方法执行前初始时间毫秒值是:"+System.currentTimeMillis());
  7.     }
  8.     public void after(){
  9.         System.out.println("当前方法执行后初始时间毫秒值是:"+System.currentTimeMillis());
  10.     }
  11. }
复制代码
   

  • 将共性功能抽取的位置定义成切入点,写入关照类中
   
  1. /*
  2. 通知类
  3. 切入点:@Pointcut
  4. 你想增强哪个方法,那个方法就当做切入点,切入点在通知
  5. 类中这样配置
  6. void
  7. com.lzw.service.UserServiceImpl.save(com.lzw.poj
  8. o.User)
  9. */
  10. public class AopAdvice {
  11. //配置切入点 是用来找到需要被增强的方法的
  12.     @Pointcut("execution(void
  13.     com.lzw.service.UserServiceImpl.save(com.lzw.poj
  14.     o.User))")
  15.     public void pt(){
  16.     }
  17. }
复制代码
   

  • 绑定原始操作与被抽取的功能之间的关系
   
  1. package com.cjx.aop;
  2. import org.aspectj.lang.annotation.After;
  3. import org.aspectj.lang.annotation.Aspect;
  4. import org.aspectj.lang.annotation.Before;
  5. import org.aspectj.lang.annotation.Pointcut;
  6. import org.springframework.stereotype.Component;
  7. import java.util.Date;
  8. /*
  9. 通知类
  10.     切入点:@Pointcut
  11.     你想增强哪个方法,那个方法就当做切入点,切入点在通知
  12.     类中这样配置
  13. void
  14. com.lzw.service.UserServiceImpl.
  15. save(com.lzw.pojo.User)
  16. @Component 我这个通知类交给spring管理
  17. @Aspect 当前类为切面类 里面包含 切入点+方位信息
  18. +增强逻辑 目的是spring底层自动完成织入
  19. */
  20. @Component
  21. @Aspect
  22. public class AopAdvice {
  23.     /*
  24.         before after 使我们抽出来的 逻辑代码
  25.         在aop的术语中 这两个方法 叫做通知方法
  26.         在 pt 切入点 方法执行之前 执行 before功能
  27.     */
  28.     //配置切入点 是用来找到需要被增强的方法的
  29.     @Pointcut("execution(void com.cjx.service.UserServiceImpl.save(com.cjx.pojo.User))")
  30.     public void pt(){
  31.     }
  32.     @Pointcut("execution(void com.cjx.service.UserServiceImpl.update(com.cjx.pojo.User))")
  33.     public void update(){
  34.     }
  35.     @Before("pt()")
  36.     public void before(){
  37.         System.out.println("当前方法执行前初始时间毫秒值是:"+System.currentTimeMillis());
  38.     }
  39.     @After("pt()")
  40.     public void after(){
  41.         System.out.println("当前方法执行后初始时间毫秒值是:"+System.currentTimeMillis());
  42.     }
  43.     @Before("update()")
  44.     public void before01(){
  45.         System.out.println("当前方法执行的时间是:"+new Date());
  46.     }
  47.     @After("update()")
  48.     public void after01(){
  49.         System.out.println("当前方法执行的时间是:"+new Date());
  50.     }
  51. }
复制代码
   

  • 在Spring设置类上声明开启AOP功能
   
  1. package com.cjx.config;
  2. import org.springframework.context.annotation.ComponentScan;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.context.annotation.EnableAspectJAutoProxy;
  5. //Spring配置文件
  6. @Configuration
  7. @ComponentScan("com.cjx")
  8. @EnableAspectJAutoProxy
  9. public class SpringConfig {
  10. }
复制代码
    8.4.4 工作流程分析

     

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

  • 动态将关照内容织入到署理对象对应的方法中
  • 最终由署理对象完成最终工作
  1. package com.cjx.test;
  2. import com.cjx.config.SpringConfig;
  3. import com.cjx.pojo.User;
  4. import com.cjx.service.UserService;
  5. import org.junit.Test;
  6. import org.junit.runner.RunWith;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.test.context.ContextConfiguration;
  9. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  10. import java.sql.PreparedStatement;
  11. @RunWith(SpringJUnit4ClassRunner.class)
  12. @ContextConfiguration(classes = SpringConfig.class)
  13. public class UserTest {
  14.     @Autowired
  15.     private User user;
  16.     @Autowired
  17.     private UserService userService;
  18.     @Test
  19.     public void test01(){
  20.         userService.save(user);
  21.         userService.update(user);
  22.         userService.delete("1");
  23.         userService.findAll();
  24.     }
  25. }
复制代码
ok了家人们,see you later


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

民工心事

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表