spring的aop的目的是什么呢?为什么要有aop这个概念呢?这是我在学习aop所思考的事情。
1.spring的aop的目的就是为了把相同的代码抽出来,降低代码之间的耦合度的作用
2.大家学java肯定知道oop(面向对象编程),oop里面有四大特性封装,继承,多态和抽象。大家写程序知道它是由上到下或者下由到上,但是有时不光需要由上到下或者下由到上,还需要由左到右或者右由到左这种思想。aop(面向切面编程)的概念由此而生
我看过好多博客有的人写aop就是按书上来的。我喜欢简单,通俗,易懂的。
按成程序分,肯定有核心业务和非核心
核心业务代码,我们肯定要日志呀,异常呀。非核心指的就是指的日志和异常这些代码。这就让我们想到核心代码的哪个方法加,例如日志代码我可以统一放一起不。核心代码那么多,方法那多对吧。可能这一段是针对日志的,那一边段是针对异常的。无论核心代码还是非核心代码都有很多店需要关注对吧。
以上是我们可以想到这些问题?我们该如何处理呢?
这样应运而生了几个概念,我们关注点也叫横切点,针对核心代码被拦截方法的就叫做连接点,那拦截方法的就要做切入点。统一放到一起的切入面是不是就可以叫做切面了。那谁告诉切面什么时候该执行什么,那就得有人告诉他吧。所以通知就有了,核心代码肯定需要写在不同的对象吧。那目标对象(代理对象)概念应运而生。我们的目的得让他起作用吧,切面应用到目标对象(使代理对象)被创建的过程叫做织入,在原来代码不被修改,而起到作用叫做引用。
以上我把特抽象的概念,一步步的变成大家分析出来,这样那些概念是不是就很容易理解了,原理也很清楚了。我们总结一下aop概念
1.横切点(那些方法需要被拦截),2.连接点(被拦截的方法),3.切入点(拦截的方法),4.切入面(切入点的集合),5.目标对象(代理对象),7.通知(告诉切面什么时候执行目标对象那些方法),8.织入(创建代理对象的过程),9.引入(在不修改代码,将原来共有的放到一起。而不影响功能)
这里我有几个网址专门学习aop的:1.https://www.cnblogs.com/best/p/5736422.html,2.https://blog.csdn.net/qq_27093465/article/details/53367361,3.https://www.cnblogs.com/hongwz/p/5764917.html
这里强调一下,对于springaop采用2种代理对象
1.如果目标类实现了接口,则spring会采用jdk动态代理
2.如果目标类没有显现接口,则采用cglib动态代理
这个aop测试所使用到的jar包有以下:
aspectjrt.jar
aspectjweaver.jar
cglib-nodep-2.1_3.jar
spring.jar
下面是测试代码。
共同接口:(其实这个在实际使用中是可以省略的,又不是对他进行添加切面编程的)
- package com.lxk.spring.aop;
- import java.util.List;
- /**
- * 目标对象和代理对象都实现的接口
- */
- public interface PersonDao {
- void deletePerson();
- List<Person> getPerson() throws Exception;
- void savePerson();
- void updatePerson();
- }
目标对象实现目标接口:(这才是真正要面对的,要扩展的。要未他们做切面的地方)
- package com.lxk.spring.aop;
- import com.google.common.collect.Lists;
- import java.util.List;
- /**
- * 目标对象:实现目标接口
- */
- public class PersonDaoImpl implements PersonDao {
- @Override
- public void deletePerson() {
- System.out.println("delete perosn");
- }
- @Override
- public List<Person> getPerson() throws Exception {
- List<Person> personList = Lists.newArrayList();
- Person person1 = new Person();
- person1.setPid(1L);
- person1.setPname("person1");
- System.out.println("get person");
- personList.add(person1);
- Person person2 = new Person();
- person2.setPid(2L);
- person2.setPname("person2");
- personList.add(person2);
- return personList;
- }
- @Override
- public void savePerson() {
- System.out.println("delete perosn");
- }
- @Override
- public void updatePerson() {
- System.out.println("delete perosn");
- }
- }
切面和切面里面的通知
这个才是对上面的要扩展的扩展,这就是切面里面的操作,,,对很多相同的操作提取到切面上去操作。这个只是把切面内部的方法写好,下面的配置文件说明这个切面切哪里也就是切入点在哪,怎么切,是前置,环绕,后置,还是异常等等。
- package com.lxk.spring.aop;
- import org.aspectj.lang.JoinPoint;
- import org.aspectj.lang.ProceedingJoinPoint;
- import java.util.ArrayList;
- import java.util.List;
- /**
- * 切面(spring aop 就不需要拦截器啦)
- * (模拟hibernate里面保存数据要打开事物,然后各种增删改之后,再提交事物。)
- */
- public class Transaction {
- public void beginTransaction() {//前置通知
- //打开事物
- System.out.println("begin Transaction");
- }
- /**
- * @param joinPoint 通过joinPoint可以得到目标类和目标方法的一些信息
- * @param val 目标方法的返回值
- * 和<aop:after-returning returning="val"/>中returning的值保质一致
- */
- public void commit(JoinPoint joinPoint, Object val) {//后置通知
- String methodName = joinPoint.getSignature().getName();
- System.out.println(methodName);
- System.out.println(joinPoint.getTarget().getClass().getName());
- //提交事物
- System.out.println("commit");
- List<Person> personList = (ArrayList<Person>) val;
- for (Person person : personList) {
- System.out.println(person.getPname());
- }
- }
- public void finalMethod() {
- System.out.println("最终通知");
- }
- public void aroundMethod(ProceedingJoinPoint joinPoint) {//环绕通知
- try {
- System.out.println("around method");
- joinPoint.proceed();//调用目标类的目标方法
- } catch (Throwable e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- /**
- * 异常通知
- */
- public void throwingMethod(Throwable except) {
- System.out.println(except.getMessage());
- }
- }
aop配置基于配置文件方式
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
- http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
- <!--
- 1、目标类
- 2、切面
- 3、进行aop的配置
- (目标接口没有:因为引入到容器是为了实例化对象,接口是不能实现对象的。)
- (也没有拦截器的引入,有的只是aop的配置,如上的3、)
- -->
- <!-- 目标类 -->
- <bean id="personDao" class="com.lxk.spring.aop.PersonDaoImpl"/>
- <!-- 切面的声明 -->
- <bean id="transaction" class="com.lxk.spring.aop.Transaction"/>
- <!-- aop配置 -->
- <aop:config>
- <!--
- 配置aop的切入点
- id 是切入点的标识
- expression 为切入点的表达式
- execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)
- throws-pattern?)
- modifiers-pattern 修饰符 可选 public private protected
- ret-type-pattern 返回类型 必选 * 代表任意类型
- declaring-type-pattern 方法的声明类型
- name-patterm 方法名称类型
- set* 以set开头的所有的方法名称
- update* 以update开头的所有的方法名称
- param-pattern 参数匹配
- (..) 任意多个参数,每个参数任意多个类型
- (*,String) 两个参数 第一个是任意类型,第二个是String
- (String,*,Integer) 三个参数,第一个是String类型,第二个是任意类型,第三个是Integer类型
- throws-pattern 异常的匹配模式
- 例子:
- execution(* cn.itcast.spring.aop.xml.AService.*(..));
- cn.itcast.spring.aop.xml.AService下的所有的方法
- execution(public * cn.itcast.oa..*.*(..))
- 返回值为任意类型,修饰符为public,在cn.itcast.oa包及子包下的所有的类的所有的方法
- exectuion(* cn.itcast.oa..*.update*(*,String))
- 返回值是任意类型,在cn.itcast.oa包及子包下所有的以update开头的参数为两个,第一个为任意类型
- 第二个为String类型的所有类的所有的方法
- -->
- <aop:pointcut expression="execution(* com.lxk.spring.aop.PersonDaoImpl.*(..))" id="perform"/>
- <!-- 配置切面(切面里面配置通知)—— ref 指向声明切面的类 -->
- <aop:aspect ref="transaction">
- <!-- 前置通知pointcut-ref 引用一个切入点 -->
- <aop:before method="beginTransaction" pointcut-ref="perform"/>
- <!--
- 后置通知
- * returning 目标方法的返回值
- * 如果目标方法中有可能存在异常,异常确实发生了,这个时候,后置通知将不再执行
- -->
- <!--<aop:after-returning method="commit" pointcut-ref="perform" returning="val"/>-->
- <!--
- 最终通知
- * 不能得到目标方法的返回值
- * 无论目标方法是否有异常,最终通知都将执行
- * 资源的关闭、连接的释放写在最终通知里
- -->
- <!--<aop:after pointcut-ref="perform" method="finalMethod"/>-->
- <!--
- 环绕通知
- * ProceedingJoinPoint的proceed方法就是目标对象的目标方法
- * 环绕通知可以控制目标对象目标方法执行
- -->
- <!--
- <aop:around method="aroundMethod" pointcut-ref="perform"/>
- -->
- <!--
- 异常通知
- 在异常通知中获取目标方法抛出的异常
- -->
- <!--<aop:after-throwing method="throwingMethod" pointcut-ref="perform" throwing="except"/>-->
- </aop:aspect>
- </aop:config>
- <!--aop开启注解方式 -->
<aop:aspectj-autoproxy/> - </beans>
这里写的是非常不错的关于aop配置的xml,注释很详细
AspectJ中的切入点匹配的执行点称作连接的(Join Point),在通知方法中可以声明一个JoinPoint类型的参数。通过JoinPoint可以访问连接点的细节。下面简要介绍JponPoint的方法:
1.java.lang.Object[] getArgs():获取连接点方法运行时的入参列表;
2.Signature getSignature() :获取连接点的方法签名对象;
3.java.lang.Object getTarget() :获取连接点所在的目标对象;
4.java.lang.Object getThis() :获取代理对象本身;
ProceedingJoinPoint继承JoinPoint子接口,它新增了两个用于执行连接点方法的方法:
5.java.lang.Object proceed() throws java.lang.Throwable:通过反射执行目标对象的连接点处的方法;
6.java.lang.Object proceed(java.lang.Object[] args) throws java.lang.Throwable:通过反射执行目标对象连接点处的方法,不过使用新的参数替换原来的参数。
package com.zhangguo.Spring052.aop02; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; /** * 通知类,横切逻辑 * */
//次注解不影响aop的功能(spring注解中@component就是说把这个类交给Spring管理,又一次起个名字叫userManager,因为不清楚这个类是属于哪个层面,所以就用@Component)
@Component
@Aspect public class Advices { @Before("execution(* com.zhangguo.Spring052.aop02.Math.*(..))") public void before(JoinPoint jp){ System.out.println("----------前置通知----------"); System.out.println(jp.getSignature().getName()); } @After("execution(* com.zhangguo.Spring052.aop02.Math.*(..))") public void after(JoinPoint jp){ System.out.println("----------最终通知----------"); } }
上面的代码与下面的配置基本等同
<!-- 通知 --> <bean id="advices" class="com.zhangguo.Spring052.aop01.Advices"></bean> <!-- aop配置 --> <aop:config proxy-target-class="true"> <!--切面 --> <aop:aspect ref="advices"> <!-- 切点 --> <aop:pointcut expression="execution(* com.zhangguo.Spring052.aop01.Math.*(..))" id="pointcut1"/> <!--连接通知方法与切点 --> <aop:before method="before" pointcut-ref="pointcut1"/> <aop:after method="after" pointcut-ref="pointcut1"/> </aop:aspect> </aop:config>