java-spring-2
一 学习大纲
1. 动态代理设计模式(JDK和cglib)
2. AOP详解
3. AOP中几种通知类型
4.两种实现方式(Schema-base和Aspectj)
二 知识点详解
1. AOP:中文名称 面向切面编程
2 英文全称:Aspect Object Programming
3 正常程序执行流程都行纵向执行流程,spring Aop 又叫面向切面编程,在原有的纵向执行流程中添加横切面
3.1 不需要修改原有程序代码,优点:1.高扩展性,2. 原有功能相当于释放部分逻辑,让职务更加明确。
4.面向切面编程是什么?
4.1在程序原有的纵向流程执行中,针对某一个或者某一些方法添加通知,形成横切面向过程就叫做面向切面编程。
5.常用概念
5.1 原有功能:切点,pointcut
5.2 前置通知:在切点之前执行的功能.before advice
5.3 后置通知:在切点之后执行的功能,after advice
5.4 在切点执行过程中出现的异常,会触发异常通知 throws advice
5.5 所有功能总称叫做切面
5.6 织入:把切面嵌入到原有功能的过程叫做织入
6 spring提供了2种Aop实现方式
6.1 Schema-based
6.1.1 每个通知都需要实现接口或类
6.1.2 配置spring配置文件时在 <aop:config> 配置
6.2 AspectJ
6.2.1 每个通知都不需要实现接口或类
6.2.2 配置 spring 配置文件是在 <aop:config>的子标签 <aop:aspect> 中配置的
二 Schema-based 实现步骤
1. 导入jar包
2. 新建通知类
2.1 新建前置通知类,三个参数,agr0:切点方法对象Method 对象。
agr1:切点方法参数,arg2:切点在哪个对象中
public class MyBeforeAdvice implements MethodBeforeAdvice{
@Overload
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable{
System.out.println("执行前置通知");
}
}
2.2 新建后置通知类,agr0:切点方法返回值, arg1 切点方法对象,arg2 切点方法参数,arg3 切点方法所在类的对象。
public class MyAfterAdvice implements AfterReturningAdvice{
@Overload
public void afterReturning(Object arg0,Method arg,1, Object[] arg2, Object arg3) throws Throwable{
System.out.println("执行后置通知");
}
}
3 配置spring 配置文件
引入 aop 命名空间 ,配置通知类的<bean> 配置切面
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/sc
hema/beans
http://www.springframework.org/schema/beans/spring-be
ans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.
xsd">
<!-- 配置通知类对象,在切面中引入 -->
<bean id="mybefore" class="com.chen.advice.MyBeforeAdvice"></bean>
<bean id="myafter" class="com.chen.advice.MyAfterAdvice"></bean>
<!-- 配置切面 -->
<aop:config>
<!-- 配置切点 -->
<aop:pointcut expression="execution(* com.chen.test.Demo.demo2())" id="mypoint"/>
<!-- 通知 -->
<aop:advisor advice-ref="mybefore" pointcut-ref="mypoint"/>
<aop:advisor advice-ref="myafter" pointcut-ref="mypoint"/>
</aop:config>
<!-- 配置 Demo 类,测试使用 ,ioc 控制反转特效,把对bean的引用 夺到spring 中去-->
<bean id="demo" class="com.chen.test.Demo"></bean>
</beans>
测试类
public class Test {
public static void main(String[] args) {
/**
*通过ioc特性,自己不用创建demo
*/
// Demo demo = new Demo();
// demo.demo1();
// demo.demo2();
// demo.demo3();
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
Demo demo = ac.getBean("demo",Demo.class);
demo.demo1();
demo.demo2();
demo.demo3();
}
}
执行结果
三 配置异常通知的步骤(AspectJ方式)
1. 只有切点报异常才能出发异常通知,切点也就是 Demo
2. 在 spring 中 有AspectJ 方式提供了异常通知的方法,如果想用 schema-base 实现实现特点的要求自己编写方法
3. 实现步骤,新建类,在类写任意名称的方法
public class MyThrowAdvice{
public void myexception(Exception e1){
System.out.println("执行异常通知"+e1.getMessage());
}
}
3.1 在spring 配置文件中配置
<aop:aspect> 的ref属性表示:方法在哪个类中
<aop:xxxx/> 表示什么通知
method:当触发这个通知时,调用哪个方法
throwing:异常对象名,必须和通知中方法参数名相同(可以不在通知声明异常对象)
applicationContext.xml
<bean id="mythrow" class="com.chen.advice.MyThrowAdvice"></bean>
<aop:config>
<aop:aspect ref="mythrow">
<aop:pointcut expression="execution(* com.chen.test.Demo.demo1())" id="mypoint"/>
<aop:after-throwing method="myexception" pointcut-ref="mypoint" throwing="e1"/>
</aop:aspect>
</aop:config>
<bean id="demo" class="com.chen.test.Demo"></bean>
四 异常通知(Schema-based方式)
1. 新建一个类,实现throwsAdvice接口,
必须自己写方法,切必须叫afterThrowing
有两个参数方式,必须是一个或者4个
异常类型要与切点报的异常类型一致
public class MyThrow implements ThrowsAdvice{
public void afterThrowing(Exception ex) throws Throwable {
System.out.println("执行异常通过-schema-base 方式");
}
}
在applicationContext.xml 配置
<bean id="mythrow" class="com.chen.advice.MyThrow"></bean>
<aop:config>
<aop:pointcut expression="execution(* com.chen.test.Demo.demo1())" id="mypoint"/>
<aop:advisor advice-ref="mythrow" pointcut-ref="mypoint" />
</aop:config>
<bean id="demo" class="com.chen.test.Demo"></bean>
五 环绕通知(Schema-based 方式)
1. 把前置通知和后置通知都写到了一个通知里,组成那个了环绕通知
2.实现步骤,新建一个类实现 MethodInterceptor
public class MyArround implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation arg0) throws Throwable {
System.out.println("环绕-前置");
Object result = arg0.proceed();//放行,调用切点方式
System.out.println("环绕-后置");
return result;
}
}
配置 applicationContext.xml
<bean id="myarround" class="com.chen.advice.MyArround"></bean>
<aop:config>
<aop:pointcut expression="execution(* com.chen.test.Demo.demo1())" id="mypoint"/>
<aop:advisor advice-ref="myarround" pointcut-ref="mypoint" />
</aop:config>
<bean id="demo" class="com.chen.test.Demo"></bean>
六 使用AspectJ 方式实现
1. 新建类,不用实现
1.1 类中方法名任意
public class MyAdvice {
public void mybefore(String name1,int age1){
System.out.println("前置"+name1 );
}
public void mybefore1(String name1){
System.out.println("前置:"+name1);
}
public void myaftering(){
System.out.println("后置 2");
}
public void myafter(){
System.out.println("后置 1");
}
public void mythrow(){
System.out.println("异常");
}
public Object myarround(ProceedingJoinPoint p) throws Throwable{
System.out.println("执行环绕");
System.out.println("环绕-前置");
Object result = p.proceed();
System.out.println("环绕后置");
return result;
}
1.2 配置spring 配置文件
<aop: after> 后置通知,是否出现异常都执行
<aop:after-returning/> 后置通知,只有切点正确执行时执行,
<aop:after/> 和 <aop:after-returning/> 和 <aop: after-throwing/> 执行顺序和配置顺序相关
execution() 括号不能扩上,args
中间使用 and 不能使用 && 由 spring 把 and 解析成 &&
args(名称) 名称自定义,顺序和demo1(参数,参数)对应
<aop:before/> arg-name="名称" 名称来源于expression ="" 中args(),名称必须一样
args() 有几个参数,arg-name 里面必须有几个参数
arg-name="" 里面名称必须和通知方法参数名对象
<aop:config>
<aop:aspect ref="myadvice">
<aop:pointcut expression="execution(* com.chen.test.Demo.demo1(String,int)) and args(name1,age1)" id="mypoint"/>
<aop:pointcut expression="execution(* com.chen.test.Demo.demo1(String)) and args(name1)" id="mypoint1"/>
<aop:before method="mybefore" pointcut-ref="mypoint" arg-names="name1,age1"/>
<aop:before method="mybefore1" pointcut-ref="mypoint1" arg-names="name1"/>
</aop:aspect>
</aop:config>
七 使用注解(基于 Aspect)
1. spring 不会自动去寻找注解,必须告诉 spring 哪些包下的类中可能有注解
1.1 引入xmlns:context
<context:component-scan base-package="com.chen.advice"></context:component-scan>
2. @Component
相当于<bean/>
如果没有参数,把类名首字母变小写,相当于<bean id=""/>
@Component("自定义名称")
实现步骤
在spring配置文件中配置注解在哪些包中1. 然后多个包 用 , 隔开
在Demo 类中添加@Componet
在方法中添加@Pointcut("")定义切点
@Component
public class Demo {
@Pointcut("execution(*com.chen.test.Demo.demo1())")
public void demo1() throws Exception{
// int i = 5/0;
System.out.println("demo1");
}
}
3.3 在通知类中配置
@Component 类被 Spring管理
@Aspect 相当于<aop:aspect/> 表示通知方法在当前类中
@Component
@Aspect
public class MyAdvice {
@Before("com.chen.test.Demo.demo1()")
public void mybefore(){
System.out.println("前置");
}
@After("com.chen.test.Demo.demo1()")
public void myafter(){
System.out.println("后置通知");
}
@AfterThrowing("com.chen.test.Demo.demo1()")
public void mythrow(){
System.out.println("异常通知");
}
@Around("com.chen.test.Demo.demo1()")
public Object myarround(ProceedingJoinPoint p) throws Throwable{
System.out.println("环绕-前置");
Object result = p.proceed();
System.out.println("环绕-后置");
return result;
}
}
九 静态代理设计模式
1. 由代理对象代理所有真实对象的功能
自己编写代理类
每个代理的功能需要单独编写
2静态代理设计模式的缺点
当代理功能比较多时,代理类中方法需要写很多
十 动态代理
1. 为了解决静态代理频繁编写代理功能缺点
2 分类
jdk 提供的
cglib 动态代理
十一 JDK 动态代理
1. 和cglib 动态代理相比
优点:jdk自带,不需要导入额外jar
缺点 : 真实对象必须实现接口,利用反射机制,效率不高
2. 使用JDK动态代理是可能出现下面异常
出现原因:希望把接口对象转换为具体真实对象
异常: java.lang.ClassCastException
十二 cglib 动态代理
1.cglib优点
基于字节码,生成真实对象的子类
运行效率高于JDK动态代理
不需要实现接口
2 cgilb 缺点
非JDK功能,需要导入额外jar
3. 使用spring aop时,只要出现Proxy 和真实对象转换异常
设置为true 使用cglib
设置为 false 使用 jdk(默认值)
<aop: aspectj-autoproxyproxy-target-class="true"></aop:aspectj-autoproxy>