AOP简介
1.什么是AOP ? 它有什么用?
AOP(Aspect Oriented Programming,面向切面编程), 可以说是OOP(Object Oriented Programing,面向对象编程)的补充和完善。在不改动源码的前提下,对原有的功能进行扩展 | 升级
2.AOP的底层原理是什么?
在JAVA的世界里,能够不改代码而又能进行扩展代码的,不多,常见的有装饰者模式,代理模式(静态代理,动态代理),而AOP的底层使用的是动态代理,因为使用静态代理 | 装饰者模式 需要提供一个实实在在类,而使用动态代理,只需要一个被装饰对象,或对象字节码对象即可
3.代理回顾,静态代理方式
3.代理回顾,动态代理实现方式
画的有点丑,别介意
4.基于JDK方式代码
//创建一个接口 JDK方式需要实现接口,如果类没有实现接口,那么只能使用cglib方式
public interface A {
void run();
void eat();
}
//创建一个实体类 实现接口
public class People implemts A{
public void eat(){
System.out.println("吃");
}
public void run(){
System.out.println("跑");
}
}
public class Jdk {
@Test
public void Test_01(){
A people = new People(); //创建实体类对象
// 使用动态代理
Class clazz = people.getClass();
//Proxy.newProxyInstance()需要的参数
//1.类加载器 2.目标类实现的接口,也就是People实现的接口 3.一个new InvocationHandler()的回调函数
//获取代理对象
A a = (A) Proxy.newProxyInstance(this.getClass().getClassLoader(), clazz.getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//加强睡的方法
if ("eat".equals(method.getName())) {
System.out.println("加餐啦!");
}
return method.invoke(people,args);
}
});
proxy.eat();
}
}
运行结果如下
5.基于Cglib方式代码
主要针对没有实现接口的类,底层通过创建另一个子类作为代理类
//创建一个目标类,不实现接口
public class Anli {
public void eat(){
System.out.println("开始吃");
}
public void run(){
System.out.println("开始跑");
}
}
创建一个测试类
public class Cglib {
@Test
public void Test_01(){
//创建目标类的真实对象,
final Anli anli = new Anli();
//创建一个代理对象
Enhancer enhancer = new Enhancer(); //这不是代理对象,是创建代理对象的模板,可以理解为代理对象工厂
enhancer.setSuperclass(anli.getClass());
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//加强吃的方法
if ("eat".equals(method.getName())) {
System.out.println("再一次加餐啦");
}
return method.invoke(anli,objects);
}
});
//创建代理类
Anli a = (Anli)enhancer.create();
a.eat();
}
}
运行结果如下
总结 : 被代理类如果实现了接口就使用JDK的方式,如果没有实现,就是用Cglib方式,开发中通常会使用JDK的方式,开发中都是使用接口来定义规范的.
6.回归正题,我们开始讲AOP
讲AOP之前,我们需要了解AOP的术语,
- 连接点
- 切入点
- 切面
话不多说,直接上图
了解完AOP术语,那么我们就开始实际操作
1.导入spring需要的jar包,和日志包(如果没有的话,可以在我上传资源里面下载,注意,jar包只用导入一部分)
导入jar包如下
- 五个核心jar包
spring-beans-xxx.jar
spring-context-xxx.jar
spring-context.xxx.jar
spring-core-xxx.jar
spring-expression-xxx.jar - 日志jar包
jcl-over-slf4j-xxx.jar
log4j-xxx.jar
slf44j-api-xxx.jar
slf4j-log4j-xxx.jar
需要注意的是,导入日志jar包后,需要提供一个log4j.properties文件 - 导入aop需要的jar包
spring-aop-xxx.jar
spring-aspects-xxx.jar
面向切面过程中Spring AOP是遵循AOP联盟规范实现的,所以需要AOP联盟的接口包
aoplliance-xx.jar
接口包依赖aspectJweaver-xxx.jar
如果没有jar包的话,可以在我的资源里面找
log4j.properties文件内容如下
##\u8BBE\u7F6E\u65E5\u5FD7\u8BB0\u5F55\u5230\u63A7\u5236\u53F0\u7684\u65B9\u5F0F
log4j.appender.s=org.apache.log4j.ConsoleAppender
log4j.appender.s.Target=System.err
log4j.appender.s.layout=org.apache.log4j.PatternLayout
log4j.appender.s.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %5p %c{1}:%L - %m%n
##\u8BBE\u7F6E\u65E5\u5FD7\u8BB0\u5F55\u5230\u6587\u4EF6\u7684\u65B9\u5F0F
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %5p %c{1}:%L - %m%n
##\u65E5\u5FD7\u8F93\u51FA\u7684\u7EA7\u522B\uFF0C\u4EE5\u53CA\u914D\u7F6E\u8BB0\u5F55\u65B9\u6848
log4j.rootLogger=info, s, file
2.导入applicationContext.xml配置所需要的约束 (aop约束)
spring-framework-4.2.9.RELEASE\docs\spring-framework-reference\html\xsd-configuration.html里面搜索aop就行
spring-framework-4.2.9.RELEASE是在spring官网下载的文件夹名
html索搜快捷键 ctrl+shift +f
复制约束,粘贴到xml即可
3.编写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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="Pipixia" class="cn.itcast.aop.Pipixia"/>
<bean id="People" class="cn.itcast.aop.People"/>
<aop:config >
<!-- 切入点 到底哪些方法要被扩展
execution(* com.xyz.myapp.service..(..))
execution: 固定写法
第一个* : 任意返回值
com.xyz.myapp.service : 具体包
第二个* : 包下的所有类
第三个* : 类中的所有方法
(..) : 方法的任意参数
-->
<!--创建一个切入点-->
<aop:pointcut id="point" expression="execution(* cn.itcast.aop.Pipixia.*(..))"/>
<!--创建一个切面 ,ref是提供扩展方法的类-->
<aop:aspect ref="People">
<!--提供People类的eat方法,before是增强位置-->
<aop:before method="eat" pointcut-ref="point" />
</aop:aspect>
</aop:config>
</beans>
AOP增加介绍
<aop:config>
<!-- us === saveUser
ps === saveProduct
os ===saveOrder -->
<aop:pointcut expression="execution(* com.itcast.*.*.eat*(..))" id="aa"/>
<aop:aspect ref="People">
<!-- 前置增强,People类eat方法会在执行itcast包下所有类的方法前执行,只要是itcast包下的方法,执行前都会执行一遍People的方法,出异常不知晓 -->
<!-- <aop:before method="eat" pointcut-ref="aa" /> -->
<!-- 最终增强 不管有没有异常都执行-->
<!-- <aop:after method="eat" pointcut-ref="aa" /> -->
<!-- 环绕增强 -->
<!-- <aop:before method="eat" pointcut-ref="aa" />
<aop:after method="eat" pointcut-ref="aa" /> -->
<!--环绕增强,被增强方法执行前后都是执行eat方法,不过需要额外配置-->
<!-- <aop:around method="around" pointcut-ref="aa"/> -->
<!-- 后置增强,被增强方法执行完后会执行一遍 People类的eat方法-->
<!-- <aop:after-returning method="eat" pointcut-ref="aa"/> -->
<!-- 异常增强,被增强方法出了异常才会增强执行eat方法 -->
<aop:after-throwing method="eat" pointcut-ref="aa"/>
</aop:aspect>
</aop:config>
由于时间问题,就不演示了
就值演示一个before前置增强
编写一个测试类
//使用spring提供的测试方法,使用需要导入test的jar包
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo_02 {
@Resource
private Pipixia p;
@Test
public void demo_01(){
p.eat();
}
}
执行结果如下
它先执行了People的eat方法,
然后再执行了Pipixia的eat方法