博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
spring-aop-1
阅读量:4325 次
发布时间:2019-06-06

本文共 8602 字,大约阅读时间需要 28 分钟。

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支持@EnableAspectJAutoProxypublic class AppConfig {}

 

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

(2)声明一个Target

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

(3)声明一个aspect

@Component@Aspectpublic class DaoAspect {}

(4)声明切点

@Component@Aspectpublic 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:

beforequery

success!!!

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

一共9种:

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

①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 @Componentpublic 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--------------------------beforequery

结论:

表达式的最小粒度为类

 

③agr 

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

案例:

  修改indexDao

@Componentpublic 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--------------------------beforequery1111

结论: 

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

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

案例--->演示&&

修改UserDao用于做对照

@Componentpublic 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实现该接口

@Componentpublic 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:

beforequery

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

 

 

 

  

转载于:https://www.cnblogs.com/cg961107/p/11247931.html

你可能感兴趣的文章
Java面试知识点之线程篇(二)
查看>>
Android开发-之五大布局
查看>>
Math、Number、String 、Array、Object 的新 APIs
查看>>
浅谈STRUTS2
查看>>
Android 开发 学习网站
查看>>
spring启动quartz定时器
查看>>
poj -- 1042 Gone Fishing(枚举+贪心)
查看>>
经典C面试真题精讲
查看>>
Remove Duplicates from Sorted List解题报告
查看>>
到爱尔兰敲代码 / Come, Coding in Ireland
查看>>
ffmpeg格式转换命令
查看>>
CSS选择器 < ~ +
查看>>
用VS开发PHP扩展
查看>>
万方数据知识平台 TFHpple +Xpath解析
查看>>
Hive实现oracle的Minus函数
查看>>
秒杀多线程第四篇 一个经典的多线程同步问题
查看>>
RocketMQ配置
查看>>
vs code调试console程序报错--preLaunchTask“build”
查看>>
蚂蚁金服井贤栋:用技术联手金融机构,形成服务小微的生态合力
查看>>
手机通话记录统计分析
查看>>