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支持的切入点指示器
介绍其中的三种-->一般开发中最常用的
①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-->将匹配限制为特定类型中的连接点
(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----
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实现的