一、Aop概述
1.Aop概念
面向切面编程,扩展功能通过不修改源代码实现;Aop采用横向抽取机制,取代了传统纵向继承体系重复性代码
2.Aop原理
原始修改代码的过程:
public class User {
// 添加用户的用法
public void add() {
// 添加逻辑
// 修改源代码,添加日志逻辑(功能扩展在此处添加)
}
}
纵向机制的解决
public class BaseUser {
// 创建日志添加的方法
public void writelog() {
// 添加日志逻辑
}
}
public class User extends BaseUser{
public void add() {
// 添加逻辑
// 功能扩展,添加日志的功能
// 调用父类的方法实现日志功能
super.writelog();
}
}
问题:比如父类的方法名称发生变化,在子类调用的方法也需要变化。
Aop:横向抽取机制
底层使用动态代理方式实现
第一种情况(有接口的情况):
public interface Dao {
public void add();
}
public class DaoImpl implements Dap {
public void add() {
// 添加逻辑
}
}
使用动态代理方式,创建接口实现类代理对象(对象并不是new出来的,而是代理出来的)
创建和DaoImpl类平级对象,这个类不是真正的对象,而是代理对象,实现和DaoImpl相同的功能
使用jdk动态代理,针对有接口情况
第二种情况(无接口的情况):
public class User {
public void add() {
...
}
}
// 动态代理实现
// 创建User类的子类的代理对象
// 在子类里面调用父类的方法完成增强
使用cglib动态代理,针对没有接口的情况。
二、Aop相关术语
以下面代码为例:
public class User {
public void add() {...}
public void delete() {...}
public void update() {...}
public void query() {...}
}
1.Joinpoint(连接点)(重要)
类里面有哪些方法可以被增强,这些方法称为连接点,例如上面类中的四个方法都可以被增强,所有这四个方法均为连接点。
2.Pointcut(切入点)(重要)
在类里面可以有很多的方法被增强,比如在实际操作中,只是增强了类里面add方法和update方法,那么add和update这两个被增强的方法就称为切入点。
3.通知/增强(重要)
增强的逻辑称为增强,比如扩展日志功能,这个日志功能称为增强。通知/增强可分为前置通知、后置通知、异常通知、最终通知、环绕通知。
(1)前置通知:在方法之前执行。
(2)后置通知:在方法执行之后执行,无论是否发生异常。
(3)异常通知:在目标方法抛出异常后通知。
(4)返回通知:在目标方法返回结果后执行。
(5)环绕通知:在方法之前和之后执行。
4.切面
把增强应用到具体方法上面,过程称为切面,即把增强用到切入点的过程。
5.Introduction(引介)
引介可以使用动态的方式向类中增加属性和方法。
5.Target(目标对象)
增强方法所在的类称为目标对象。
6.Weaving(织入)
织入就是增强应用到增强方法所在类的过程。
7.Proxy(代理)
一个类被Aop织入增强后,就产生一个结果代理类。
三、Spring的Aop操作
1.在Spring里面进行Aop操作,使用aspectj实现
(1)aspectj不是Spring的一部分,和Spring一起使用进行Aop操作
(2)Spring2.0以后新增了对aspectj的支持
2.Aop操作的准备
(1)除了导入基本的jar包之外,还需要导入aop相关的jar包:
aopalliance-1.0.jar
aspectjweaver-1.8.7.jar
spring-aspects-5.0.4.RELEASE.jar
spring-aop-5.0.4.RELEASE.jar
附:
aopalliance-1.0.jar下载地址:
http://mvnrepository.com/artifact/aopalliance/aopalliance/1.0
这个包是AOP联盟的API包,里面包含了针对面向切面的接口。通常Spring等其它具备动态织入功能的框架依赖此包。
aspectjweaver-1.8.7.jar下载地址:
http://mvnrepository.com/artifact/org.aspectj/aspectjweaver/1.8.7
aspectjweaver这个包是spring的切入点表达式需要用的包。
(2)创建Spring核心配置文件
除了引入了约束spring-beans之外还需要引入新约束spring-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" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-aop.xsd">
</beans>
3.使用表达式配置切入点
(1)切入点:实际增强的方法
(2)常用表达式
语法:
excute(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)
被增强类:
package com.jxs.aop;
/** * Created by jiangxs on 2018/3/28. */
public class Book {
public void print() {
System.out.println("I'm a Normal Book!");
}
}
增强类
package com.jxs.aop;
/** * Created by jiangxs on 2018/3/28. */
public class NBBook {
public void before1() {
System.out.println("前置增强");
}
}
表达式写法:
(1)execute(* com.jxs.aop.Book.print(..)) <!-- .. 表示其中有参数也会包含-->
(2)excution(* com.jxs.aop.Book.*(..))<!--对类中的所有参数都做增强-->
(3)excution(* *.*(..))<!--对所有类中的所有方法都做增强-->
(4)excution(* add*(..))<!--匹配所有add开头的方法-->
aop操作示例代码1(前置增强):
Book.java和NBBook.java见上
bean3.xml文件如下所示:
<?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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 1.配置对象 -->
<bean id="book" class="com.jxs.aop.Book"></bean>
<bean id="nbBook" class="com.jxs.aop.NBBook"></bean>
<!-- 2.配置aop操作 -->
<aop:config>
<!-- 2.1配置切入点 -->
<aop:pointcut id="pointcut1" expression="execution(* com.jxs.aop.Book.*(..))"/>
<!-- 2.2配置切面 -->
<aop:aspect ref="nbBook">
<!-- 配置增强类型 method:增强类里面使用哪个方法作为前置 pointcut-ref:增强的切入点的id -->
<aop:before method="before1" pointcut-ref="pointcut1"/>
</aop:aspect>
</aop:config>
</beans>
测试代码:
package com.jxs.aop;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/** * Created by jiangxs on 2018/3/28. */
public class TestAop {
@Test
public void testBook() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean3.xml");
Book book = (Book) context.getBean("book");
book.print();
}
}
测试结果:
log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
前置增强
I'm a Normal Book!
Process finished with exit code 0
aop操作示例代码2(后置增强+环绕通知):
package com.jxs.aop;
/** * Created by jiangxs on 2018/3/28. */
public class Book {
public void print() {
System.out.println("I'm a Normal Book!");
}
}
package com.jxs.aop;
import org.aspectj.lang.ProceedingJoinPoint;
/** * Created by jiangxs on 2018/3/28. */
public class NBBook {
public void before1() {
System.out.println("前置增强");
}
public void after1() {
System.out.println("后置增强");
}
// 环绕通知
public void around1(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
// 方法之前
System.out.println("方法之前");
// 执行被增强的方法
proceedingJoinPoint.proceed();
// 方法之后
System.out.println("方法之后");
}
}
<?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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 1.配置对象 -->
<bean id="book" class="com.jxs.aop.Book"></bean>
<bean id="nbBook" class="com.jxs.aop.NBBook"></bean>
<!-- 2.配置aop操作 -->
<aop:config>
<!-- 2.1配置切入点 -->
<aop:pointcut id="pointcut1" expression="execution(* com.jxs.aop.Book.*(..))"/>
<!-- 2.2配置切面 -->
<aop:aspect ref="nbBook">
<!-- 配置增强类型 method:增强类里面使用哪个方法作为前置 pointcut-ref:增强的切入点的id -->
<aop:before method="before1" pointcut-ref="pointcut1"/>
<!-- 后置 -->
<aop:after method="after1" pointcut-ref="pointcut1"/>
<!-- 环绕 -->
<aop:around method="around1" pointcut-ref="pointcut1"/>
</aop:aspect>
</aop:config>
</beans>
测试代码:
package com.jxs.aop;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/** * Created by jiangxs on 2018/3/28. */
public class TestAop {
@Test
public void testBook() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean3.xml");
Book book = (Book) context.getBean("book");
book.print();
}
}
测试结果:
log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
前置增强
方法之前
I'm a Normal Book!
方法之后
后置增强
Process finished with exit code 0
可以看出环绕通知的前后与前置增强和后置增强的前后关系。
四、基于aspectj的注解Aop操作
1.创建对象
2.在Spring核心配置文件中开启Aop操作
具体操作见xml配置文件代码:
<?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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 1.开启aop操作 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!-- 2.配置对象 -->
<bean id="book" class="com.jxs.aspectj.Book"></bean>
<bean id="nbBook" class="com.jxs.aspectj.NBBook"></bean>
</beans>
3.在增强类上面使用注解完成Aop操作
被增强类:
package com.jxs.aspectj;
/** * Created by jiangxs on 2018/3/29. */
public class Book {
public void print() {
System.out.println("I'm a book");
}
}
增强类:
package com.jxs.aspectj;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/** * Created by jiangxs on 2018/3/29. */
// 在增强的方法所在类上加@Aspect注解
@Aspect
public class NBBook {
// 在方法上面使用注解完成增强配置
@Before(value = "execution(* com.jxs.aspectj.Book.*(..))")
public void before1() {
System.out.println("前置通知");
}
}
测试方法:
/** * Created by jiangxs on 2018/3/29. */
public class TestBook {
@Test
public void testBook() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean1.xml");
Book book = (Book) context.getBean("book");
book.print();
}
}
测试结果:
前置通知
I'm a book
Process finished with exit code 0