- Aop
- 为什么学习 AOP
-
- 对程序进行增强:不修改源码的情况下.
- AOP 可以进行权限校验,日志记录,性能监控,事务控制.
- 对面向对象的思维方式的有力补充
-
这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程
面试的时候问到概念的东西怎么回答
方法1:举例
方法2: 画图
- Aop能做的事情:
例如验证登陆,权限,性能检测,错误信息记录等等,AOP的目的就是将这些东西分离开来,让开发人员专注与核心关注点,下面用到一个简单的验证身份的例子。
在登陆的时候,我们简单的将用户信息放置于session["User"]中,通常在实现一些关键操作的时候,都会记录用户的信息,所以每次都要判断一下session是否过期,但这动作应该由AOP来做
-
- 是对面向对象的思维方式的有力补充
我们在两个类中,可能都需要在每个方法中做日志。按面向对象的设计方法,我们就必须在两个类的方法中都加入日志的内容。也许他们是完全相同的,但就是因为面向对象的设计让类与类之间无法联系,而不能将这些重复的代码统一起来。 也许有人会说,那好办啊,我们可以将这段代码写在一个独立的类独立的方法里,然后再在这两个类中调用。但是,这样一来,这两个类跟我们上面提到的独立的类就有耦合了,它的改变会影响这两个类。那么,有没有什么办法,能让我们在需要的时候,随意地加入代码呢?这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程
Spring AOP的底层实现技术---
http://www.cnblogs.com/luotaoyeah/p/3778183.html
http://www.51cto.com/specbook/223/39480.htm
- Springaop
指挥Spring 帮我们生成代理对象
----Spring实现aop的原理
jdk动态代理
被代理对象必须要实现接口,才能产生代理对象
cglib代理
-->jdk动态代理局限性,cglib代理(第三方代理技术)
原理:对目标对象进行继承代理
(hibernate中查询的load方法创建时首先查询session缓存,没有就创建代理对象
要求 实体类不能被final修饰)
如果目标对象被final修饰,那么该类无法被cglib代理
有接口springaop优先使用 jdk动态代理,无使用cglib
/** * jdk动态代理 * @author Administrator * */ public class MyJDKProxy implements InvocationHandler {
/** * arg0 代理对象 * arg1 调用的方法 * arg2 方法的参数 */ @Override public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable { System.out.println("打开事务"); Object object= arg1.invoke(userDao, arg2); System.out.println("提交事务"); return object; }
private UserDao userDao;
public MyJDKProxy(UserDao userDao) { this.userDao = userDao; }
// 编写工具方法:生成代理: public UserDao createProxy() { UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(), this); return userDaoProxy; }
} |
- Spring基于AspectJ的AOP开发
- AOP开发的相关术语
JoinPoint(连接点)
PointCut(切入点)
Advice (通知)
Target(目标对象)
Weaving(织入)
Proxy(代理)
Aspect(切面)
- 通知类型
前置通知 :在目标方法执行之前执行.
后置通知 :在目标方法执行之后执行
环绕通知 :在目标方法执行前和执行后执行
异常抛出通知:在目标方法执行出现 异常的时候 执行
最终通知 :无论目标方法是否出现异常 最终通知都会 执行.
- 切入点表达式
execution(表达式)
- Spring AOP配置与应用API 6.2.3.4.
- 添加jar
- 两种方式:
- 使用Annotation
- 使用xml
使用Annotation
applicationContext.xml中添加 <aop:aspectj-autoproxy />
切入点表达式的写法: 关键字:execution(表达式) 表达式: 访问修饰符 返回值 包名.包名.包名...类名.方法名(参数列表) 标准的表达式写法: public void com.vp.service.impl.AccountServiceImpl.saveAccount() 访问修饰符可以省略 void com.vp.service.impl.AccountServiceImpl.saveAccount() 返回值可以使用通配符,表示任意返回值 * com.vp.service.impl.AccountServiceImpl.saveAccount() 包名可以使用通配符,表示任意包。但是有几级包,就需要写几个*. * *.*.*.*.AccountServiceImpl.saveAccount()) 包名可以使用..表示当前包及其子包 * *..AccountServiceImpl.saveAccount() 类名和方法名都可以使用*来实现通配 * *..*.*() 参数列表: 可以直接写数据类型: 基本类型直接写名称 int 引用类型写包名.类名的方式 java.lang.String 可以使用通配符表示任意类型,但是必须有参数 可以使用..表示有无参数均可,有参数可以是任意类型 全通配写法: * *..*.*(..)
实际开发中切入点表达式的通常写法: 切到业务层实现类下的所有方法 * com.vp.service.impl.*.*(..) |
Xml代码
<!-- 开启spring的注解 --> <context:annotation-config /> <!-- 开启扫描器 --> <context:component-scan base-package="com.demo" /> <!-- 开启aop的注解 --> <aop:aspectj-autoproxy />
|
添加日志
@Aspect @Component public class Logging { // 定义切入点 @Pointcut("execution(public * com.demo.service..*.*(..))") public void mythod() { }
/** * 在业务方法运行之前运行 */ @Before("mythod()") public void before() { System.out.println("日志开始记录......."); }
@After("mythod()") public void after() { System.out.println("日志记录结束"); }
@Around("mythod()") public Object aroud(ProceedingJoinPoint pjp) throws Throwable { System.out.println("环绕..."); Object object = pjp.proceed(); return object; }
}
|
注意:环绕通知织入的方法如果有返回值 需要
-
- @Before 发放执行之前织入
- @AfterReturning 方法正常执行完返回之后织入(无异常)
- @AfterThrowing 方法抛出异常后织入
- @After 类似异常的finally
- @Around 环绕 类似filter , 如需继续往下执行则需要像filter中执行FilterChain.doFilter(..)对象一样 执行 ProceedingJoinPoint.proceed()方可,例子如下:
@Around("execution(* com.demo.dao..*.*(..))")
public void before(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("method start");
pjp.proceed();//类似FilterChain.doFilter(..)告诉jvm继续向下执行
}
- xml配置AOP 6.3.1
applicationContext.xml
<!-- 创建日志bean --> <bean id="logging" class="com.vp.logging.Logging"></bean> <aop:config> <!-- 定义切面 相当于注解版在类前面添加@Aspect --> <aop:aspect id="logg" ref="logging"> <!-- 定义切入点 --> <aop:pointcut expression="execution(public * com.demo.service..*.*(..))" id="mm" /> <aop:before method="before" pointcut-ref="mm" /> <aop:after method="after" pointcut-ref="mm" /> <aop:around method="aroud" pointcut-ref="mm" /> </aop:aspect> </aop:config> |