目录
1.AOP的概述?
1.1 什么是AOP的技术?
- AOP为Aspect Oriented Programming的缩写,意为:面向切面编程
- 通过 预编译方式 和 运行期动态代理 实现程序功能的统一维护的一种技术
- AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型
- 对业务逻辑的各个部分进行隔离,(模块化)从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性.
1.2 为什么学习AOP?:
- 面向切面编程.(思想.---解决OOP遇到一些问题)
- AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)
- 可以在不修改源代码的前提下,对程序进行增强!!
2.Spring框架的AOP的底层实现
Spring框架的AOP技术底层也是采用的代理技术,代理的方式提供了两种
1. 基于JDK的动态代理
* 必须是面向接口的,只有实现了具体接口的类才能生成代理对象
2. 基于CGLIB动态代理
* 对于没有实现了接口的类,也可以产生代理,产生这个类的子类的方式
2.1JDK的动态代理
①特点:运行期间生成
②代码实现:
public class MyProxyUtils {
public static UserDao getProxy(final UserDao dao) {
// 使用Proxy类生成代理对象
UserDao proxy = (UserDao) Proxy.newProxyInstance(dao.getClass().getClassLoader(),
dao.getClass().getInterfaces(), new InvocationHandler() {
// 代理对象方法一直行,invoke方法就会执行一次
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("save".equals(method.getName())){//方法增强*****
System.out.println("记录日志...");
// 开启事务
}
// 提交事务
// 让dao类的save或者update方法正常的执行下去
return method.invoke(dao, args);
}
});
// 返回代理对象
return proxy;
}
}
③代码分析:
由图可以看出:
代理对象控制目标对象的访问,在这可以写任何代码
最后再有代理对象去访问目标对象UserDao
代理对象是UserDao外层 对象
这样比如说做一个记录日志的功能,现在代理对象中去实现它然后再由代理对象去访问UserDao
效果:UserDao中的代码就没有被修改,但是功能改变了
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序
*参数:
*loader - 定义代理类的类加载器
*interfaces - 代理类要实现的接口列表
*h - 指派方法调用的调用处理程序 别名回调函数
2.2 CGLIB的代理技术
①特点:在类的加载的过程中生成子类的方式
②引入CBLIB的开发包:(Spring框架核心包中已经引入了CGLIB的开发包)
③代码实现:
OrderDaoImpl这个类没有实现接口
public class OrderDaoImpl{
public void save() {
System.out.println("保存订单");
}
public void update() {
System.out.println("更新订单");
}
}
测试代码:
@Test
public void run1()
{
//目标对象
OrderDaoImpl dao = new OrderDaoImpl();
dao.save();
dao.update();
//使用CGlib方式生成代理对象
OrderDaoImpl proxy =MyCglibUtils.getproxy();
proxy.save();
proxy.upate();
}
实现代理对象
public class MyCglibUtils {
public static OrderDaoImpl getProxy(){
// 创建CGLIB核心的类
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(OrderDaoImpl.class);
// 设置回调函数
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
if("save".equals(method.getName())){
// 记录日志
System.out.println("记录日志了...");
}
return methodProxy.invokeSuper(obj, args);
}
});
// 生成代理对象
OrderDaoImpl proxy = (OrderDaoImpl) enhancer.create();
return proxy;
}
}
④:需要注意的是一个是要设置父类 ,另外一个是生成代理对象的时候就会调用那个回调函数。
3.Spring基于AspectJ的AOP的开发(XML)
*代理对象一般都人家帮你生成
*但自己得配置 增强需自己来写
3.1技术分析之AOP的相关术语
1. Joinpoint(连接点) -- 所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点 2. Pointcut(切入点) -- 所谓切入点是指我们要对哪些Joinpoint进行拦截的定义 3. Advice(通知/增强) -- 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能) 4. Introduction(引介) -- 引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field 5. Target(目标对象) -- 代理的目标对象 6. Weaving(织入) -- 是指把增强应用到目标对象来创建新的代理对象的过程 7. Proxy(代理) -- 一个类被AOP织入增强后,就产生一个结果代理类 8. Aspect(切面) -- 是切入点和通知的结合,以后咱们自己来编写和配置的
3.2Aop的入门的环境的准备
步骤一:创建JavaWEB项目,引入具体的开发的jar包
先引入Spring框架开发的基本开发包
再引入Spring框架的AOP的开发包
** spring的传统AOP的开发的包
* spring-aop-4.2.4.RELEASE.jar
* com.springsource.org.aopalliance-1.0.0.jar(定义了很多的规范)
** aspectJ的开发包
* com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
* spring-aspects-4.2.4.RELEASE.jar
步骤二:创建Spring的配置文件,引入具体的AOP的schema约束
applicationContext.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"> <!-- bean definitions here -->
</beans>
log4j.properties
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=c\:mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ###
log4j.rootLogger=info, stdout
步骤三:创建包结构,编写具体的接口和实现类
public interface CustomerDao {
public void save();
public void update();
}
public class CustomerDaoImpl implements CustomerDao {
public void save() {
System.out.println("保存客户...");
}
public void update() {
System.out.println("修改客户...");
}
}
public class Demo{
@Resource(name="customerDao")
private CustomerDao customerDao;
@Test
public void run1(){
customerDao.save();
customerDao.update();
}
}
步骤四:将目标类配置到Spring中 (把创建对象的权利翻转交给spring)
<bean id="customerDao" class="com.itheima.demo3.CustomerDaoImpl"/>
3.3需求分析:
在执行save方法之前输出 记录日志 但是不能改变源代码
最原始的配置文件
现在用Spring的aop技术:
- 帮我们生成实现类的代理对象
- 然后让代理对象去执行
- 然后去置入增强 (必须自己去手动编写)
- 最后让目标的方法去执行
3.4解决需求之AspectJ的XML方式
①定义切面类@Aspect
@Aspect
public class MyAspectXml {
// 定义通知(具体增强)
public void log(){
System.out.println("记录日志...");
}
}
注意:方法log要运行就得有类MyAspectXml的对象,这时不需要自己new交给spring管理 ,去配置文件中把切面类配置好交给 spring
②在配置文件中定义切面类
<bean id="myAspectXml" class="com.itheima.demo3.MyAspectXml"/>
③在配置文件中完成aop的配置(代理对象的生成配置和使用增强也需要配置)
<aop:config>
**<!-- 引入切面类(包含切入点和通知) -->
<aop:aspect ref="myAspectXml">
<!-- 定义通知类型:切面类的方法和切入点的表达式 -->
<aop:before method="log" pointcut="execution(public void com.itheima.demo3.CustomerDaoImpl.save())"/>
</aop:aspect>
</aop:config>
其中需要注意点是先用method找到增加的方法,然后现在是想让save方法执行之前去执行增强的,所以切入点写的是save的。
④:测试(现在执行的时候会增强)
package com.java.spring_aop2;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* 测试aop的功能
* @author BBQi
*
*/
@RunWith(SpringJUnit4ClassRunner.class)//Junit的环境
@Contex