zoukankan      html  css  js  c++  java
  • spring-aop-1

    1.Spring的aop是什么?

    与OOP对比,AOP是处理一些横切性问题,这些横切性问题不会影响到主逻辑实现的,但是会散落到代码的各个部分,难以维护。AOP就是把这些问题和主业务逻辑分开,达到与主业务逻辑解耦的目的。

    2.aop的应用场景

    日志记录

    权限验证

    效率检查

    事务管理

    3.关于aop的一些概念

    join point

      -->连接点:目标对象中的方法

      -->例如:dao层有四个方法需要添加事务,这四个方法成为连接点.

    Pointcut

      -->切点:连接点的集合,

      切点有两个关键的要素:

      1.where:增强到哪里方法?

      2.when:什么时候进行增强

      可以把切点理解为数据库中的一张表,那么连接点就是表中的一条数据.

    Weaving

      -->织入:把代理逻辑加入到目标对象上的过程叫做织入

    advice

      -->通知

      包含了两部分:

      1.通知具体的内容(代理逻辑代码)

      2.where:通知到哪里去-->代理逻辑代码织入到目标对象那个位置

    Aspect

      -->切面

      在AspectJ中它是一个类

      在xml中它就是一个标签  

    target 

      -->目标对象,原始对象

    aop Proxy

      -->代理对象,包含了原始对象的代码和增加后的代码的那个对象

    4.springAop和AspectJ的关系

    (1)aop与springAop的关系

    aop是一种思想,springAop是aop思想的实现.类似于ioc与di的关系

    (2)spring AOP提供两种编程风格

      @AspectJ support ------------>利用aspectj的注解

      Schema-based AOP support ----------->xml aop:config 命名空间

      证明:spring,通过源码分析了,我们可以知道spring底层使用的是JDK或者CGLIB来完成的代理,并且在官网上spring给出了aspectj的文档,和springAOP是不同

    (3)简单的总结

      spring支持AspectJ的语法,但具体的底层实现是spring自己实现的.

      类似与javaScript借鉴java语法一样.

    5.springAop基于AspectJ实现

    (1)

    ①使用Java配置启用@AspectJ支持---需要映入aspectj相应的pom文件

    @Configuration
    @ComponentScan("cn.cg.*")
    //启动@AspectJ支持 @EnableAspectJAutoProxy
    public class AppConfig { }

    ②使用xml配置启用@AspectJ支持

    <aop:aspectj-autoproxy/>

    (2)声明一个Target

    @Component
    public class IndexDao {
        public void query(){
            System.out.println("query");
        }
    }

    (3)声明一个aspect

    @Component
    @Aspect
    public class DaoAspect {
    
    }

    (4)声明切点

    @Component
    @Aspect
    public class DaoAspect {
      //切点
        @Pointcut("execution(* cn.cg.target.IndexDao.query(..))")
        public void pointCut(){
            //空,若写代码不执行
        }
        
    }

    (5)声明一个通知@Component@Aspect

    public class DaoAspect {
    
        @Pointcut("execution(* cn.cg.target.IndexDao.query(..))")
        public void pointCut(){ }
        //关键词:
      //when-->before-->前置通知-->具体的逻辑代码织入到目标方法的前面
    @Before(
    "pointCut()") public void before(){ //具体的逻辑代码 System.out.println("before"); } }

    (6)测试

      

    public class MainClass {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
            IndexDao dao = (IndexDao) ac.getBean("indexDao");
            dao.query();
        }
    }

    console:

    before
    query

    success!!!

    (7)Supported Pointcut Designators-->spring支持的切入点指示器

    一共9种:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#aop-pointcuts

    介绍其中的三种-->一般开发中最常用的

    ①execution

    For matching method execution join points --->用于匹配方法执行连接点

    例如:* public cn.cg.target.IndexDao.query(..)
    同时切点表达式也支持*,&&,||等符号--后面有案例
    例如:* cn.*.target.Index*.*(..)
    --官方给出的execute表达式语法:
    execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
                throws-pattern?)
    这里问号表示当前项可以有也可以没有,其中各项的语义如下
    modifiers-pattern:方法的可见性,如public,protected;
    ret-type-pattern:方法的返回值类型,如int,void等;
    declaring-type-pattern:方法所在类的全路径名,如com.spring.Aspect;
    name-pattern:方法名类型,如buisinessService();
    param-pattern:方法的参数类型,如java.lang.String;
    throws-pattern:方法抛出的异常类型,如java.lang.Exception;

    ②within

     Limits matching to join points within certain types-->将匹配限制为特定类型中的连接点

    表达式的最小粒度为类
      
    @Pointcut("within(com.cg.dao.*)")//匹配com.chenss.dao包中的任意方法
    @Pointcut("within(com.cg.dao..*)")//匹配com.chenss.dao包及其子包中的任意方法

    案例:

    配置一个新的Dao

    //添加新的Dao
    @Component
    public class UserDao { public void Personquery(){ System.out.println("query"); } }

    配置切点

    //cn.cg.target.UserDao下所有的方法
    //如果将表达式改为:@Pointcut("within(cn.cg.target.UserDao.*)")则不生效
    @Pointcut("within(cn.cg.target.UserDao)")
        public void pointCut2(){
    
        }
    
        @Before("pointCut2()")
        public void before( ){
            //具体的逻辑代码
            System.out.println("before");
        }

    测试:

    public class MainClass {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
            IndexDao dao = (IndexDao) ac.getBean("indexDao");
            dao.query();
            System.out.println("--------------------------");
            UserDao dao2 = (UserDao) ac.getBean("userDao");
            dao2.Personquery();
        }
    }

    console:

    query
    --------------------------
    before
    query

    结论:

    表达式的最小粒度为类

    ③agr 

     args表达式的作用是匹配指定参数类型和指定参数数量的方法,与包名和类名无关
    
    
    /**
     * args同execution不同的地方在于:
     * args匹配的是运行时传递给方法的参数类型
     * execution(* *(java.io.Serializable))匹配的是方法在声明时指定的方法参数类型。
     */
    @Pointcut("args(java.io.Serializable)")//匹配运行时传递的参数类型为指定类型的、且参数个数和顺序匹配
    @Pointcut("@args(com.chenss.anno.Chenss)")//接受一个参数,并且传递的参数的运行时类型具有@Classified

    案例:

      修改indexDao

    @Component
    public class IndexDao {
        public void query(){
            System.out.println("query");
        }
        public void query(String str){
            System.out.println("query"+str);
        }
    }

    切点:

      

     @Pointcut("args(java.lang.String)")
        public void pointCut3(){
    
        }
        @Before("pointCut3()")
        public void before( ){
            //具体的逻辑代码
            System.out.println("before");
        }

    测试:

    public class MainClass {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
            IndexDao dao = (IndexDao) ac.getBean("indexDao");
            dao.query();
            System.out.println("--------------------------");
            UserDao dao2 = (UserDao) ac.getBean("userDao");
            dao2.Personquery();
            System.out.println("--------------------------");
            dao.query("1111");
        }
    }

    console:

    query
    --------------------------
    query
    --------------------------
    before
    query1111

    结论: 

    args表达式的作用是匹配指定参数类型和指定参数数量的方法,与包名和类名无关

    (4)||,&&,!的使用

    案例--->演示&&

    修改UserDao用于做对照

    @Component
    public class UserDao {
        public void Personquery(){
            System.out.println("query");
        }
        public void Personquery(String str){
            System.out.println("query"+str);
        }
        
    }

    添加切点

    //indexDao类中需传入参数为String类型的方法,并且该参数存在且只有一个
    @Pointcut("within(cn.cg.target.IndexDao)&&args(java.lang.String)") public void pointCut4(){ } @Before("pointCut4()") public void before( ){ //具体的逻辑代码 System.out.println("before"); }

    测试:

    public class MainClass {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
            IndexDao dao = (IndexDao) ac.getBean("indexDao");
            dao.query("index");
            System.out.println("--------------------------");
            UserDao dao2 = (UserDao) ac.getBean("userDao");
            dao2.Personquery("person");
            System.out.println("--------------------------");
            dao.query();
            System.out.println("--------------------------");
            dao2.Personquery();
        }
    }

    console:

      

    before
    queryindex
    --------------------------
    queryperson
    --------------------------
    query
    --------------------------
    query

    结论:

      仅增强了indexDao中query(String str)方法,达到了预期的目的.

    (5)target----指向接口和子类

     this----JDK代理时,指向接口和代理类proxy,cglib代理时 指向接口和子类(不使用proxy)

    /**
     * 此处需要注意的是,如果配置设置proxyTargetClass=false,或默认为false,则是用JDK代理,否则使用的是CGLIB代理
     * JDK代理的实现方式是基于接口实现,代理类继承Proxy,实现接口。
     * 而CGLIB继承被代理的类来实现。
    * 所以使用target会保证目标不变,关联对象不会受到这个设置的影响。 * 但是使用this对象时,会根据该选项的设置,判断是否能找到对象。
    */ @Pointcut("target(com.chenss.dao.IndexDaoImpl)")//目标对象,也就是被代理的对象。限制目标对象为com.chenss.dao.IndexDaoImpl类 @Pointcut("this(com.chenss.dao.IndexDaoImpl)")//当前对象,也就是代理对象,代理对象时通过代理目标对象的方式获取新的对象,与原值并非一个 @Pointcut("@target(com.chenss.anno.Chenss)")//具有@Chenss的目标对象中的任意方法 @Pointcut("@within(com.chenss.anno.Chenss)")//等同于@targ

     案例:

      添加切点

    @Pointcut("this(cn.cg.target.IndexDao)")
        public void pointCut5(){
    
        }
    
        @Before("pointCut5()")
        public void before( ){
            //具体的逻辑代码
            System.out.println("before");
        }

      创建接口

    public interface Dao {
        public void query();
        public void query(String str);
    }

      IndexDao实现该接口

    @Component
    public class IndexDao implements Dao {
        public void query(){
            System.out.println("query");
        }
        public void query(String str){
            System.out.println("query"+str);
        }
    }

      测试:

    public class MainClass {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
            IndexDao dao = (IndexDao) ac.getBean("indexDao");
    
        }
    }

      console:

    Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy18 cannot be cast to cn.cg.target.IndexDao
        at cn.cg.MainClass.main(MainClass.java:18)

    为什么呢?

    IndexDao实现了Dao接口--->spring容器对IndexDao代理方式为动态代理-->代理对象(extends Proxy implements Dao)-->代理对象与IndexDao无关

    所以修改测试方法

    public class MainClass {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
            Dao dao = (Dao) ac.getBean("indexDao");
            dao.query();
        }
    }
    

      console:

    query

    为什么目标方法没有被增强呢?

    与上述问题的原因一致,因为容器中获取的代理对象并不是IndexDao的实现类

    解决方案:使用cglib代理

    @Configuration
    @ComponentScan("cn.cg.*")
    
    @EnableAspectJAutoProxy(proxyTargetClass = true)
    public class AppConfig { }

    console:

    before
    query

    原因:因为cglib代理是通过继承IndexDao实现的

      

  • 相关阅读:
    739. Daily Temperatures
    535. Encode and Decode TinyURL
    811. Subdomain Visit Count
    706. Design HashMap
    C++-static作用(转)
    大学四年应当如何渡过(转)
    计算机导论第八章-总结
    计算机导论第四章习题
    计算机导论-第一章习题
    20世纪最伟大的十大算法
  • 原文地址:https://www.cnblogs.com/cg961107/p/11247931.html
Copyright © 2011-2022 走看看